chiark / gitweb /
Massively improved /officer cmd handling
authorIan Jackson <ian@liberator.relativity.greenend.org.uk>
Sun, 17 May 2009 12:31:52 +0000 (13:31 +0100)
committerIan Jackson <ian@liberator.relativity.greenend.org.uk>
Sun, 17 May 2009 12:31:52 +0000 (13:31 +0100)
yoweb-scrape
yoweb-scrape.txt

index 3c42f7e6bfcb4ca16ffdec44463c2cab40722e49..c3e6d73513adc3d5516252d8bd5c06ef706a26bd 100755 (executable)
@@ -429,18 +429,28 @@ class ChatLogTracker:
                self._pl = {}   # self._pl['Pirate'] =
                self._vl = {}   #   self._vl['Vessel']['Pirate'] = PirateAboard
                                # self._vl['Vessel']['#lastinfo']
                self._pl = {}   # self._pl['Pirate'] =
                self._vl = {}   #   self._vl['Vessel']['Pirate'] = PirateAboard
                                # self._vl['Vessel']['#lastinfo']
-               self._v = None          # self._v =
-               self._vessel = None     #       self._vl[self._vessel]
+                               # self._vl['Vessel']['#name']
+                               # self._v = self._vl[self._vessel]
                self._date = None
                self._myself = myself_pi
                self._date = None
                self._myself = myself_pi
-               self._need_redisplay = False
                self._f = file(logfn)
                self._lbuf = ''
                self._progress = [0, os.fstat(self._f.fileno()).st_size]
                self._f = file(logfn)
                self._lbuf = ''
                self._progress = [0, os.fstat(self._f.fileno()).st_size]
+               self._disembark_myself()
+               self._need_redisplay = False
+
+       def _disembark_myself(self):
+               self._v = None
+               self._vessel = None
+               self.force_redisplay()
 
        def force_redisplay(self):
                self._need_redisplay = True
 
 
        def force_redisplay(self):
                self._need_redisplay = True
 
+       def _vessel_updated(self, v, timestamp):
+               v['#lastinfo'] = timestamp
+               self.force_redisplay()
+
        def _onboard_event(self,v,timestamp,pirate,event):
                pa = self._pl.get(pirate, None)
                if pa is not None and pa.v is v:
        def _onboard_event(self,v,timestamp,pirate,event):
                pa = self._pl.get(pirate, None)
                if pa is not None and pa.v is v:
@@ -451,44 +461,110 @@ class ChatLogTracker:
                        pa = PirateAboard(pirate, v, timestamp, event)
                        self._pl[pirate] = pa
                        v[pirate] = pa
                        pa = PirateAboard(pirate, v, timestamp, event)
                        self._pl[pirate] = pa
                        v[pirate] = pa
-               v['#lastinfo'] = timestamp
-               self.force_redisplay()
+               self._vessel_updated(v, timestamp)
                return pa
 
        def _trash_vessel(self, v):
                for pn in v:
                        if pn.startswith('#'): continue
                        del self._pl[pn]
                return pa
 
        def _trash_vessel(self, v):
                for pn in v:
                        if pn.startswith('#'): continue
                        del self._pl[pn]
+               vn = v['#name']
+               del self._vl[vn]
+               if v is self._v: self._disembark_myself()
                self.force_redisplay()
 
                self.force_redisplay()
 
+       def _vessel_stale(self, v, timestamp):
+               return timestamp - v['#lastinfo'] > opts.ship_reboard_clearout
+
+       def _vessel_check_expire(self, v, timestamp):
+               if not self._vessel_stale(v, timestamp):
+                       return v
+               self._debug_line_disposition(timestamp,'',
+                       'stale-reset ' + v['#name'])
+               self._trash_vessel(v)
+               return None
+
        def expire_garbage(self, timestamp):
        def expire_garbage(self, timestamp):
-               for (vn,v) in list(self._vl.iteritems()):
-                       la = v['#lastinfo']
-                       if timestamp - la > opts.ship_reboard_clearout:
-                               self._debug_line_disposition(timestamp,'',
-                                       'stale reset '+vn)
-                               self._trash_vessel(v)
-                               del self._vl[vn]
-
-       def _create_vessel(self, vn, timestamp):
-               self._vl[vn] = v = { '#lastinfo': timestamp }
-               return v
+               for v in self._vl.values():
+                       self._vessel_check_expire(v, timestamp)
 
 
-       def _update_vessel_lookup(self, vn, timestamp, dml):
+       def _vessel_lookup(self, vn, timestamp, dml=[], create=False):
                v = self._vl.get(vn, None)
                v = self._vl.get(vn, None)
