Added image release tool to rebuild automatically and put the bin file
out.
Showing
7 changed files
with
440 additions
and
319 deletions
... | @@ -12,6 +12,14 @@ I like the NodeMcu devices. They have 4mb flash and work well and are fairly rob | ... | @@ -12,6 +12,14 @@ I like the NodeMcu devices. They have 4mb flash and work well and are fairly rob |
12 | 12 | ||
13 | ### How to Load on a New ESP8266 | 13 | ### How to Load on a New ESP8266 |
14 | 14 | ||
15 | Vagrant setup | ||
16 | |||
17 | https://learn.adafruit.com/building-and-running-micropython-on-the-esp8266/build-firmware | ||
18 | |||
19 | Setup Vagrant SCP | ||
20 | |||
21 | vagrant plugin install vagrant-scp | ||
22 | |||
15 | Use python 3.6+ | 23 | Use python 3.6+ |
16 | 24 | ||
17 | Create a virtual environment to save your brainspace. | 25 | Create a virtual environment to save your brainspace. |
... | @@ -38,6 +46,14 @@ Install esptool and the other required pip modules which will let you install th | ... | @@ -38,6 +46,14 @@ Install esptool and the other required pip modules which will let you install th |
38 | pip install -r requirements.txt | 46 | pip install -r requirements.txt |
39 | ``` | 47 | ``` |
40 | 48 | ||
49 | NEW | ||
50 | Custom Vagrant image: | ||
51 | |||
52 | ``` | ||
53 | python release_image.py | ||
54 | ``` | ||
55 | |||
56 | <!-- THIS IS REPLACED BY THE NEW RELEASE IMAGE SCRIPT | ||
41 | After the modules are installed you will need to use esptool to erase the flash. | 57 | After the modules are installed you will need to use esptool to erase the flash. |
42 | if things go wrong... https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html | 58 | if things go wrong... https://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html |
43 | 59 | ||
... | @@ -60,6 +76,7 @@ After the file has been downloaded (use wget? browser? curl? whatever makes you | ... | @@ -60,6 +76,7 @@ After the file has been downloaded (use wget? browser? curl? whatever makes you |
60 | ``` | 76 | ``` |
61 | esptool --port COM1 --baud 460800 write_flash --flash_size=detect 0 esp8266-20171101-v1.9.3.bin | 77 | esptool --port COM1 --baud 460800 write_flash --flash_size=detect 0 esp8266-20171101-v1.9.3.bin |
62 | ``` | 78 | ``` |
79 | --> | ||
63 | 80 | ||
64 | This will write the binary micropython os to the device. After this you will want to validate the write was successful. | 81 | This will write the binary micropython os to the device. After this you will want to validate the write was successful. |
65 | 82 | ... | ... |
1 | #!/usr/bin/env python | 1 | import weemud |
2 | |||
3 | """ | ||
4 | MudServer author: Mark Frimston - mfrimston@gmail.com | ||
5 | |||
6 | Micropython port and expansion author: Barry Ruffner - barryruffner@gmail.com | ||
7 | """ | ||
8 | |||
9 | from time import sleep | ||
10 | from math import floor | ||
11 | from sys import platform | ||
12 | from os import listdir | ||
13 | |||
14 | from mudserver import MudServer | ||
15 | from commandhandler import CommandHandler | ||
16 | from utils import load_object_from_file, save_object_to_file, password_hash, calc_att, get_att | ||
17 | |||
18 | if 'esp' in platform: | ||
19 | from gc import collect, mem_free | ||
20 | from machine import Pin | ||
21 | # import the MUD server class | ||
22 | |||
23 | |||
24 | print('STARTING MUD\r\n\r\n\r\n') | ||
25 | |||
26 | # Setup button so when pressed the mud goes in to wifi hotspot mode on 192.168.4.1 | ||
27 | if 'esp' in platform: | ||
28 | flash_button = Pin(0, Pin.IN, Pin.PULL_UP) | ||
29 | |||
30 | # stores the players in the game | ||
31 | players = {} | ||
32 | |||
33 | # start the server | ||
34 | globals()['mud'] = MudServer() | ||
35 | |||
36 | |||
37 | def show_prompt(pid): | ||
38 | if "prompt" not in players[pid]: | ||
39 | players[pid]["prompt"] = "> " | ||
40 | if 'hp' not in players[pid]: | ||
41 | players[pid]["hp"] = 100 | ||
42 | if 'mp' not in players[pid]: | ||
43 | players[pid]["mp"] = 100 | ||
44 | if 'sta' not in players[pid]: | ||
45 | players[pid]["sta"] = 10 | ||
46 | |||
47 | prompt = players[pid]["prompt"].replace('%st', str(floor(players[pid]['sta']))).replace('%hp', str(floor(players[pid]["hp"]))).replace('%mp', str(floor(players[pid]["mp"]))) | ||
48 | mud.send_message(pid, "\r\n" + prompt, '') | ||
49 | |||
50 | tick = 0.0 | ||
51 | spawn = 0.0 | ||
52 | cmd_handler = CommandHandler() | ||
53 | |||
54 | def spawn_mobs(players): | ||
55 | rooms = listdir('rooms') | ||
56 | for room in rooms: | ||
57 | if '_monsters.json' not in room: | ||
58 | continue | ||
59 | room_monsters = load_object_from_file('rooms/{}'.format(room)) | ||
60 | for mon_name, monster in room_monsters.items(): | ||
61 | monster_template = load_object_from_file('mobs/{}.json'.format(mon_name)) | ||
62 | if not monster_template: | ||
63 | continue | ||
64 | while len(room_monsters[mon_name]['active']) < monster['max']: | ||
65 | print('Spawning {} in {}'.format(mon_name, room)) | ||
66 | mp = get_att(monster_template['spawn']["mp"]) | ||
67 | hp = get_att(monster_template['spawn']["hp"]) | ||
68 | sta = get_att(monster_template['spawn']["sta"]) | ||
69 | new_active = { | ||
70 | "mp": mp, | ||
71 | "maxhp": hp, | ||
72 | "hp": hp, | ||
73 | "sta": sta, | ||
74 | "maxmp": mp, | ||
75 | "target": "", | ||
76 | "action": "attack", | ||
77 | "maxsta": sta} | ||
78 | room_monsters[mon_name]['active'].append(new_active) | ||
79 | for pid, pl in players.items(): | ||
80 | if players[pid]['room'].lower() == room.split('_')[0]: | ||
81 | mud.send_message(pid, "a {} arrived".format(mon_name)) | ||
82 | save_object_to_file(room_monsters, 'rooms/{}'.format(room)) | ||
83 | |||
84 | def run_mobs(players, mud): | ||
85 | for pid, player in players.items(): | ||
86 | if not player or not player.get("name") or not player.get('password'): | ||
87 | continue | ||
88 | if player['mp'] < player['maxmp']: | ||
89 | players[pid]['mp'] += player['mpr'] | ||
90 | if player['sta'] < player['maxsta']: | ||
91 | players[pid]['sta'] += player['star'] | ||
92 | room_monsters = load_object_from_file('rooms/{}_monsters.json'.format(player['room'])) | ||
93 | for mon_name, monster in room_monsters.items(): | ||
94 | monster_template = load_object_from_file('mobs/{}.json'.format(mon_name)) | ||
95 | for active_monster_idx, active_monster in enumerate(monster['active']): | ||
96 | sta = active_monster['sta'] | ||
97 | if active_monster['mp'] < active_monster['maxmp']: | ||
98 | active_monster['mp'] += monster_template['mpr'] | ||
99 | if sta < active_monster['maxsta']: | ||
100 | sta += monster_template['star'] | ||
101 | active_monster['sta'] = sta | ||
102 | if active_monster['action'] == "attack" and active_monster['target'] == player['name']: | ||
103 | if player.get("weapon"): | ||
104 | weapon = load_object_from_file('inventory/{}.json'.format(player['weapon'])) | ||
105 | att = get_att(weapon['damage']) | ||
106 | mud.send_message(pid, "Your %s strikes the %s for %d" % (weapon['title'], mon_name, att,), color='yellow') | ||
107 | else: | ||
108 | att = get_att(player['aa']) | ||
109 | mud.send_message(pid, "You hit the %s for %d" % (mon_name, att,), color='yellow') | ||
110 | active_monster['hp'] -= att | ||
111 | if active_monster['hp'] <= 0: | ||
112 | del room_monsters[mon_name]['active'][active_monster_idx] | ||
113 | mud.send_message(pid, "The %s dies." % (mon_name,), color=['bold', 'blue']) | ||
114 | break | ||
115 | if active_monster.get("weapon"): | ||
116 | weapon = load_object_from_file('inventory/{}.json'.format(active_monster['weapon'])) | ||
117 | att = get_att(weapon['damage']) | ||
118 | mud.send_message(pid, "The %s strikes you with a %s for %d" % (mon_name, weapon['title'], att,), color='magenta') | ||
119 | else: | ||
120 | att = get_att(monster_template['aa']) | ||
121 | mud.send_message(pid, "You were hit by a %s for %d" % (mon_name, att,), color='magenta') | ||
122 | players[pid]['hp'] -= att | ||
123 | if (active_monster['hp']/active_monster['maxhp'] < 0.25) or (active_monster['mp'] > 0 and active_monster['mp']/active_monster['maxmp'] > 0.5) or (sta > 0 and sta/active_monster['maxsta'] > 0.5): | ||
124 | magic_cast = False | ||
125 | if active_monster['mp'] > 0: | ||
126 | att, active_monster['mp'] = calc_att(mud, pid, monster_template['sp'], active_monster['mp']) | ||
127 | players[pid]['hp'] -= att | ||
128 | if att > 0: | ||
129 | magic_cast = True | ||
130 | if not magic_cast: | ||
131 | if sta > 0: | ||
132 | att, sta = calc_att(mud, pid, monster_template['at'], sta) | ||
133 | active_monster['sta'] = sta | ||
134 | players[pid]['hp'] -= att | ||
135 | |||
136 | room_monsters[mon_name]['active'][active_monster_idx] = active_monster | ||
137 | |||
138 | save_object_to_file(room_monsters, 'rooms/{}_monsters.json'.format(player['room'])) | ||
139 | |||
140 | # main game loop. We loop forever (i.e. until the program is terminated) | ||
141 | while True: | ||
142 | if 'esp' in platform: | ||
143 | collect() | ||
144 | if flash_button.value() == 0: | ||
145 | break | ||
146 | # pause for 1/5 of a second on each loop, so that we don't constantly | ||
147 | sleep(0.002) | ||
148 | if 'esp' in platform: | ||
149 | tick += 0.001 | ||
150 | else: | ||
151 | tick += 0.0005 | ||
152 | if spawn >= 10: | ||
153 | spawn = 0 | ||
154 | try: | ||
155 | spawn_mobs(players) | ||
156 | except Exception as e: | ||
157 | print('spawner error:') | ||
158 | print(e) | ||
159 | if 'esp' in platform: | ||
160 | collect() | ||
161 | if tick >= 1: | ||
162 | if 'esp' in platform: | ||
163 | print(mem_free()) | ||
164 | spawn += tick | ||
165 | tick = 0 | ||
166 | try: | ||
167 | run_mobs(players, mud) | ||
168 | except Exception as e: | ||
169 | print('mob error:') | ||
170 | print(e) | ||
171 | # 'update' must be called in the loop to keep the game running and give | ||
172 | # us up-to-date information | ||
173 | mud.update() | ||
174 | |||
175 | # go through any newly connected players | ||
176 | for id in mud.get_new_players(): | ||
177 | |||
178 | # add the new player to the dictionary, noting that they've not been | ||
179 | # named yet. | ||
180 | # The dictionary key is the player's id number. We set their room to | ||
181 | # None initially until they have entered a name | ||
182 | # Try adding more player stats - level, gold, inventory, etc | ||
183 | players[id] = load_object_from_file("defaultplayer.json") | ||
184 | with open('welcome.txt', 'r', encoding='utf-8') as f: | ||
185 | for line in f: | ||
186 | mud.send_message(id, line, "\r") | ||
187 | # send the new player a prompt for their name | ||
188 | #bytes_to_send = bytearray([mud._TN_INTERPRET_AS_COMMAND, mud._TN_WONT, mud._ECHO]) | ||
189 | #mud.raw_send(id, bytes_to_send) | ||
190 | mud.send_message(id, "What is your name?") | ||
191 | |||
192 | # go through any recently disconnected players | ||
193 | for id in mud.get_disconnected_players(): | ||
194 | |||
195 | # if for any reason the player isn't in the player map, skip them and | ||
196 | # move on to the next one | ||
197 | if id not in players: | ||
198 | continue | ||
199 | |||
200 | # go through all the players in the game | ||
201 | for pid, pl in players.items(): | ||
202 | # send each player a message to tell them about the diconnected | ||
203 | # player | ||
204 | if players[pid].get("name") is not None: | ||
205 | mud.send_message(pid, "{} quit the game".format(players[pid]["name"])) | ||
206 | |||
207 | # remove the player's entry in the player dictionary | ||
208 | if players[id]["name"] is not None: | ||
209 | save_object_to_file(players[id], "players/{}.json".format(players[id]["name"])) | ||
210 | del(players[id]) | ||
211 | |||
212 | # go through any new commands sent from players | ||
213 | for id, command, params in mud.get_commands(): | ||
214 | |||
215 | # if for any reason the player isn't in the player map, skip them and | ||
216 | # move on to the next one | ||
217 | if id not in players: | ||
218 | continue | ||
219 | |||
220 | # if the player hasn't given their name yet, use this first command as | ||
221 | # their name and move them to the starting room. | ||
222 | if players[id].get("name") is None: | ||
223 | if command.strip() == '' or len(command) > 12 or not command.isalnum() or command is None: | ||
224 | mud.send_message(id, "Invalid Name") | ||
225 | print("Bad guy!") | ||
226 | print(mud.get_remote_ip(id)) | ||
227 | continue | ||
228 | already_logged_in = False | ||
229 | for pid, pl in players.items(): | ||
230 | if players[pid].get("name") == command: | ||
231 | mud.send_message(id, "{} is already logged in.".format(command)) | ||
232 | mud.disconnect_player(id) | ||
233 | continue | ||
234 | loaded_player = load_object_from_file("players/{}.json".format(command)) | ||
235 | mud.remote_echo(id, False) | ||
236 | players[id]["name"] = command | ||
237 | if not loaded_player: | ||
238 | # Player does not exist. | ||
239 | mud.send_message(id, "Character does not exist. Please enter a password to create a new character: ") | ||
240 | else: | ||
241 | mud.send_message(id, "Enter Password: ") | ||
242 | |||
243 | elif players[id]["password"] is None: | ||
244 | if players[id].get("retries", 0) > 1: | ||
245 | mud.send_message(id, "Too many attempts") | ||
246 | mud.disconnect_player(id) | ||
247 | continue | ||
248 | |||
249 | if command.strip() == '' or command is None: | ||
250 | players[id]["retries"] = players[id].get("retries", 0) + 1 | ||
251 | mud.send_message(id, "Invalid Password") | ||
252 | continue | ||
253 | |||
254 | loaded_player = load_object_from_file("players/{}.json".format(players[pid]["name"])) | ||
255 | if loaded_player is None: | ||
256 | players[id]["password"] = password_hash(players[pid]["name"], command) | ||
257 | players[id]["room"] = "town/tavern" | ||
258 | else: | ||
259 | if loaded_player["password"] == password_hash(players[pid]["name"], command): | ||
260 | players[id] = loaded_player | ||
261 | else: | ||
262 | players[id]["retries"] = players[id].get("retries", 0) + 1 | ||
263 | mud.send_message(id, "Invalid Password") | ||
264 | continue | ||
265 | |||
266 | # go through all the players in the game | ||
267 | for pid, pl in players.items(): | ||
268 | # send each player a message to tell them about the new player | ||
269 | mud.send_message(pid, "{} entered the game".format(players[id]["name"])) | ||
270 | |||
271 | mud.remote_echo(id, True) | ||
272 | # send the new player a welcome message | ||
273 | mud.send_message(id, "\r\n\r\nWelcome to the game, {}. ".format(players[id]["name"]) + | ||
274 | "\r\nType 'help' for a list of commands. Have fun!\r\n\r\n") | ||
275 | |||
276 | # send the new player the description of their current room | ||
277 | |||
278 | cmd_handler.parse(id, 'look', '', mud, players) | ||
279 | show_prompt(id) | ||
280 | else: | ||
281 | if 'esp' in platform: | ||
282 | collect() | ||
283 | |||
284 | cmd_handler.parse(id, command, params, mud, players) | ||
285 | show_prompt(id) | ||
286 | |||
287 | # Start WIFI Setup | ||
288 | if 'esp' in platform: | ||
289 | if flash_button.value() == 0: | ||
290 | print('Starting WIFIWeb') | ||
291 | import wifiweb | ||
... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
... | @@ -12,9 +12,13 @@ import socket | ... | @@ -12,9 +12,13 @@ import socket |
12 | import select | 12 | import select |
13 | import time | 13 | import time |
14 | import sys | 14 | import sys |
15 | import struct | ||
16 | import json | 15 | import json |
17 | 16 | ||
17 | if 'esp' in sys.platform: | ||
18 | import ustruct | ||
19 | else: | ||
20 | import struct | ||
21 | |||
18 | from utils import get_color, get_color_list, multiple_replace | 22 | from utils import get_color, get_color_list, multiple_replace |
19 | 23 | ||
20 | 24 | ||
... | @@ -426,7 +430,7 @@ class MudServer(object): | ... | @@ -426,7 +430,7 @@ class MudServer(object): |
426 | 430 | ||
427 | try: | 431 | try: |
428 | # read data from the socket, using a max length of 4096 | 432 | # read data from the socket, using a max length of 4096 |
429 | data = cl.socket.recv(4096) | 433 | data = cl.socket.recv(1024) |
430 | 434 | ||
431 | # process the data, stripping out any special Telnet commands | 435 | # process the data, stripping out any special Telnet commands |
432 | message = self._process_sent_data(cl, data) | 436 | message = self._process_sent_data(cl, data) |
... | @@ -640,7 +644,10 @@ class MudServer(object): | ... | @@ -640,7 +644,10 @@ class MudServer(object): |
640 | if option_state == self._READ_NAWS: | 644 | if option_state == self._READ_NAWS: |
641 | height = 30 | 645 | height = 30 |
642 | width = 100 | 646 | width = 100 |
643 | height, width = struct.unpack('>hh', option_data) | 647 | if 'esp' in sys.platform: |
648 | height, width = ustruct.unpack('>hh', option_data) | ||
649 | else: | ||
650 | height, width = struct.unpack('>hh', option_data) | ||
644 | if height > 0: | 651 | if height > 0: |
645 | client.height = height | 652 | client.height = height |
646 | if width > 0: | 653 | if width > 0: | ... | ... |
... | @@ -11,15 +11,12 @@ IPADDRESS = '192.168.1.189' | ... | @@ -11,15 +11,12 @@ IPADDRESS = '192.168.1.189' |
11 | 11 | ||
12 | BAUDRATE = 115200 | 12 | BAUDRATE = 115200 |
13 | 13 | ||
14 | folders = ['help', 'rooms', 'inventory', 'commands', 'mobs'] | 14 | folders = ['help', 'rooms', 'rooms/town', 'rooms/wilderness', 'inventory', 'commands', 'mobs'] |
15 | 15 | ||
16 | files = [ | 16 | files = [ |
17 | "main.py", | ||
18 | "mobs.txt", | ||
19 | "spawner.txt", | ||
20 | "welcome.txt", | 17 | "welcome.txt", |
21 | "wifiweb.py", | 18 | "defaultplayer.json", |
22 | "defaultplayer.json" | 19 | "main.py" |
23 | ] | 20 | ] |
24 | 21 | ||
25 | def run_command(sio, command, expected='>>>'): | 22 | def run_command(sio, command, expected='>>>'): |
... | @@ -27,18 +24,21 @@ def run_command(sio, command, expected='>>>'): | ... | @@ -27,18 +24,21 @@ def run_command(sio, command, expected='>>>'): |
27 | sio.flush() # it is buffering. required to get the data out *now* | 24 | sio.flush() # it is buffering. required to get the data out *now* |
28 | res = '' | 25 | res = '' |
29 | while expected not in res: | 26 | while expected not in res: |
30 | res = sio.readline() | 27 | try: |
31 | # print(res) | 28 | res = sio.readline() |
29 | except UnicodeDecodeError: | ||
30 | return '' | ||
32 | return res | 31 | return res |
33 | 32 | ||
34 | with serial.Serial(PORT, BAUDRATE, timeout=1) as ser: | 33 | with serial.Serial(PORT, BAUDRATE, timeout=1) as ser: |
35 | sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser)) | 34 | sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser)) |
36 | run_command(sio, '\x03') | 35 | run_command(sio, '\x03') |
37 | run_command(sio, 'import os') | 36 | run_command(sio, 'import os') |
38 | root = eval(run_command(sio, 'os.listdir()', expected=']')) | 37 | root = run_command(sio, 'os.listdir()', expected=']') |
39 | |||
40 | if not set(folders).issubset(root): | ||
41 | 38 | ||
39 | if 'EEXIST' in root: | ||
40 | print('Folders already created.') | ||
41 | else: | ||
42 | print('Creating folders.') | 42 | print('Creating folders.') |
43 | # we are missing folders so they need to be created. | 43 | # we are missing folders so they need to be created. |
44 | tmp_folders = folders | 44 | tmp_folders = folders |
... | @@ -48,18 +48,19 @@ with serial.Serial(PORT, BAUDRATE, timeout=1) as ser: | ... | @@ -48,18 +48,19 @@ with serial.Serial(PORT, BAUDRATE, timeout=1) as ser: |
48 | continue | 48 | continue |
49 | print('Creating folder: {}'.format(folder)) | 49 | print('Creating folder: {}'.format(folder)) |
50 | run_command(sio, 'os.mkdir("{}")'.format(folder)) | 50 | run_command(sio, 'os.mkdir("{}")'.format(folder)) |
51 | else: | ||
52 | print('Folders already created.') | ||
53 | 51 | ||
54 | 52 | ||
55 | for folder in folders: | 53 | for folder in folders: |
56 | for f in os.listdir(folder): | 54 | for f in os.listdir(folder): |
57 | files.append('{}/{}'.format(folder, f)) | 55 | if not os.path.isdir(f): |
56 | files.append('{}/{}'.format(folder, f)) | ||
58 | 57 | ||
59 | with open('releasepw.conf', 'r', encoding='utf-8') as f: | 58 | with open('releasepw.conf', 'r', encoding='utf-8') as f: |
60 | password = f.read() | 59 | password = f.read() |
61 | 60 | ||
62 | for file in files: | 61 | for file in files: |
63 | os.system("python webrepl\webrepl_cli.py -p {} {} {}:/{}".format(password, file, IPADDRESS, file)) | 62 | # print("python webrepl\\webrepl_cli.py -p {} {} {}:/{}".format(password, file, IPADDRESS, file)) |
63 | os.system("python webrepl\\webrepl_cli.py -p {} {} {}:/{}".format(password, file, IPADDRESS, file)) | ||
64 | 64 | ||
65 | run_command(sio, 'import machine;machine.reset') | 65 | print("Rebooting via machine reset") |
66 | run_command(sio, 'import machine;machine.reset()') | ... | ... |
release_image.py
0 → 100644
1 | import os | ||
2 | import io | ||
3 | import serial | ||
4 | import time | ||
5 | |||
6 | ###################################### | ||
7 | # EDIT THIS TO MATCH YOUR SETTINGS | ||
8 | PORT = 'COM17' | ||
9 | VMID = '1e5fc8c' | ||
10 | ESSID = 'Volley' | ||
11 | WIFI_PASSWORD = '6198472223' | ||
12 | ###################################### | ||
13 | |||
14 | BAUDRATE = 115200 | ||
15 | |||
16 | baked_files = [ | ||
17 | "weemud.py", | ||
18 | "mudserver.py", | ||
19 | "commandhandler.py", | ||
20 | "wifiweb.py", | ||
21 | "utils.py" | ||
22 | ] | ||
23 | |||
24 | |||
25 | def run_command(sio, command, expected='>>>'): | ||
26 | sio.write("{}\n".format(command)) | ||
27 | sio.flush() # it is buffering. required to get the data out *now* | ||
28 | res = '' | ||
29 | while expected not in res: | ||
30 | try: | ||
31 | res = sio.readline() | ||
32 | except UnicodeDecodeError: | ||
33 | return '' | ||
34 | return res | ||
35 | |||
36 | # Compile vagrant image | ||
37 | for file in baked_files: | ||
38 | os.system("vagrant scp {} {}:micropython/ports/esp8266/modules/{}".format(file, VMID, file)) | ||
39 | os.system('vagrant ssh {} -c "cd micropython/ports/esp8266;make"'.format(VMID)) | ||
40 | try: | ||
41 | os.mkdir('image') | ||
42 | except FileExistsError: | ||
43 | pass | ||
44 | os.system('vagrant scp {}:micropython/ports/esp8266/build/firmware-combined.bin image/firmware-combined.bin'.format(VMID)) | ||
45 | |||
46 | os.system('esptool --port {} erase_flash'.format(PORT)) | ||
47 | os.system('esptool --port {} --baud 460800 write_flash --flash_size=detect 0 image/firmware-combined.bin'.format(PORT)) | ||
48 | |||
49 | print("Sleeping 10 seconds for reboot") | ||
50 | time.sleep(10) | ||
51 | |||
52 | with open('releasepw.conf', 'r', encoding='utf-8') as f: | ||
53 | WEBREPL_PASS = f.read() | ||
54 | |||
55 | with serial.Serial(PORT, BAUDRATE, timeout=1) as ser: | ||
56 | sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser)) | ||
57 | run_command(sio, '\x03') | ||
58 | run_command(sio, 'import os') | ||
59 | print('Setting up Webrepl') | ||
60 | run_command(sio, "f = open('webrepl_cfg.py', 'w+').write(\"PASS = '{}'\\n\")".format(WEBREPL_PASS)) | ||
61 | run_command(sio, 'f = open("boot.py", "w+").write("# This file is executed on every boot (including wake-boot from deepsleep)\\n#import esp\\n#esp.osdebug(None)\\nimport gc\\nimport webrepl\\nwebrepl.start()\\ngc.collect()\\n")') | ||
62 | |||
63 | print('Setting up Wifi Network') | ||
64 | run_command(sio, 'import network') | ||
65 | run_command(sio, 'sta_if = network.WLAN(network.STA_IF)') | ||
66 | run_command(sio, 'ap_if = network.WLAN(network.AP_IF)') | ||
67 | |||
68 | run_command(sio, 'sta_if.active(True)') | ||
69 | |||
70 | print('Connecting to {}'.format(ESSID)) | ||
71 | run_command(sio, "sta_if.connect('{}', '{}')".format(ESSID, WIFI_PASSWORD)) | ||
72 | |||
73 | waiting_for_ip = True | ||
74 | while waiting_for_ip: | ||
75 | try: | ||
76 | ifconfig = eval(run_command(sio, "sta_if.ifconfig()", expected='(')) | ||
77 | |||
78 | if ifconfig[0] != '0.0.0.0': | ||
79 | print("New IP Address: {}".format(ifconfig[0])) | ||
80 | waiting_for_ip = False | ||
81 | except SyntaxError: | ||
82 | pass | ||
83 | except NameError: | ||
84 | pass | ||
85 | |||
86 | print("Rebooting via machine reset") | ||
87 | |||
88 | run_command(sio, 'import machine;machine.reset()') | ||
89 | |||
90 | print('Starting the squishy mud release') | ||
91 | time.sleep(5) | ||
92 | # Run the rest of the mud setup | ||
93 | import release | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
1 | import json | 1 | import json |
2 | import sys | 2 | import sys |
3 | import re | ||
4 | 3 | ||
5 | if 'esp' in sys.platform: | 4 | if 'esp' in sys.platform: |
6 | from urandom import getrandbits | 5 | from urandom import getrandbits |
... | @@ -12,7 +11,7 @@ codes = {'resetall': 0, 'bold': 1, 'underline': 4, | ... | @@ -12,7 +11,7 @@ codes = {'resetall': 0, 'bold': 1, 'underline': 4, |
12 | 'blinkoff': 25, 'underlineoff': 24, 'reverseoff': 27, | 11 | 'blinkoff': 25, 'underlineoff': 24, 'reverseoff': 27, |
13 | 'reset': 0, 'black': 30, 'red': 31, 'green': 32, | 12 | 'reset': 0, 'black': 30, 'red': 31, 'green': 32, |
14 | 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, | 13 | 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, |
15 | 'white': 37} | 14 | 'white': 37 } |
16 | 15 | ||
17 | 16 | ||
18 | def password_hash(name, password): | 17 | def password_hash(name, password): |
... | @@ -37,13 +36,10 @@ def get_color(name): | ... | @@ -37,13 +36,10 @@ def get_color(name): |
37 | return '\x1b[{}m'.format(codes.get(name, 0)) | 36 | return '\x1b[{}m'.format(codes.get(name, 0)) |
38 | 37 | ||
39 | 38 | ||
40 | def multiple_replace(text, adict): | 39 | def multiple_replace(text, replace_words): |
41 | rx = re.compile('|'.join(map(re.escape, adict))) | 40 | for word, replacement in replace_words.items(): |
42 | 41 | text = text.replace(word, get_color(replacement)) | |
43 | def one_xlat(match): | 42 | return text |
44 | return get_color(adict[match.group(0)]) | ||
45 | return rx.sub(one_xlat, text) | ||
46 | |||
47 | 43 | ||
48 | def save_object_to_file(obj, filename): | 44 | def save_object_to_file(obj, filename): |
49 | with open(filename.lower(), 'w', encoding='utf-8') as f: | 45 | with open(filename.lower(), 'w', encoding='utf-8') as f: | ... | ... |
weemud.py
0 → 100644
1 | #!/usr/bin/env python | ||
2 | |||
3 | """ | ||
4 | MudServer author: Mark Frimston - mfrimston@gmail.com | ||
5 | |||
6 | Micropython port and expansion author: Barry Ruffner - barryruffner@gmail.com | ||
7 | """ | ||
8 | |||
9 | from time import sleep | ||
10 | from math import floor | ||
11 | from sys import platform | ||
12 | from os import listdir | ||
13 | |||
14 | from mudserver import MudServer | ||
15 | from commandhandler import CommandHandler | ||
16 | from utils import load_object_from_file, save_object_to_file, password_hash, calc_att, get_att | ||
17 | |||
18 | if 'esp' in platform: | ||
19 | from gc import collect, mem_free | ||
20 | from machine import Pin | ||
21 | # import the MUD server class | ||
22 | |||
23 | |||
24 | print('STARTING MUD\r\n\r\n\r\n') | ||
25 | |||
26 | # Setup button so when pressed the mud goes in to wifi hotspot mode on 192.168.4.1 | ||
27 | if 'esp' in platform: | ||
28 | flash_button = Pin(0, Pin.IN, Pin.PULL_UP) | ||
29 | |||
30 | # stores the players in the game | ||
31 | players = {} | ||
32 | |||
33 | # start the server | ||
34 | globals()['mud'] = MudServer() | ||
35 | |||
36 | |||
37 | def show_prompt(pid): | ||
38 | if "prompt" not in players[pid]: | ||
39 | players[pid]["prompt"] = "> " | ||
40 | if 'hp' not in players[pid]: | ||
41 | players[pid]["hp"] = 100 | ||
42 | if 'mp' not in players[pid]: | ||
43 | players[pid]["mp"] = 100 | ||
44 | if 'sta' not in players[pid]: | ||
45 | players[pid]["sta"] = 10 | ||
46 | |||
47 | prompt = players[pid]["prompt"].replace('%st', str(floor(players[pid]['sta']))).replace('%hp', str(floor(players[pid]["hp"]))).replace('%mp', str(floor(players[pid]["mp"]))) | ||
48 | mud.send_message(pid, "\r\n" + prompt, '') | ||
49 | |||
50 | tick = 0.0 | ||
51 | spawn = 0.0 | ||
52 | cmd_handler = CommandHandler() | ||
53 | |||
54 | def spawn_mobs(players): | ||
55 | rooms = listdir('rooms') | ||
56 | for room in rooms: | ||
57 | if '_monsters.json' not in room: | ||
58 | continue | ||
59 | room_monsters = load_object_from_file('rooms/{}'.format(room)) | ||
60 | for mon_name, monster in room_monsters.items(): | ||
61 | monster_template = load_object_from_file('mobs/{}.json'.format(mon_name)) | ||
62 | if not monster_template: | ||
63 | continue | ||
64 | while len(room_monsters[mon_name]['active']) < monster['max']: | ||
65 | print('Spawning {} in {}'.format(mon_name, room)) | ||
66 | mp = get_att(monster_template['spawn']["mp"]) | ||
67 | hp = get_att(monster_template['spawn']["hp"]) | ||
68 | sta = get_att(monster_template['spawn']["sta"]) | ||
69 | new_active = { | ||
70 | "mp": mp, | ||
71 | "maxhp": hp, | ||
72 | "hp": hp, | ||
73 | "sta": sta, | ||
74 | "maxmp": mp, | ||
75 | "target": "", | ||
76 | "action": "attack", | ||
77 | "maxsta": sta} | ||
78 | room_monsters[mon_name]['active'].append(new_active) | ||
79 | for pid, pl in players.items(): | ||
80 | if players[pid]['room'].lower() == room.split('_')[0]: | ||
81 | mud.send_message(pid, "a {} arrived".format(mon_name)) | ||
82 | save_object_to_file(room_monsters, 'rooms/{}'.format(room)) | ||
83 | |||
84 | def run_mobs(players, mud): | ||
85 | for pid, player in players.items(): | ||
86 | if not player or not player.get("name") or not player.get('password'): | ||
87 | continue | ||
88 | if player['mp'] < player['maxmp']: | ||
89 | players[pid]['mp'] += player['mpr'] | ||
90 | if player['sta'] < player['maxsta']: | ||
91 | players[pid]['sta'] += player['star'] | ||
92 | room_monsters = load_object_from_file('rooms/{}_monsters.json'.format(player['room'])) | ||
93 | for mon_name, monster in room_monsters.items(): | ||
94 | monster_template = load_object_from_file('mobs/{}.json'.format(mon_name)) | ||
95 | for active_monster_idx, active_monster in enumerate(monster['active']): | ||
96 | sta = active_monster['sta'] | ||
97 | if active_monster['mp'] < active_monster['maxmp']: | ||
98 | active_monster['mp'] += monster_template['mpr'] | ||
99 | if sta < active_monster['maxsta']: | ||
100 | sta += monster_template['star'] | ||
101 | active_monster['sta'] = sta | ||
102 | if active_monster['action'] == "attack" and active_monster['target'] == player['name']: | ||
103 | if player.get("weapon"): | ||
104 | weapon = load_object_from_file('inventory/{}.json'.format(player['weapon'])) | ||
105 | att = get_att(weapon['damage']) | ||
106 | mud.send_message(pid, "Your %s strikes the %s for %d" % (weapon['title'], mon_name, att,), color='yellow') | ||
107 | else: | ||
108 | att = get_att(player['aa']) | ||
109 | mud.send_message(pid, "You hit the %s for %d" % (mon_name, att,), color='yellow') | ||
110 | active_monster['hp'] -= att | ||
111 | if active_monster['hp'] <= 0: | ||
112 | del room_monsters[mon_name]['active'][active_monster_idx] | ||
113 | mud.send_message(pid, "The %s dies." % (mon_name,), color=['bold', 'blue']) | ||
114 | break | ||
115 | if active_monster.get("weapon"): | ||
116 | weapon = load_object_from_file('inventory/{}.json'.format(active_monster['weapon'])) | ||
117 | att = get_att(weapon['damage']) | ||
118 | mud.send_message(pid, "The %s strikes you with a %s for %d" % (mon_name, weapon['title'], att,), color='magenta') | ||
119 | else: | ||
120 | att = get_att(monster_template['aa']) | ||
121 | mud.send_message(pid, "You were hit by a %s for %d" % (mon_name, att,), color='magenta') | ||
122 | players[pid]['hp'] -= att | ||
123 | if (active_monster['hp']/active_monster['maxhp'] < 0.25) or (active_monster['mp'] > 0 and active_monster['mp']/active_monster['maxmp'] > 0.5) or (sta > 0 and sta/active_monster['maxsta'] > 0.5): | ||
124 | magic_cast = False | ||
125 | if active_monster['mp'] > 0: | ||
126 | att, active_monster['mp'] = calc_att(mud, pid, monster_template['sp'], active_monster['mp']) | ||
127 | players[pid]['hp'] -= att | ||
128 | if att > 0: | ||
129 | magic_cast = True | ||
130 | if not magic_cast: | ||
131 | if sta > 0: | ||
132 | att, sta = calc_att(mud, pid, monster_template['at'], sta) | ||
133 | active_monster['sta'] = sta | ||
134 | players[pid]['hp'] -= att | ||
135 | |||
136 | room_monsters[mon_name]['active'][active_monster_idx] = active_monster | ||
137 | |||
138 | save_object_to_file(room_monsters, 'rooms/{}_monsters.json'.format(player['room'])) | ||
139 | |||
140 | def isalnum(c): | ||
141 | for letter in c: | ||
142 | if not letter.isalpha() and not letter.isdigit(): | ||
143 | return False | ||
144 | return True | ||
145 | |||
146 | # main game loop. We loop forever (i.e. until the program is terminated) | ||
147 | while True: | ||
148 | if 'esp' in platform: | ||
149 | collect() | ||
150 | if flash_button.value() == 0: | ||
151 | break | ||
152 | # pause for 1/5 of a second on each loop, so that we don't constantly | ||
153 | sleep(0.002) | ||
154 | if 'esp' in platform: | ||
155 | tick += 0.001 | ||
156 | else: | ||
157 | tick += 0.0005 | ||
158 | if spawn >= 10: | ||
159 | spawn = 0 | ||
160 | try: | ||
161 | spawn_mobs(players) | ||
162 | except Exception as e: | ||
163 | print('spawner error:') | ||
164 | print(e) | ||
165 | if 'esp' in platform: | ||
166 | collect() | ||
167 | if tick >= 1: | ||
168 | if 'esp' in platform: | ||
169 | print(mem_free()) | ||
170 | spawn += tick | ||
171 | tick = 0 | ||
172 | try: | ||
173 | run_mobs(players, mud) | ||
174 | except Exception as e: | ||
175 | print('mob error:') | ||
176 | print(e) | ||
177 | # 'update' must be called in the loop to keep the game running and give | ||
178 | # us up-to-date information | ||
179 | mud.update() | ||
180 | |||
181 | # go through any newly connected players | ||
182 | for id in mud.get_new_players(): | ||
183 | |||
184 | # add the new player to the dictionary, noting that they've not been | ||
185 | # named yet. | ||
186 | # The dictionary key is the player's id number. We set their room to | ||
187 | # None initially until they have entered a name | ||
188 | # Try adding more player stats - level, gold, inventory, etc | ||
189 | players[id] = load_object_from_file("defaultplayer.json") | ||
190 | with open('welcome.txt', 'r', encoding='utf-8') as f: | ||
191 | for line in f: | ||
192 | mud.send_message(id, line, "\r") | ||
193 | # send the new player a prompt for their name | ||
194 | #bytes_to_send = bytearray([mud._TN_INTERPRET_AS_COMMAND, mud._TN_WONT, mud._ECHO]) | ||
195 | #mud.raw_send(id, bytes_to_send) | ||
196 | mud.send_message(id, "What is your name?") | ||
197 | |||
198 | # go through any recently disconnected players | ||
199 | for id in mud.get_disconnected_players(): | ||
200 | |||
201 | # if for any reason the player isn't in the player map, skip them and | ||
202 | # move on to the next one | ||
203 | if id not in players: | ||
204 | continue | ||
205 | |||
206 | # go through all the players in the game | ||
207 | for pid, pl in players.items(): | ||
208 | # send each player a message to tell them about the diconnected | ||
209 | # player | ||
210 | if players[pid].get("name") is not None: | ||
211 | mud.send_message(pid, "{} quit the game".format(players[pid]["name"])) | ||
212 | |||
213 | # remove the player's entry in the player dictionary | ||
214 | if players[id]["name"] is not None: | ||
215 | save_object_to_file(players[id], "players/{}.json".format(players[id]["name"])) | ||
216 | del(players[id]) | ||
217 | |||
218 | # go through any new commands sent from players | ||
219 | for id, command, params in mud.get_commands(): | ||
220 | |||
221 | # if for any reason the player isn't in the player map, skip them and | ||
222 | # move on to the next one | ||
223 | if id not in players: | ||
224 | continue | ||
225 | |||
226 | # if the player hasn't given their name yet, use this first command as | ||
227 | # their name and move them to the starting room. | ||
228 | if players[id].get("name") is None: | ||
229 | if command.strip() == '' or len(command) > 12 or not isalnum(command) or command is None: | ||
230 | mud.send_message(id, "Invalid Name") | ||
231 | print("Bad guy!") | ||
232 | print(mud.get_remote_ip(id)) | ||
233 | continue | ||
234 | already_logged_in = False | ||
235 | for pid, pl in players.items(): | ||
236 | if players[pid].get("name") == command: | ||
237 | mud.send_message(id, "{} is already logged in.".format(command)) | ||
238 | mud.disconnect_player(id) | ||
239 | continue | ||
240 | loaded_player = load_object_from_file("players/{}.json".format(command)) | ||
241 | mud.remote_echo(id, False) | ||
242 | players[id]["name"] = command | ||
243 | if not loaded_player: | ||
244 | # Player does not exist. | ||
245 | mud.send_message(id, "Character does not exist. Please enter a password to create a new character: ") | ||
246 | else: | ||
247 | mud.send_message(id, "Enter Password: ") | ||
248 | |||
249 | elif players[id]["password"] is None: | ||
250 | if players[id].get("retries", 0) > 1: | ||
251 | mud.send_message(id, "Too many attempts") | ||
252 | mud.disconnect_player(id) | ||
253 | continue | ||
254 | |||
255 | if command.strip() == '' or command is None: | ||
256 | players[id]["retries"] = players[id].get("retries", 0) + 1 | ||
257 | mud.send_message(id, "Invalid Password") | ||
258 | continue | ||
259 | |||
260 | loaded_player = load_object_from_file("players/{}.json".format(players[pid]["name"])) | ||
261 | if loaded_player is None: | ||
262 | players[id]["password"] = password_hash(players[pid]["name"], command) | ||
263 | players[id]["room"] = "town/tavern" | ||
264 | else: | ||
265 | if loaded_player["password"] == password_hash(players[pid]["name"], command): | ||
266 | players[id] = loaded_player | ||
267 | else: | ||
268 | players[id]["retries"] = players[id].get("retries", 0) + 1 | ||
269 | mud.send_message(id, "Invalid Password") | ||
270 | continue | ||
271 | |||
272 | # go through all the players in the game | ||
273 | for pid, pl in players.items(): | ||
274 | # send each player a message to tell them about the new player | ||
275 | mud.send_message(pid, "{} entered the game".format(players[id]["name"])) | ||
276 | |||
277 | mud.remote_echo(id, True) | ||
278 | # send the new player a welcome message | ||
279 | mud.send_message(id, "\r\n\r\nWelcome to the game, {}. ".format(players[id]["name"]) + | ||
280 | "\r\nType 'help' for a list of commands. Have fun!\r\n\r\n") | ||
281 | |||
282 | # send the new player the description of their current room | ||
283 | |||
284 | cmd_handler.parse(id, 'look', '', mud, players) | ||
285 | show_prompt(id) | ||
286 | else: | ||
287 | if 'esp' in platform: | ||
288 | collect() | ||
289 | |||
290 | cmd_handler.parse(id, command, params, mud, players) | ||
291 | show_prompt(id) | ||
292 | |||
293 | # Start WIFI Setup | ||
294 | if 'esp' in platform: | ||
295 | if flash_button.value() == 0: | ||
296 | print('Starting WIFIWeb') | ||
297 | import wifiweb |
-
Please register or sign in to post a comment