home · contact · privacy
Replace annotation polling with annotation push, cache info display.
authorChristian Heller <c.heller@plomlompom.de>
Sun, 6 Dec 2020 18:31:04 +0000 (19:31 +0100)
committerChristian Heller <c.heller@plomlompom.de>
Sun, 6 Dec 2020 18:31:04 +0000 (19:31 +0100)
plomrogue/commands.py
plomrogue/game.py
rogue_chat.html
rogue_chat.py
rogue_chat_curses.py

index 2061fe7b782560f50911753fcee3f1cf9395afac..07bfae0e1b905f8141fe281d1c475b44fcd400e0 100644 (file)
@@ -202,18 +202,6 @@ def cmd_GOD_PORTAL(game, big_yx, little_yx, msg):
     game.changed = True
 cmd_GOD_PORTAL.argtypes = 'yx_tuple yx_tuple:nonneg string'
 
-def cmd_GET_ANNOTATION(game, yx, connection_id):
-    player = game.get_player(connection_id)
-    big_yx, little_yx = player.fov_stencil.source_yxyx(yx)
-    annotation = '(unknown)'
-    if player.fov_test(big_yx, little_yx):
-        annotation = '(none)'
-        if big_yx in game.annotations:
-            if little_yx in game.annotations[big_yx]:
-                annotation = game.annotations[big_yx][little_yx]
-    game.io.send('ANNOTATION %s %s' % (yx, quote(annotation)))
-cmd_GET_ANNOTATION.argtypes = 'yx_tuple:nonneg'
-
 def cmd_MAP_LINE(game, big_yx, y, line):
     map_ = game.get_map(big_yx)
     map_.set_line(y, line)
index ff761556e0da3f4299411ad0ae27051db7957a3f..c4951799002c3a2ec086bde6342cb07231c1c6f5 100755 (executable)
@@ -202,6 +202,7 @@ class Game(GameBase):
     def send_gamestate(self, connection_id=None):
         """Send out game state data relevant to clients."""
 
+        # TODO: limit to connection_id if provided
         self.io.send('TURN ' + str(self.turn))
         from plomrogue.mapping import FovMap
         import multiprocessing
@@ -247,7 +248,9 @@ class Game(GameBase):
                 for little_yx in [little_yx for little_yx in self.annotations[big_yx]
                                   if player.fov_test(big_yx, little_yx)]:
                     target_yx = player.fov_stencil.target_yx(big_yx, little_yx)
-                    self.io.send('ANNOTATION_HINT %s' % (target_yx,), c_id)
+                    annotation = self.annotations[big_yx][little_yx]
+                    self.io.send('ANNOTATION %s %s' % (target_yx,
+                                                       quote(annotation)), c_id)
         self.io.send('GAME_STATE_COMPLETE')
 
     def run_tick(self):
index 8efe2a2a637c5e55a8d6a634976a514cb7d6001e..dd037e20997fbd620cd97228d40a94841b8e1d25 100644 (file)
@@ -120,8 +120,8 @@ keyboard input/control: <span id="keyboard_control"></span>
 </div>
 <script>
 "use strict";