-               if v is None:
-                       dml.append('new')
-                       v = self._create_vessel(vn, timestamp)
-               elif timestamp - v['#lastinfo'] > opts.ship_reboard_clearout:
-                       dml.append('stale')
-                       self._trash_vessel(v)
-                       v = self._create_vessel(vn, timestamp)
-               else:
-                       dml.append('current')
+               if v is not None:
+                       v = self._vessel_check_expire(v, timestamp)
+               if v is not None:
+                       dml.append('found')
+                       return v
+               if not create:
+                       dml.append('no')
+               dml.append('new')
+               self._vl[vn] = v = { '#name': vn }
+               self._vessel_updated(v, timestamp)
                return v
 
                return v
 
+       def _find_matching_vessel(self, pattern, timestamp, cmdr,
+                                       dml=[], create=False):
+               # use when a commander pirate `cmdr' specified a vessel
+               #  by name `pattern' (either may be None)
+               # if create is true, will create the vessel
+               #  record if an exact name is specified
+
+               if (pattern is not None and
+                   not '*' in pattern
+                   and len(pattern.split(' ')) == 2):
+                       vn = pattern.title()
+                       dml.append('exact')
+                       return self._vessel_lookup(
+                               vn, timestamp, dml=dml, create=create)
+
+               if pattern is None:
+                       pattern_check = lambda vn: True
+               else:
+                       re = '(?:.* )?%s$' % pattern.lower().replace('*','.*')
+                       pattern_check = regexp.compile(re, regexp.I).match
+
+               tries = []
+
+               cmdr_pa = self._pl.get(cmdr, None)
+               if cmdr_pa: tries.append((cmdr_pa.v, 'cmdr'))
+
+               tries.append((self._v, 'here'))
+               tried_vns = []
+
+               for (v, dm) in tries:
+                       if v is None: dml.append(dm+'?'); continue
+                       
+                       vn = v['#name']
+                       if not pattern_check(vn):
+                               tried_vns.append(vn)
+                               dml.append(dm+'#')
+                               continue
+
+                       dml.append(dm+'!')
+                       return v
+
+               if pattern is not None and '*' in pattern:
+                       search = [
+                               (vn,v)
+                               for (vn,v) in self._vl.iteritems()
+                               if not self._vessel_stale(v, timestamp)
+                               if pattern_check(vn)
+                               ]
+                       #debug('CLT-RE /%s/ wanted (%s) searched (%s)' % (
+                       #       re,
+                       #       '/'.join(tried_vns),
+                       #       '/'.join([vn for (vn,v) in search])))
+
+                       if len(search)==1:
+                               dml.append('one')
+                               return search[0][1]
+                       elif search:
+                               dml.append('many')
+                       else:
+                               dml.append('none')
+
        def _debug_line_disposition(self,timestamp,l,m):
        def _debug_line_disposition(self,timestamp,l,m):
-               debug('CLT %13s %-30s %s' % (timestamp,m,l))
+               debug('CLT %13s %-40s %s' % (timestamp,m,l))
 
        def chatline(self,l):
                rm = lambda re: regexp.match(re,l)
 
        def chatline(self,l):
                rm = lambda re: regexp.match(re,l)
@@ -538,9 +614,10 @@ class ChatLogTracker:
                if m:
                        dm = ['boarding']
                        pn = self._myself.name
                if m:
                        dm = ['boarding']
                        pn = self._myself.name
-                       self._vessel = vn = m.group(1)
-                       self._v = self._update_vessel_lookup(vn, timestamp, dm)
-
+                       vn = m.group(1)
+                       v = self._vessel_lookup(vn, timestamp, dm, create=True)
+                       self._vessel = vn
+                       self._v = v
                        ob_x(pn, 'we boarded')
                        self.expire_garbage(timestamp)
                        return d(' '.join(dm))
                        ob_x(pn, 'we boarded')
                        self.expire_garbage(timestamp)
                        return d(' '.join(dm))
@@ -580,11 +657,11 @@ class ChatLogTracker:
                        (cmdr, metacmd) = m.groups()
                        metacmd = regexp.sub('\\s+', ' ', metacmd).strip()
                        m2 = regexp.match(
                        (cmdr, metacmd) = m.groups()
                        metacmd = regexp.sub('\\s+', ' ', metacmd).strip()
                        m2 = regexp.match(
-                               '/([ad]) (?:([A-Za-z ]+)\\s*:)?([A-Za-z ]+)$',
+                               '/([ad]) (?:([A-Za-z* ]+)\\s*:)?([A-Za-z ]+)$',
                                metacmd)
                        if not m2: return chat(chan)
 
                                metacmd)
                        if not m2: return chat(chan)
 
-                       (cmd, vn, targets) = m2.groups()
+                       (cmd, pattern, targets) = m2.groups()
                        dml = ['cmd', chan, cmd]
 
                        if cmd == 'a': each = self._onboard_event
                        dml = ['cmd', chan, cmd]
 
                        if cmd == 'a': each = self._onboard_event
