home · contact · privacy
Control login limit from optional file to be changed during server run.
[plomrogue2] / plomrogue / game.py
index 0c84e044d5b087baf52bdcde18b0c7b8f7e40267..b76860aa5ff7954c802c63334d22367a9acb7ffa 100755 (executable)
@@ -50,7 +50,14 @@ class SaveableMap(Map):
             return False
         return True
 
-    def draw_presets(self, alternate_hex=0):
+    def draw_presets(self, big_yx, type_):
+        if type_ == 1:
+            if big_yx.y < 0:
+                self.terrain = 'X' * self.size_i
+        elif type_ == 2:
+            self.draw_presets_grid(big_yx)
+
+    def draw_presets_grid(self, big_yx):
         old_modified = self.modified
         if type(self.geometry) == MapGeometrySquare:
             self.set_line(0, 'X' * self.geometry.size.x)
@@ -88,6 +95,7 @@ class SaveableMap(Map):
                                 if self.inside(yx):
                                     self[yx] = 'X'
 
+                alternate_hex = big_yx.y % 2
                 if alternate_hex:
                     draw_snake(offset + YX(0, 0))
                 draw_snake(offset + YX((0 + alternate_hex) * distance,
@@ -130,8 +138,9 @@ class Game(GameBase):
         self.map_controls = {}
         self.map_control_passwords = {}
         self.annotations = {}
-        self.spawn_point = YX(0, 0), YX(0, 0)
+        self.spawn_points = []
         self.portals = {}
+        self.intro_messages = []
         self.player_chars = string.digits + string.ascii_letters
         self.players_hat_chars = {}
         self.player_char_i = -1
@@ -146,6 +155,7 @@ class Game(GameBase):
             '=': Terrain('=', 'glass', blocks_sound=True, blocks_movement=True),
             'T': Terrain('T', 'table', blocks_movement=True),
         }
+        self.draw_control_presets = 1
         if os.path.exists(self.io.save_file):
             if not os.path.isfile(self.io.save_file):
                 raise GameError('save file path refers to non-file')
@@ -194,6 +204,12 @@ class Game(GameBase):
             return self.thing_types.keys()
         return None
 
+    def get_default_spawn_point(self):
+        import random
+        if len(self.spawn_points) == 0:
+            return (YX(0, 0), YX(0, 0))
+        return random.choice(self.spawn_points)
+
     def get_map_geometry_shape(self):
         return self.map_geometry.__class__.__name__[len('MapGeometry'):]
 
@@ -277,7 +293,7 @@ class Game(GameBase):
             self.io.send('PLAYERS_HAT_CHARS ' + quote(player.get_cookie_chars()),
                          c_id)
             self.io.send('STATS %s %s' % (player.need_for_toilet,
-                                          player.weariness), c_id)
+                                          player.energy), c_id)
             if player.id_ in player_ids_send_fov:
                 self.io.send('FOV %s' % quote(player.fov_stencil.terrain), c_id)
                 self.io.send('MAP %s %s %s' % (self.get_map_geometry_shape(),
@@ -306,8 +322,9 @@ class Game(GameBase):
                     if hasattr(t, 'installable') and not t.portable:
                         self.io.send('THING_INSTALLED %s' % (t.id_), c_id)
                     if hasattr(t, 'design'):
-                        self.io.send('THING_HAT %s %s' % (t.id_,
-                                                          quote(t.design)), c_id)
+                        self.io.send('THING_DESIGN %s %s %s'
+                                     % (t.id_, t.design_size, quote(t.design)),
+                                     c_id)
                 for t in [t for t in player.seen_things if t.carrying]:
                     # send this last so all carryable things are already created
                     self.io.send('THING_CARRYING %s %s' % (t.id_, t.carrying.id_),
@@ -332,25 +349,42 @@ class Game(GameBase):
         self.changed = True
 
     def login(self, nick, connection_id):
+        login_limit_filename = 'login_limit'
+        if os.path.exists(login_limit_filename):
+            with open(login_limit_filename, 'r') as f:
+                lines = f.readlines()
+                login_limit = int(lines[0])
+                if len(self.sessions) > login_limit - 1:
+                    print('DEBUG LOGIN TOO MANY FOR', nick, connection_id)
+                    self.io.send('CHAT "sorry, too many users currently '
+                                 'logged in, try again later '
+                                 'by re-entering your name"', connection_id)
+                    return
         for t in [t for t in self.things
                   if t.type_ == 'Player' and t.name == nick]:
             self.io.send('GAME_ERROR ' + quote('name already in use'),
                          connection_id)
             return
-        t = self.add_thing('Player', self.spawn_point)
+        t = self.add_thing('Player', self.get_default_spawn_point())
         t.name = nick
         t.thing_char = self.get_next_player_char()
         self.sessions[connection_id] = {
             'thing_id': t.id_,
             'status': 'player'
         }
+        print('DEBUG LOGIN', t.name, len(self.sessions))
         self.io.send('PLAYER_ID %s' % t.id_, connection_id)
         self.io.send('LOGIN_OK', connection_id)
+        for msg in self.intro_messages:
+            self.io.send('CHAT ' + quote(msg), connection_id)
         self.io.send('CHAT ' + quote(t.name + ' entered the map.'))
         for s in [s for s in self.things
                   if s.type_ == 'SpawnPoint' and s.name == t.name]:
             t.position = s.position
-            break
+            if s.temporary:
+                self.remove_thing(s)
+                break
+        t.try_to_sit()
 
     def run_tick(self):
 
@@ -366,6 +400,10 @@ class Game(GameBase):
                 t = self.get_player(connection_id)
                 if hasattr(t, 'name'):
                     self.io.send('CHAT ' + quote(t.name + ' left the map.'))
+                spawn_point = self.add_thing('SpawnPoint', t.position)
+                spawn_point.temporary = True
+                spawn_point.name = t.name
+                print('DEBUG LEFT MAP', t.name)
                 self.remove_thing(t)
                 to_delete += [connection_id]
         for connection_id in to_delete:
@@ -498,6 +536,9 @@ class Game(GameBase):
         with open(self.io.save_file, 'w') as f:
             write(f, 'TURN %s' % self.turn)
             map_geometry_shape = self.get_map_geometry_shape()
+            # must come before MAP, otherwise first get_map uses the default
+            # TODO: refactor into MAP
+            write(f, 'MAP_CONTROL_PRESETS %s' % self.draw_control_presets)
             write(f, 'MAP %s %s' % (map_geometry_shape, self.map_geometry.size,))
             for terrain in self.terrains.values():
                 write(f, 'TERRAIN %s %s %s %s %s' % (quote(terrain.character),
@@ -546,11 +587,13 @@ class Game(GameBase):
                     write(f, 'GOD_THING_NAME %s %s' % (t.id_, quote(t.name)))
                 if hasattr(t, 'installable') and (not t.portable):
                     write(f, 'THING_INSTALLED %s' % t.id_)
+                if hasattr(t, 'design'):
+                    if t.type_ != 'Hat':
+                        write(f, 'GOD_THING_DESIGN_SIZE %s %s' % (t.id_,
+                                                                  t.design_size))
+                    write(f, 'GOD_THING_DESIGN %s %s' % (t.id_, quote(t.design)))
                 if t.type_ == 'Door' and t.blocks_movement:
                     write(f, 'THING_DOOR_CLOSED %s %s' % (t.id_, int(t.locked)))
-                elif t.type_ == 'Hat':
-                    write(f, 'THING_HAT_DESIGN %s %s' % (t.id_,
-                                                         quote(t.design)))
                 elif t.type_ == 'MusicPlayer':
                     write(f, 'THING_MUSICPLAYER_SETTINGS %s %s %s %s' %
                           (t.id_, int(t.playing), t.playlist_index, int(t.repeat)))
@@ -561,8 +604,29 @@ class Game(GameBase):
                     write(f, 'THING_BOTTLE_EMPTY %s' % t.id_)
                 elif t.type_ == 'DoorKey':
                     write(f, 'THING_DOOR_KEY %s %s' % (t.id_, t.door.id_))
-            write(f, 'SPAWN_POINT %s %s' % (self.spawn_point[0],
-                                            self.spawn_point[1]))
+                elif t.type_ == 'Crate':
+                    for item in t.content:
+                        write(f, 'THING_CRATE_ITEM %s %s' % (t.id_, item.id_))
+                elif t.type_ == 'SpawnPoint':
+                    timestamp = 0
+                    if t.temporary:
+                        timestamp = int(t.created_at.timestamp())
+                    write(f, 'THING_SPAWNPOINT_CREATED %s %s' % (t.id_,
+                                                                 timestamp))
+            next_thing_id = self.new_thing_id()
+            for t in [t for t in self.things if t.type_ == 'Player']:
+                write(f, 'THING %s %s SpawnPoint %s'
+                      % (t.position[0], t.position[1], next_thing_id))
+                write(f, 'GOD_THING_NAME %s %s' % (next_thing_id, t.name))
+                write(f, 'THING_SPAWNPOINT_CREATED %s %s'
+                      % (next_thing_id, int(datetime.datetime.now().timestamp())))
+                next_thing_id += 1
+            for s in self.spawn_points:
+                write(f, 'SPAWN_POINT %s %s' % (s[0], s[1]))
+            for msg in self.intro_messages:
+                write(f, 'INTRO_MSG %s' % quote(msg))
+
+
 
     def get_map(self, big_yx, type_='normal'):
         if type_ == 'normal':
@@ -572,7 +636,7 @@ class Game(GameBase):
         if big_yx not in maps:
             maps[big_yx] = SaveableMap(self.map_geometry)
             if type_ == 'control':
-                maps[big_yx].draw_presets(big_yx.y % 2)
+                maps[big_yx].draw_presets(big_yx, self.draw_control_presets)
         return maps[big_yx]
 
     def new_world(self, map_geometry):
@@ -581,7 +645,6 @@ class Game(GameBase):
         self.annotations = {}
         self.portals = {}
         self.admin_passwords = []
-        self.spawn_point = YX(0, 0), YX(0, 0)
         self.map_geometry = map_geometry
         self.map_control_passwords = {'X': 'secret'}
         self.get_map(YX(0, 0))