-let websocket_location = "wss://plomlompom.com/rogue_chat/";
-//let websocket_location = "ws://localhost:8000/";
+//let websocket_location = "wss://plomlompom.com/rogue_chat/";
+let websocket_location = "ws://localhost:8001/";
 
 let mode_helps = {
     'play': {
@@ -437,9 +437,10 @@ let server = {
         let tokens = parser.tokenize(event.data);
         if (tokens[0] === 'TURN') {
             game.turn_complete = false;
-            explorer.empty_info_db();
+            explorer.empty_annotations();
             game.things = {};
             game.portals = {};
+            game.fov = '';
             game.turn = parseInt(tokens[1]);
         } else if (tokens[0] === 'THING') {
             let t = game.get_thing(tokens[4], true);
@@ -477,9 +478,8 @@ let server = {
             game.turn_complete = true;
             if (tui.mode.name == 'post_login_wait') {
                 tui.switch_mode('play');
-            } else if (tui.mode.name == 'study') {
-                explorer.query_info();
             }
+            explorer.info_cached = false;
             tui.full_refresh();
         } else if (tokens[0] === 'CHAT') {
              tui.log_msg('# ' + tokens[1], 1);
@@ -501,12 +501,9 @@ let server = {
         } else if (tokens[0] === 'PORTAL') {
             let position = parser.parse_yx(tokens[1]);
             game.portals[position] = tokens[2];
-        } else if (tokens[0] === 'ANNOTATION_HINT') {
-            let position = parser.parse_yx(tokens[1]);
-            explorer.info_hints = explorer.info_hints.concat([position]);
         } else if (tokens[0] === 'ANNOTATION') {
             let position = parser.parse_yx(tokens[1]);
-            explorer.update_info_db(position, tokens[2]);
+            explorer.update_annotations(position, tokens[2]);
             tui.full_refresh();
         } else if (tokens[0] === 'UNHANDLED_INPUT') {
             tui.log_msg('? unknown command');
@@ -742,9 +739,6 @@ let tui = {
     }
     if (game.player_id in game.things && (this.mode.shows_info || this.mode.name == 'control_tile_draw')) {
         explorer.position = game.things[game.player_id].position;
-        if (this.mode.shows_info) {
-            explorer.query_info();
-        }
     }
     this.inputEl.value = "";
     this.restore_input_values();
@@ -806,8 +800,8 @@ let tui = {
       }
   },
   restore_input_values: function() {
-      if (this.mode.name == 'annotate' && explorer.position in explorer.info_db) {
-          let info = explorer.info_db[explorer.position];
+      if (this.mode.name == 'annotate' && explorer.position in explorer.annotations) {
+          let info = explorer.annotations[explorer.position];
           if (info != "(none)") {
               this.inputEl.value = info;
           }
@@ -1034,8 +1028,8 @@ let tui = {
       this.offset_links(offset, log_links);
   },
   draw_info: function() {
-      let [lines, link_data] = this.msg_into_lines_of_width(explorer.get_info(),
-                                                            this.window_width);
+      const info = "MAP VIEW: " + tui.map_mode + "\n" + explorer.get_info();
+      let [lines, link_data] = this.msg_into_lines_of_width(info, this.window_width);
       let offset = [this.height_header, this.window_width];
       for (let y = offset[0], i = 0; y < terminal.rows && i < lines.length; y++, i++) {
         terminal.write(y, offset[1], lines[i]);
@@ -1214,81 +1208,79 @@ server.init(websocket_location);
 
 let explorer = {
     position: [0,0],
-    info_db: {},
-    info_hints: [],
+    annotations: {},
+    info_cached: false,
     move: function(direction) {
         let target = game.move(this.position, direction);
         if (target) {
             this.position = target
-            if (tui.mode.shows_info) {
-                this.query_info();
-            } else if (tui.tile_draw) {
+            this.info_cached = false;
+            if (tui.tile_draw) {
                 this.send_tile_control_command();
             }
         } else {
             terminal.blink_screen();
         };
     },
-    update_info_db: function(yx, str) {
-        this.info_db[yx] = str;
+    update_annotations: function(yx, str) {
+        this.annotations[yx] = str;
         if (tui.mode.name == 'study') {
             tui.full_refresh();
         }
     },
-    empty_info_db: function() {
-        this.info_db = {};
-        this.info_hints = [];
+    empty_annotations: function() {
+        this.annotations = {};
         if (tui.mode.name == 'study') {
             tui.full_refresh();
         }
     },
-    query_info: function() {
-        server.send(["GET_ANNOTATION", unparser.to_yx(explorer.position)]);
-    },
     get_info: function() {
-        let info = "MAP VIEW: " + tui.map_mode + "\n";
+        if (this.info_cached) {
+            return this.info_cached;
+        }
+        let info_to_cache = '';
         let position_i = this.position[0] * game.map_size[1] + this.position[1];
         if (game.fov[position_i] != '.') {
-            return info + 'outside field of view';
-        };
-        let terrain_char = game.map[position_i]
-        let terrain_desc = '?'
-        if (game.terrains[terrain_char]) {
-            terrain_desc = game.terrains[terrain_char];
-        };
-        info += 'TERRAIN: "' + terrain_char + '" / ' + terrain_desc + "\n";
-        let protection = game.map_control[position_i];
-        if (protection == '.') {
-            protection = 'unprotected';
-        };
-        info += 'PROTECTION: ' + protection + '\n';
-        for (let t_id in game.things) {
-             let t = game.things[t_id];
-             if (t.position[0] == this.position[0] && t.position[1] == this.position[1]) {
-                 let symbol = game.thing_types[t.type_];
-                 let protection = t.protection;
-                 if (protection == '.') {
-                     protection = 'none';
-                 }
-                 info += "THING: " + t.type_ + " / " + symbol;
-                 if (t.thing_char) {
-                     info += t.thing_char;
-                 };
-                 if (t.name_) {
-                     info += " (" + t.name_ + ")";
-                 }
-                 info += " / protection: " + protection + "\n";
-             }
-        }
-        if (this.position in game.portals) {
-            info += "PORTAL: " + game.portals[this.position] + "\n";
-        }
-        if (this.position in this.info_db) {
-            info += "ANNOTATIONS: " + this.info_db[this.position];
+            info_to_cache += 'outside field of view';
         } else {
-            info += 'waiting …';
+            let terrain_char = game.map[position_i]
+            let terrain_desc = '?'
+            if (game.terrains[terrain_char]) {
+                terrain_desc = game.terrains[terrain_char];
+            };
+            info_to_cache += 'TERRAIN: "' + terrain_char + '" / ' + terrain_desc + "\n";
+            let protection = game.map_control[position_i];
+            if (protection == '.') {
+                protection = 'unprotected';
+            };
+            info_to_cache += 'PROTECTION: ' + protection + '\n';
+            for (let t_id in game.things) {
+                 let t = game.things[t_id];
+                 if (t.position[0] == this.position[0] && t.position[1] == this.position[1]) {
+                     let symbol = game.thing_types[t.type_];
+                     let protection = t.protection;
+                     if (protection == '.') {
+                         protection = 'none';
+                     }
+                     info_to_cache += "THING: " + t.type_ + " / " + symbol;
+                     if (t.thing_char) {
+                         info_to_cache += t.thing_char;
+                     };
+                     if (t.name_) {
+                         info_to_cache += " (" + t.name_ + ")";
+                     }
+                     info_to_cache += " / protection: " + protection + "\n";
+                 }
+            }
+            if (this.position in game.portals) {
+                info_to_cache += "PORTAL: " + game.portals[this.position] + "\n";
+            }
+            if (this.position in this.annotations) {
+                info_to_cache += "ANNOTATION: " + this.annotations[this.position];
+            }
         }
-        return info;
+        this.info_cached = info_to_cache;
+        return this.info_cached;
     },
     annotate: function(msg) {
         if (msg.length == 0) {
index 4509d33a73dc1d01a294524edafa21dd4800ca13..8dea1766ba8aef9ae82bbd56db03b4c81b267798 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python3
 from plomrogue.game import Game
 from plomrogue.commands import (cmd_ALL, cmd_LOGIN, cmd_NICK, cmd_PING, cmd_THING,
-                                cmd_MAP, cmd_TURN, cmd_MAP_LINE, cmd_GET_ANNOTATION,
+                                cmd_MAP, cmd_TURN, cmd_MAP_LINE,
                                 cmd_ANNOTATE, cmd_PORTAL, cmd_GET_GAMESTATE,
                                 cmd_TASKS, cmd_MAP_CONTROL_LINE, cmd_MAP_CONTROL_PW,
                                 cmd_GOD_ANNOTATE, cmd_GOD_PORTAL, cmd_THING_TYPES,
@@ -33,7 +33,6 @@ game.register_command(cmd_MAP)
 game.register_command(cmd_MAP_LINE)
 game.register_command(cmd_MAP_CONTROL_LINE)
 game.register_command(cmd_MAP_CONTROL_PW)
-game.register_command(cmd_GET_ANNOTATION)
 game.register_command(cmd_ANNOTATE)
 game.register_command(cmd_PORTAL)
 game.register_command(cmd_GOD_ANNOTATE)
index 420c858190b6ab51dbb1329274bee2bd89fd432d..70d60b27f1c6ee8abfccf31f6b3d0a0f73d54d2e 100755 (executable)
@@ -134,12 +134,12 @@ class PlomSocketClient(PlomSocket):
             pass  # we assume socket will be known as dead by now
 
 def cmd_TURN(game, n):
-    game.info_db = {}
-    game.info_hints = []
+    game.annotations = {}
     game.turn = n
     game.things = []
     game.portals = {}
     game.turn_complete = False
+    game.fov = ''
 cmd_TURN.argtypes = 'int:nonneg'
 
 def cmd_LOGIN_OK(game):
@@ -224,10 +224,9 @@ cmd_MAP_CONTROL.argtypes = 'string'
 def cmd_GAME_STATE_COMPLETE(game):
     if game.tui.mode.name == 'post_login_wait':
         game.tui.switch_mode('play')
-    if game.tui.mode.shows_info:
-        game.tui.query_info()
     game.turn_complete = True
     game.tui.do_refresh = True
+    game.tui.info_cached = None
 cmd_GAME_STATE_COMPLETE.argtypes = ''
 
 def cmd_PORTAL(game, position, msg):
@@ -250,12 +249,8 @@ def cmd_ARGUMENT_ERROR(game, msg):
     game.tui.do_refresh = True
 cmd_ARGUMENT_ERROR.argtypes = 'string'
 
-def cmd_ANNOTATION_HINT(game, position):
-    game.info_hints += [position]
-cmd_ANNOTATION_HINT.argtypes = 'yx_tuple:nonneg'
-
 def cmd_ANNOTATION(game, position, msg):
-    game.info_db[position] = msg
+    game.annotations[position] = msg
     if game.tui.mode.shows_info:
         game.tui.do_refresh = True
 cmd_ANNOTATION.argtypes = 'yx_tuple:nonneg string'
@@ -309,7 +304,6 @@ class Game(GameBase):
         self.register_command(cmd_MAP_CONTROL)
         self.register_command(cmd_PORTAL)
         self.register_command(cmd_ANNOTATION)
-        self.register_command(cmd_ANNOTATION_HINT)
         self.register_command(cmd_GAME_STATE_COMPLETE)
         self.register_command(cmd_ARGUMENT_ERROR)
         self.register_command(cmd_GAME_ERROR)
@@ -320,8 +314,7 @@ class Game(GameBase):
         self.register_command(cmd_RANDOM_COLORS)
         self.map_content = ''
         self.player_id = -1
-        self.info_db = {}
-        self.info_hints = []
+        self.annotations = {}
         self.portals = {}
         self.terrains = {}
 
@@ -536,14 +529,9 @@ class TUI:
         if len(self.log) > 100:
             self.log = self.log[-100:]
 
-    def query_info(self):
-        self.send('GET_ANNOTATION ' + str(self.explorer))
-
     def restore_input_values(self):
-        if self.mode.name == 'annotate' and self.explorer in self.game.info_db:
-            info = self.game.info_db[self.explorer]
-            if info != '(none)':
-                self.input_ = info
+        if self.mode.name == 'annotate' and self.explorer in self.game.annotations:
+            self.input_ = self.game.annotations[self.explorer]
         elif self.mode.name == 'portal' and self.explorer in self.game.portals:
             self.input_ = self.game.portals[self.explorer]
         elif self.mode.name == 'password':
@@ -597,8 +585,6 @@ class TUI:
         if self.mode.shows_info or self.mode.name == 'control_tile_draw':
             player = self.game.get_thing(self.game.player_id)
             self.explorer = YX(player.position.y, player.position.x)
-            if self.mode.shows_info:
-                self.query_info()
         if self.mode.is_single_char_entry:
             self.show_help = True
         if self.mode.name == 'waiting_for_server':
@@ -640,6 +626,47 @@ class TUI:
         curses.init_color(2, rand(0), rand(0), rand(0))
         self.do_refresh = True
 
+    def get_info(self):
+        if self.info_cached:
+            return self.info_cached
+        pos_i = self.explorer.y * self.game.map_geometry.size.x + self.explorer.x
+        info_to_cache = ''
+        if len(self.game.fov) > pos_i and self.game.fov[pos_i] != '.':
+            info_to_cache += 'outside field of view'
+        else:
+            terrain_char = self.game.map_content[pos_i]
+            terrain_desc = '?'
+            if terrain_char in self.game.terrains:
+                terrain_desc = self.game.terrains[terrain_char]
+            info_to_cache += 'TERRAIN: "%s" / %s\n' % (terrain_char,
+                                                       terrain_desc)
+            protection = self.game.map_control_content[pos_i]
+            if protection == '.':
+                protection = 'unprotected'
+            info_to_cache += 'PROTECTION: %s\n' % protection
+            for t in self.game.things:
+                if t.position == self.explorer:
+                    protection = t.protection
+                    if protection == '.':
+                        protection = 'none'
+                    info_to_cache += 'THING: %s / %s' %\
+                        (t.type_, self.game.thing_types[t.type_])
+                    if hasattr(t, 'thing_char'):
+                        info_to_cache += t.thing_char
+                    if hasattr(t, 'name'):
+                        info_to_cache += ' (%s)' % t.name
+                    info_to_cache += ' / protection: %s\n' % protection
+            if self.explorer in self.game.portals:
+                info_to_cache += 'PORTAL: ' +\
+                    self.game.portals[self.explorer] + '\n'
+            else:
+                info_to_cache += 'PORTAL: (none)\n'
+            if self.explorer in self.game.annotations:
+                info_to_cache += 'ANNOTATION: ' +\
+                    self.game.annotations[self.explorer]
+        self.info_cached = info_to_cache
+        return self.info_cached
+
     def loop(self, stdscr):
         import datetime
 
@@ -694,9 +721,8 @@ class TUI:
         def move_explorer(direction):
             target = self.game.map_geometry.move_yx(self.explorer, direction)
             if target:
+                self.info_cached = None
                 self.explorer = target
-                if self.mode.shows_info:
-                    self.query_info()
                 if self.tile_draw:
                     self.send_tile_control_command()
             else:
@@ -715,42 +741,7 @@ class TUI:
                 safe_addstr(max_y - i - 1, self.window_width, lines[i])
 
         def draw_info():
-            if not self.game.turn_complete:
-                return
-            pos_i = self.explorer.y * self.game.map_geometry.size.x + self.explorer.x
-            info = 'MAP VIEW: %s\n' % self.map_mode
-            if self.game.fov[pos_i] != '.':
-                info += 'outside field of view'
-            else:
-                terrain_char = self.game.map_content[pos_i]
-                terrain_desc = '?'
-                if terrain_char in self.game.terrains:
-                    terrain_desc = self.game.terrains[terrain_char]
-                info += 'TERRAIN: "%s" / %s\n' % (terrain_char, terrain_desc)
-                protection = self.game.map_control_content[pos_i]
-                if protection == '.':
-                    protection = 'unprotected'
-                info += 'PROTECTION: %s\n' % protection
-                for t in self.game.things:
-                    if t.position == self.explorer:
-                        protection = t.protection
-                        if protection == '.':
-                            protection = 'none'
-                        info += 'THING: %s / %s' % (t.type_,
-                                                    self.game.thing_types[t.type_])
-                        if hasattr(t, 'thing_char'):
-                            info += t.thing_char
-                        if hasattr(t, 'name'):
-                            info += ' (%s)' % t.name
-                        info += ' / protection: %s\n' % protection
-                if self.explorer in self.game.portals:
-                    info += 'PORTAL: ' + self.game.portals[self.explorer] + '\n'
-                else:
-                    info += 'PORTAL: (none)\n'
-                if self.explorer in self.game.info_db:
-                    info += 'ANNOTATION: ' + self.game.info_db[self.explorer]
-                else:
-                    info += 'ANNOTATION: waiting …'
+            info = 'MAP VIEW: %s\n%s' % (self.map_mode, self.get_info())
             lines = msg_into_lines_of_width(info, self.window_width)
             height_header = 2
             for i in range(len(lines)):
@@ -792,7 +783,7 @@ class TUI:
                         map_lines_split += [[c + ' ' for c
                                              in self.game.map_content[start:end]]]
                 if self.map_mode == 'terrain + annotations':
-                    for p in self.game.info_hints:
+                    for p in self.game.annotations:
                         map_lines_split[p.y][p.x] = 'A '
                 elif self.map_mode == 'terrain + things':
                     for p in self.game.portals.keys():