27842ac9 by Barry

Fairly big rewrite to be more memory friendly.

1 parent 27c9cd56
import utils
from roomloader import RoomLoader
global_aliases = {
'n': 'north',
......@@ -7,10 +10,23 @@ global_aliases = {
'u': 'up',
'd': 'down',
'l': 'look',
'\'': 'say',
}
roomloader = RoomLoader('rooms')
class CommandHandler(object):
def alias(self, command):
if command in global_aliases:
return global_aliases[command]
if 'aliases' not in self.players[self.id]:
self.players[self.id]["aliases"] = {}
if command in self.players[self.id]["aliases"]:
return self.players[self.id]["aliases"][command]
return command
def tokenize_params(self):
cleaned = self.params.lower().strip()
tokens = []
......@@ -21,14 +37,15 @@ class CommandHandler(object):
self.tokens = tokens
def look(self):
room_name = self.players[self.id]["room"]
self.mud.send_message(self.id, "")
if len(self.tokens) > 0 and self.tokens[0] == 'at':
del self.tokens[0]
if len(self.tokens) == 0:
self.mud.send_message(self.id, self.room.get_description())
self.mud.send_message(self.id, roomloader.get_description(room_name))
else:
subject = self.tokens[0]
items = self.room.get_look_items()
items = roomloader.get_look_items(room_name)
for item in items:
if subject in item:
self.mud.send_message(self.id, items[item])
......@@ -51,7 +68,7 @@ class CommandHandler(object):
# send player a message containing the list of exits from this room
self.mud.send_message(self.id, "Exits are: {}".format(
", ".join(self.room.get_exits())))
", ".join(roomloader.get_exits(room_name))))
return True
......@@ -69,7 +86,7 @@ class CommandHandler(object):
ex = self.params.lower().strip()
# if the specified exit is found in the room's exits list
if ex in self.room.get_exits():
if ex in roomloader.get_exits(self.players[self.id]["room"]):
# go through all the players in the game
for pid, pl in self.players.items():
# if player is in the same room and isn't the player
......@@ -82,13 +99,13 @@ class CommandHandler(object):
self.players[self.id]["name"], ex))
# update the player's current room to the one the exit leads to
loaded = self.load_room(self.room.get_exits()[ex])
if loaded == None:
if roomloader.get_title(ex) == None:
self.mud.send_message(id, "An invisible force prevents you from going in that direction.")
return True
else:
self.players[self.id]["room"] = self.room.get_exits()[ex]
self.room = loaded
self.players[self.id]["room"] = roomloader.get_exits(self.players[self.id]["room"])[ex]
self.room = ex
# go through all the players in the game
for pid, pl in self.players.items():
......@@ -103,41 +120,70 @@ class CommandHandler(object):
self.players[self.id]["name"], ex))
# send the player a message telling them where they are now
self.mud.send_message(id, "You arrive at '{}'".format(self.room.get_title()))
self.mud.send_message(self.id, "You arrive at '{}'".format(roomloader.get_title(self.players[self.id]["room"])))
self.tokens = []
return self.look()
# the specified exit wasn't found in the current room
else:
# send back an 'unknown exit' message
self.mud.send_message(id, "Unknown exit '{}'".format(ex))
return True
def help(self):
if len(self.tokens) > 0:
filename = 'help/' + self.tokens[0] + '.txt'
else:
filename = 'help/help.txt'
try:
with open(filename, 'r', encoding='utf-8') as f:
for line in f:
self.mud.send_message(self.id, line, '\r')
self.mud.send_message(self.id, '')
except:
self.mud.send_message(self.id, 'No help topics available for \"{}\". A list\r\n of all commands is available at: help index'.format(self.tokens[0]))
def say(self):
# go through every player in the game
for pid, pl in self.players.items():
# if they're in the same room as the player
if self.players[pid]["room"] == self.players[self.id]["room"]:
# send them a message telling them what the player said
self.mud.send_message(pid, "{} says: {}".format(
self.players[self.id]["name"], self.params))
return True
def quit(self):
# send the player back the list of possible commands
self.mud.send_message(id, "Saving...")
utils.save_object_to_file(self.players[self.id], "players/{}.json".format(self.players[self.id]["name"]))
self.mud.disconnect_player(self.id)
return True
def save(self):
# send the player back the list of possible commands
self.mud.send_message(self.id, "Saving...")
utils.save_object_to_file(self.players[self.id], "players/{}.json".format(self.players[self.id]["name"]))
self.mud.send_message(self.id, "Save complete")
return True
def alias(self, command):
if command in global_aliases:
return global_aliases[command]
if 'aliases' not in self.players[self.id]:
self.players[self.id]["aliases"] = {}
if command in self.players[self.id]["aliases"]:
return self.players[self.id]["aliases"][command]
return command
def parse(self, id, cmd, params, room, mud, players, load_room):
def parse(self, id, cmd, params, mud, players):
self.id = id
self.params = params
self.tokenize_params()
self.room = room
self.mud = mud
self.players = players
self.load_room = load_room
self.cmd = self.alias(cmd)
try:
if self.cmd in self.room.get_exits():
if self.cmd in roomloader.get_exits(self.players[id]["room"]):
self.params = self.cmd + " " + self.params
self.cmd = "go"
method = getattr(self, self.cmd)
return method()
except AttributeError:
except AttributeError as e:
print(e)
mud.send_message(id, "Unknown command '{}'".format(self.cmd))
return False
......
Command:
go
Usage:
go <exit>
Description:
The go command moves through the exit specified. Common exits are
north, south, east, west, up, and down. Normally the exits will
be shown when you 'look' but not always since there may be a
hidden exit.
You may also exit from an area by typing the direction name itself,
e.g. > north
......@@ -2,6 +2,10 @@ Commands:
say <message> - Says something out loud, e.g. 'say Hello'
look - Examines the surroundings, e.g. 'look'
go <exit> - Moves through the exit specified, e.g. 'go outside'
<exit> - You can exclude the 'go' and just enter the exit.
who - Lists all players currently connected
save - Saves your character
quit - Saves your character then closes the connection
\ No newline at end of file
quit - Saves your character then closes the connection
For general help:
help index
\ No newline at end of file
......
Additional information can be learned about a command by passing a command: help <subject>
Help Topics:
aliases
go
look
quit
save
say
who
......@@ -22,19 +22,20 @@ author: Mark Frimston - mfrimston@gmail.com
"""
import time
# import datetime
#import io
import json
import machine
import sys
if 'esp' in sys.platform:
import gc
import machine
# import the MUD server class
from mudserver import MudServer
from commands import CommandHandler
from commandhandler import CommandHandler
from roomloader import RoomLoader
import utils
print('STARTING MUD\r\n\r\n\r\n')
flash_button = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
rooms = {}
if 'esp' in sys.platform:
flash_button = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
# stores the players in the game
players = {}
......@@ -44,34 +45,7 @@ mud = MudServer()
cmd_handler = CommandHandler()
def load_room(room_name):
print("Loading room:" + room_name)
try:
if room_name in rooms:
return rooms[room_name]
module = __import__('rooms.' + room_name.lower())
room_module = getattr(module, room_name.lower())
room_class = getattr(room_module, room_name)
instance = room_class()
rooms[room_name] = instance
return instance
except Exception as e:
print(e)
return None
def save_object_to_file(obj, filename):
with open(filename, 'w', encoding='utf-8') as f:
f.write(json.dumps(obj))
def load_object_from_file(filename):
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.loads(f.read())
except Exception:
return None
roomloader = RoomLoader('rooms')
def prompt(pid):
if "prompt" not in players[pid]:
......@@ -80,20 +54,14 @@ def prompt(pid):
# main game loop. We loop forever (i.e. until the program is terminated)
while True:
gc.collect()
if flash_button.value() == 0:
break
if 'esp' in sys.platform:
gc.collect()
if flash_button.value() == 0:
break
# pause for 1/5 of a second on each loop, so that we don't constantly
# use 100% CPU time
time.sleep(0.001)
# TODO: Add some cache removal if older than X
# now = datetime.datetime.now()
# delta = now - datetime.timedelta(minutes = 2)
# for room in rooms:
# if room.created < delta:
# del rooms[room]
# 'update' must be called in the loop to keep the game running and give
# us up-to-date information
mud.update()
......@@ -101,8 +69,6 @@ while True:
# go through any newly connected players
for id in mud.get_new_players():
# add the new player to the dictionary, noting that they've not been
# named yet.
# The dictionary key is the player's id number. We set their room to
......@@ -115,7 +81,9 @@ while True:
"prompt": "> ",
"aliases": {},
}
with open('welcome.txt', 'r', encoding='utf-8') as f:
for line in f:
mud.send_message(id, line, "\r")
# send the new player a prompt for their name
mud.send_message(id, "What is your name?")
......@@ -148,15 +116,13 @@ while True:
if id not in players:
continue
if players[id]["room"]:
rm = load_room(players[id]["room"])
# if the player hasn't given their name yet, use this first command as
# their name and move them to the starting room.
if players[id]["name"] is None:
if command.strip() == '' or command is None:
mud.send_message(id, "Invalid Name")
continue
loaded_player = load_object_from_file("players/{}.json".format(command))
loaded_player = utils.load_object_from_file("players/{}.json".format(command))
if loaded_player is None:
players[id]["name"] = command
players[id]["room"] = "Tavern"
......@@ -175,53 +141,15 @@ while True:
+ "\r\nType 'help' for a list of commands. Have fun!\r\n\r\n")
# send the new player the description of their current room
mud.send_message(id, load_room(players[id]["room"]).get_description())
# each of the possible commands is handled below. Try adding new
# commands to the game!
mud.send_message(id, roomloader.get_description(players[id]["room"]))
# 'help' command
elif command == "help":
with open('help/help.txt', 'r', encoding='utf-8') as f:
for line in f:
mud.send_message(id, line, '\r')
mud.send_message(id, '')
# 'help' command
elif command == "quit":
# send the player back the list of possible commands
mud.send_message(id, "Saving...")
save_object_to_file(players[id], "players/{}.json".format(players[id]["name"]))
mud.disconnect_player(id)
# 'help' command
elif command == "save":
# send the player back the list of possible commands
mud.send_message(id, "Saving...")
save_object_to_file(players[id], "players/{}.json".format(players[id]["name"]))
mud.send_message(id, "Save complete")
# 'say' command
elif command == "say":
# go through every player in the game
for pid, pl in players.items():
# if they're in the same room as the player
if players[pid]["room"] == players[id]["room"]:
# send them a message telling them what the player said
mud.send_message(pid, "{} says: {}".format(
players[id]["name"], params))
# 'look' command
else:
handler_results = cmd_handler.parse(id, command, params, rm, mud, players, load_room)
handler_results = cmd_handler.parse(id, command, params, mud, players)
prompt(id)
# Start WIFI Setup
if flash_button.value() == 0:
import wifiweb
\ No newline at end of file
if 'esp' in sys.platform:
if flash_button.value() == 0:
import wifiweb
\ No newline at end of file
......
......@@ -301,7 +301,7 @@ class MudServer(object):
try:
# read data from the socket, using a max length of 4096
data = cl.socket.recv(4096).decode("latin1")
data = cl.socket.recv(1024).decode("latin1")
# process the data, stripping out any special Telnet commands
message = self._process_sent_data(cl, data)
......
{"name": "test", "room": "Room001", "inventory": null, "prompt": "> "}
\ No newline at end of file
{"name": "test", "room": "Tavern", "inventory": {}, "prompt": "> ", "aliases": {}}
\ No newline at end of file
......
import os
import json
class RoomLoader(object):
def __init__(self, directory):
self.directory = directory
def _get_room_file(self, room_name):
try:
filename = os.path.join(self.directory, room_name) + ".txt"
with open(filename, 'r', encoding='utf-8') as f:
return json.loads(f.read())
except Exception as e:
print("Error opening room file: {} with exception: {}".format(room_name, e))
return {"title": "", "description": "", "exits": {}, "look_items": {}}
def get_title(self, room_name):
return self._get_room_file(room_name)['title']
def get_description(self, room_name):
desc = self._get_room_file(room_name)['description']
chunks, chunk_size = len(desc), 80 #len(x)/4
lines = [ desc[i:i+chunk_size] for i in range(0, chunks, chunk_size) ]
return '\r\n'.join(lines)
def get_exits(self, room_name):
return self._get_room_file(room_name)['exits']
def get_look_items(self, room_name):
return self._get_room_file(room_name)['look_items']
File mode changed
# import datetime
class BaseRoom(object):
def __init__(self, title, description, exits, look_items):
self.title = title
self.description = description
self.exits = exits
self.look_items = look_items
# self.created = datetime.datetime.now()
def get_title(self):
return self.title
def get_description(self):
return self.description
def get_exits(self):
return self.exits
def get_look_items(self):
return self.look_items
from .baseroom import BaseRoom
class Outside(BaseRoom):
def __init__(self):
title = "Outside the Tavern"
description = "You are standing outside of a simple tavern made of pressed clay and shale roof. \r\nThe door becons you to come in and have a drink."
exits = {"tavern": "Tavern"}
look_items = {
"tavern": "A roughly constructed building.",
}
super(Outside, self).__init__(title, description, exits, look_items)
{
"title": "Outside the Tavern",
"description": "You are standing outside of a simple tavern made of pressed clay and shale roof. The door becons you to come in and have a drink.",
"exits": {"tavern": "Tavern"},
"look_items": {
"tavern": "A roughly constructed building."
}
}
\ No newline at end of file
from .baseroom import BaseRoom
class Room001(BaseRoom):
def __init__(self):
title = "Behind the bar"
description = "The back of the bar gives a full view of the tavern. The bar\r\n top is a large wooden plank thrown across some barrels."
exits = {"tavern": "Tavern"}
look_items = {
"bar": "The bar is a long wooden plank thrown over roughly hewn barrels.",
"barrel,barrels": "The old barrels bands are thick with oxidation and stained\r\n with the purple of spilled wine.",
"wooden,oak,plank": "An old solid oak plank that has a large number of chips\r\n and drink marks."
}
super(Room001, self).__init__(title, description, exits, look_items)
{
"title": "Behind the bar",
"description": "The back of the bar gives a full view of the tavern. The bar\r\n top is a large wooden plank thrown across some barrels.",
"exits": {"tavern": "Tavern"},
"look_items": {
"bar": "The bar is a long wooden plank thrown over roughly hewn barrels.",
"barrel,barrels": "The old barrels bands are thick with oxidation and stained\r\n with the purple of spilled wine.",
"wooden,oak,plank": "An old solid oak plank that has a large number of chips\r\n and drink marks."
}
}
from .baseroom import BaseRoom
class Tavern(BaseRoom):
def __init__(self):
title = "Tavern"
description = "You're in a cozy tavern warmed by an open fire."
exits = {"outside": "Outside", "behind bar": "Room001"}
look_items = {
{
"title": "Tavern",
"description": "You're in a cozy tavern warmed by an open fire.",
"exits": {"outside": "Outside", "behind bar": "Room001"},
"look_items": {
"bar": "The bar is a long wooden plank thrown over roughly hewn barrels.",
"barrel,barrels": "The old barrels bands are thick with oxidation and stained\r\n with the purple of spilled wine.",
"wooden,oak,plank": "An old solid oak plank that has a large number of chips\r\n and drink marks.",
"fire": "The fire crackles quietly in the corner providing a small amount of light and heat."
}
super(Tavern, self).__init__(title, description, exits, look_items)
}
\ No newline at end of file
......
{"test": "value"}
\ No newline at end of file
import json
def save_object_to_file(self, obj, filename):
with open(filename, 'w', encoding='utf-8') as f:
f.write(json.dumps(obj))
def save_object_to_file(obj, filename):
with open(filename, 'w', encoding='utf-8') as f:
f.write(json.dumps(obj))
def load_object_from_file(filename):
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.loads(f.read())
except Exception:
return None
, \ / ,
/ \ )\__/( / \
/ \ (_\ /_) / \
____/_____\__\@ @/___/_____\____
| |\../| |
| \VV/ |
| ----=WeeMud=---- |
|_________________________________|
| /\ / \\ \ /\ |
| / V )) V \ |
|/ ` // ' \|
` V '
This mud is running on an ESP8266 chip
over a wireless connection to the internet.
It is standalone and 100% contained in 4mb
of flash and 256k of RAM. Please explore
and try to conquer the realm.
......@@ -40,32 +40,34 @@ class DNSQuery:
def accept_conn(listen_sock):
cl, addr = listen_sock.accept()
print('client connected from', addr)
print('Request:')
request = cl.recv(2048)
print(request)
if 'GET /?' in request:
#GET /?network=Volley&networkpass=test
request = str(request)
part = request.split(' ')[1]
params = part.split('?')[1].split('&')
network_param = params[0].split('=')[1]
network_pass_param = params[1].split('=')[1]
print("Setting network to: {}".format(network_param))
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect(network_param, network_pass_param)
while sta_if.isconnected() == False:
time.sleep(1)
cl.send('<html><body>Configuration Saved!<br><br>Network: {} <br>IP Address: {}<br>Password: {}<br><br><br>Restarting Device...</body></html>'.format(network_param, sta_if.ifconfig()[0], network_pass_param))
cl.close()
time.sleep(20)
machine.reset()
return
print('Getting Network STA_IF')
sta_if = network.WLAN(network.STA_IF)
print('Starting Network Scan...')
avail_networks = sta_if.scan()
print('Network Scan Complete')
endpoint_string = ""
for endpoint in avail_networks:
endpoint_string += "<option value={}>{}</option>".format(endpoint[0].decode('latin1'), endpoint[0].decode('latin1'))
......@@ -86,11 +88,8 @@ def accept_conn(listen_sock):
</body>
</html>
\n\n""".format(endpoint_string)
# rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
# response = html % '\n'.join(rows)
print('Sending Response')
cl.send(response)
print('Closing Socket')
time.sleep(0.001)
cl.close()
......