@@ -592,32 +669,20 @@ class ChatLogTracker:
 
                        if cmdr == self._myself.name:
                                dml.append('self')
 
                        if cmdr == self._myself.name:
                                dml.append('self')
-                               how = 'manual: /%s' % cmd
+                               how = 'cmd: %s' % cmd
                        else:
                                dml.append('other')
                        else:
                                dml.append('other')
-                               how = '/%s %s' % (cmd,cmdr)
-
-                       v = None
-                       if vn is not None and len(vn.split(' ')) == 2:
-                               v = self._update_vessel_lookup(
-                                       vn.title(), timestamp, dml)
-                       elif self._v is None:
-                               dml.append('no-current')
-                       elif vn is None:
-                               dml.append('current')
-                               v = self._v
-                       elif regexp.match('(?:.* )?%s$' % vn.title(),
-                                       self._vessel):
-                               dml.append('match')
-                               v = self._v
-                       else:
-                               dml.append('unk-abbrev')
+                               how = 'cmd: %s %s' % (cmd,cmdr)
+
+                       v = self._find_matching_vessel(
+                               pattern, timestamp, cmdr, dml, create=True)
 
                        if v is not None:
                                targets = targets.strip().split(' ')
                                dml.append(`len(targets)`)
                                for target in targets:
                                        each(v, timestamp, target.title(), how)
 
                        if v is not None:
                                targets = targets.strip().split(' ')
                                dml.append(`len(targets)`)
                                for target in targets:
                                        each(v, timestamp, target.title(), how)
+                               self._vessel_updated(v, timestamp)
 
                        dm = ' '.join(dml)
                        chat_core(cmdr, 'cmd '+chan)
 
                        dm = ' '.join(dml)
                        chat_core(cmdr, 'cmd '+chan)
@@ -660,13 +725,14 @@ class ChatLogTracker:
                        disembark(self._v, timestamp, pirate, 'disembarked')
                        return d('disembarked')
 
                        disembark(self._v, timestamp, pirate, 'disembarked')
                        return d('disembarked')
 
-               return d('not matched')
+               return d('not-matched')
 
        def _str_vessel(self, vn, v):
                s = ' vessel %s\n' % vn
                s += ' '*20 + "%-*s   %13s\n" % (
                                max_pirate_namelen, '#lastinfo',
                                v['#lastinfo'])
 
        def _str_vessel(self, vn, v):
                s = ' vessel %s\n' % vn
                s += ' '*20 + "%-*s   %13s\n" % (
                                max_pirate_namelen, '#lastinfo',
                                v['#lastinfo'])
+               assert v['#name'] == vn
                for pn in sorted(v.keys()):
                        if pn.startswith('#'): continue
                        pa = v[pn]
                for pn in sorted(v.keys()):
                        if pn.startswith('#'): continue
                        pa = v[pn]
index 452fdd2e31004cc77a562dd0d0d29df477fb4a89..192e446585050ee7a4ad5ba14482579e06dc11d1 100644 (file)
@@ -44,12 +44,14 @@ I know of one key defect:
    the proper mixed-case form automatically.  <ship>, if it is not
    provided, will be assumed to be the one you're on (so officers
    shouldn't send these runes unless only one ship is out).  If <ship>
    the proper mixed-case form automatically.  <ship>, if it is not
    provided, will be assumed to be the one you're on (so officers
    shouldn't send these runes unless only one ship is out).  If <ship>
-   is supplied it may be the full ship name, or only the second word.
+   is supplied it may match the full ship name, or only the second
+   word.  You can put `*'s in <ship> which will match anything.
 
    For example:
          /a haddock: aiah
         /d aiah
         /a smart sunfish: copperkatie
 
    For example:
          /a haddock: aiah
         /d aiah
         /a smart sunfish: copperkatie
+        /a s* sunfish: anaplian
    These are actually sent by typing in the chat box things like:
          /o /d plonker
          /off /a hypiscles
    These are actually sent by typing in the chat box things like:
          /o /d plonker
          /off /a hypiscles
@@ -67,6 +69,16 @@ I know of one key defect:
    where the game reports this in the log, which doesn't seem to be
    all the time), and from many of the other messages that occur.
 
    where the game reports this in the log, which doesn't seem to be
    all the time), and from many of the other messages that occur.
 
+   Matching for ship patterns in /a and /d commands picks the first
+   of the following which applies and is not contradicted by any
+   name or partial name specified in the command:
+      - The ship we think the person telling us is on board
+      - The ship we think we are on board
+      - Any other ship we know about, if at least a something was
+         specified, and provided that is not ambiguous
+      - The specific ship name if it is completely specified,
+         possibly recording this as a new ship
+
 Other things to mention:
 
  * There's nothing really resembling a manual.
 Other things to mention:
 
  * There's nothing really resembling a manual.