Advertisement
Guest User

Untitled

a guest
Aug 11th, 2016
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.60 KB | None | 0 0
  1. #!/usr/bin/env python
  2. import socket
  3. import thread
  4. import logging
  5. import time
  6. import random
  7.  
  8. HOST = ""
  9. PORT = 4004
  10.  
  11. TIME_AFK = 300
  12. TIME_KICK = 600
  13.  
  14. BANNER = """
  15. _____ _ __ __ _ _ ____
  16. |_ _(_)_ __ _ _| \/ | | | | _ \
  17. | | | | '_ \| | | | |\/| | | | | | | |
  18. | | | | | | | |_| | | | | |_| | |_| |
  19. |_| |_|_| |_|\__, |_| |_|\___/|____/
  20. |___/
  21.  
  22. Welcome to TinyMUD!
  23. Please enter your name to login, or `new' to create a new character.
  24. """
  25.  
  26. STARTING_ROOM = 0
  27.  
  28. not_allowed = ["new", "quit", "admin", "wizard", "god", "creator"]
  29. accounts = {
  30. "admin": {"character": 1, "password": "admin", "tags": ["wizard"], "last_seen": 0},
  31. "test": {"character": None, "password": "test", "tags": [], "last_seen": 0}
  32. }
  33. db = {
  34. 0: {
  35. "kind": "room",
  36. "name": "Limbo",
  37. "description": "You are floating in a featureless grey limbo.",
  38. "exits": [3, None, None, None],
  39. "items": []
  40. },
  41. 1: {
  42. "kind": "mob",
  43. "name": "One",
  44. "description": "A genderless humanoid, tall and gangly and very, very real.",
  45. "position": 0,
  46. "wield": None,
  47. "wears": None,
  48. "str": 10,
  49. "hp": 10,
  50. "max_hp": 10
  51. },
  52. 2: {
  53. "kind": "item",
  54. "name": "a red apple",
  55. "description": "It's big, red, and very, very juicy.",
  56. "position": 3,
  57. "type": "food",
  58. "hunger": -10
  59. },
  60. 3: {
  61. "kind": "room",
  62. "name": "Hillfort Plaza",
  63. "description": "The hustle and bustle of the city of Hillfort surrounds you on all sides.",
  64. "exits": [None, 0, None, None],
  65. "items": [2, 4, 5]
  66. },
  67. 4: {
  68. "kind": "item",
  69. "name": "a short sword",
  70. "description": "About two feet long and quite sharp.",
  71. "position": 3,
  72. "type": "weapon"
  73. },
  74. 5: {
  75. "kind": "mob",
  76. "name": "a city guard",
  77. "description": "He's tall, broad and dressed in chainmail.",
  78. "position": 3,
  79. "wields": None,
  80. "wears": 6,
  81. "str": 10,
  82. "hp": 10,
  83. "max_hp": 10,
  84. "items": [6]
  85. },
  86. 6: {
  87. "kind": "item",
  88. "name": "a chainmail vest",
  89. "description": "It gets down to your knees and it's pretty heavy.",
  90. "position": 5,
  91. "type": "armor"
  92. },
  93. }
  94. FREE_IDS = []
  95. ID = 6
  96.  
  97. def get_id():
  98. global ID
  99. if len(FREE_IDS) > 0:
  100. return FREE_IDS.pop()
  101. ID += 1
  102. return ID
  103.  
  104. def destroy(id):
  105. if id in db:
  106. FREE_IDS.append(id)
  107. del db[id]
  108. return True
  109. return False
  110.  
  111. Directions = [
  112. ("north", "n"),
  113. ("south", "s"),
  114. ("east", "e"),
  115. ("west", "w")
  116. ]
  117. Opposite = [1, 0, 3, 2]
  118.  
  119. def has_tag(name, tag):
  120. return tag in accounts[name]["tags"]
  121.  
  122. def add_tag(name, tag):
  123. if not has_tag(name, tag):
  124. accounts[name]["tags"].append(tag)
  125.  
  126. def remove_tag(name, tag):
  127. if has_tag(name, tag):
  128. accounts[name]["tags"].remove(tag)
  129.  
  130. ###
  131.  
  132. def is_wizard(name):
  133. return has_tag(name, "wizard")
  134.  
  135. def get_user_room(name):
  136. if name in accounts:
  137. return db.get(accounts[name]["character"], {}).get("position", None)
  138.  
  139. def in_room(room):
  140. #return db[room].get("items", [])
  141. return [item for item in sorted(db)
  142. if db[item].get("position", None) == room]
  143.  
  144. def in_room_except(room, excluded):
  145. #return filter(lambda item: item != excluded, in_room(room))
  146. return [item for item in sorted(db)
  147. if db[item].get("position", None) == room
  148. if item != excluded]
  149. ###
  150.  
  151. def prompt(client, prompt=">"):
  152. client.send(prompt)
  153. try:
  154. return client.recv(1024).strip()
  155. except socket.error:
  156. pass#continue
  157.  
  158. def accept(conn):
  159. """
  160. Call the inner func in a thread so as not to block. Wait for a
  161. name to be entered from the given connection. Once a name is
  162. entered, set the connection to non-blocking and add the user to
  163. the users dict.
  164. """
  165. def threaded():
  166. for line in BANNER.splitlines():
  167. conn.send(line + "\n")
  168. state = "username"
  169. name, password = None, None
  170. while True:
  171. if state == "username":
  172. name = prompt(conn, "Username: ")
  173. if name is None:
  174. pass
  175. elif name == "new":
  176. state = "new_username"
  177. elif name in accounts:
  178. state = "password"
  179. else:
  180. conn.send("That character does not exists. Type 'new' to create a new character.\n")
  181. elif state == "password":
  182. password = prompt(conn, "Password: ")
  183. if password is None:
  184. pass
  185. elif password == accounts[name]["password"]:
  186. state = "login"
  187. else:
  188. conn.send("Wrong username or password.\n")
  189. state = "username"
  190. elif state == "new_username":
  191. conn.send("Creating a new character.\n")
  192. name = prompt(conn, "Username: ")
  193. if name is None:
  194. pass
  195. elif name in not_allowed:
  196. conn.send("%s is not allowed. Choose another name.\n" % name)
  197. elif name in accounts:
  198. conn.send("%s is taken. Choose another name.\n" % name)
  199. else:
  200. state = "new_password"
  201. elif state == "new_password":
  202. password = prompt(conn, "Password: ")
  203. if password is None:
  204. pass
  205. else:
  206. state = "new_password_repeat"
  207. elif state == "new_password_repeat":
  208. password_r = prompt(conn, "Repeat password: ")
  209. if password_r is None:
  210. pass
  211. elif password_r != password:
  212. conn.send("The passwords are different.\n")
  213. state = "username"
  214. else:
  215. accounts[name] = {
  216. "character": None,
  217. "password": password,
  218. "tags": [],
  219. "last_seen": time.time()
  220. }
  221. state = "login"
  222. elif state == "login":
  223. conn.setblocking(False)
  224. users[name] = conn
  225. conn.send("You are logged in as %s.\n" % name)
  226. accounts[name]["last_seen"] = time.time()
  227. #conn.send("> ")
  228. do_look(name, None)
  229. broadcast(name, "+++ %s arrives +++" % name)
  230. break
  231. thread.start_new_thread(threaded, ())
  232.  
  233. def broadcast_room(name, message):
  234. """
  235. Send a message to all users in the same room from the given name.
  236. """
  237. print(message)
  238. start_room = get_user_room(name)
  239. for to_name, conn in users.items():
  240. room = get_user_room(to_name)
  241. if name is None or (to_name != name) and (start_room == room):
  242. try:
  243. conn.send(message + "\n")
  244. except socket.error:
  245. pass
  246.  
  247. def broadcast(name, message):
  248. """
  249. Send a message to all users from the given name.
  250. """
  251. print(message)
  252. for to_name, conn in users.items():
  253. if name is None or (to_name != name):
  254. try:
  255. conn.send(message + "\n")
  256. except socket.error:
  257. pass
  258.  
  259. def send(name, message):
  260. """
  261. Sends a message to the given user.
  262. """
  263. if name in users:
  264. users[name].send(message + "\n")
  265.  
  266. Commands = {}
  267. OneLetter = {}
  268.  
  269. def command(*aliases, **kwargs):
  270. wizard = kwargs.get("wizard", False)
  271. def wrap(function):
  272. for alias in aliases:
  273. if len(alias) == 1 and not alias.isalpha():
  274. OneLetter[alias] = [function, wizard]
  275. else:
  276. Commands[alias] = [function, wizard]
  277. return function
  278. return wrap
  279.  
  280. def parse_command(name, line):
  281. accounts[name]["last_seen"] = time.time()
  282. if len(line) > 0 and line[0] in OneLetter:
  283. if is_wizard(name) or not OneLetter[line[0]][1]:
  284. return OneLetter[line[0]][0](name, line[1:].strip())
  285. command, _, params = line.strip().partition(" ")
  286. command = command.lower()
  287. for i, item in enumerate(Directions):
  288. if command in item:
  289. return go_direction(name, i)
  290. if command in Commands:
  291. if is_wizard(name) or not Commands[command][1]:
  292. return Commands[command][0](name, params)
  293. send(name, "Uh?")
  294.  
  295. def go_direction(name, direction):
  296. character = accounts[name]["character"]
  297. if character is not None:
  298. room = db[character]["position"]
  299. if db[room]["exits"][direction] is not None:
  300. broadcast_room(name, "%s walks %s." % (db[character]["name"], Directions[direction][0]))
  301. db[character]["position"] = db[room]["exits"][direction]
  302. send(name, "You walk %s." % Directions[direction][0])
  303. broadcast_room(name, "%s enters from the %s." % (db[character]["name"], Directions[Opposite[direction]][0]))
  304. do_look(name, None)
  305. else:
  306. send(name, "You can't go that way.")
  307. else:
  308. send(name, "You don't have a body yet.")
  309.  
  310. @command("say", "'")
  311. def do_say(name, what):
  312. send(name, "You say \"%s\"" % what)
  313. broadcast_room(name, "%s says \"%s\"" % (name, what))
  314.  
  315. @command("shout", "!")
  316. def do_shout(name, what):
  317. send(name, "You shout \"%s\"" % what)
  318. broadcast(name, "%s shouts \"%s\"" % (name, what))
  319.  
  320. @command("emote", ":")
  321. def do_emote(name, what):
  322. send(name, "You %s" % what)
  323. broadcast_room(name, "%s %s" % (name, what))
  324.  
  325. @command("quit", "q")
  326. def do_quit(name, params):
  327. users[name].close()
  328. del users[name]
  329. broadcast(name, "--- %s leaves ---" % name)
  330.  
  331. @command("who")
  332. def do_who(name, params):
  333. send(name, "%s connected user%s" % (len(users), "s" if len(users) != 1 else ""))
  334. for user in users:
  335. if has_tag(user, "invisible"):
  336. continue
  337. afk = time.time() - accounts[user]["last_seen"] > TIME_AFK
  338. flags = "[%s%s]" % ("W" if is_wizard(user) else " ", "A" if afk else " ")
  339. send(name, flags + " " + user)
  340.  
  341. @command("shutdown", wizard=True)
  342. def do_shutdown(name, params):
  343. raise SystemExit
  344.  
  345. @command("kick", wizard=True)
  346. def do_kick(name, params):
  347. who = params.strip()
  348. if who in users:
  349. send(name, "You kicked %s out of the game." % who)
  350. send(who, "You have been kicked out of the game.")
  351. users[who].close()
  352. del users[who]
  353. broadcast(name, "--- %s has been kicked ---" % who)
  354. else:
  355. send(name, "No user with that name.")
  356.  
  357. @command("invisible", "invis", wizard=True)
  358. def do_invisible(name, params):
  359. if has_tag(name, "invisible"):
  360. remove_tag(name, "invisible")
  361. send(name, "You are now visible.")
  362. broadcast_room(name, "%s appears from the empty air." % name)
  363. else:
  364. add_tag(name, "invisible")
  365. send(name, "You are now invisible.")
  366. broadcast_room(name, "%s vanishes from sight." % name)
  367.  
  368. @command("look", "l")
  369. def do_look(name, params):
  370. character = accounts[name]["character"]
  371. if character is not None:
  372. room = db[character]["position"]
  373. send(name, db[room]["name"])
  374. send(name, db[room]["description"])
  375. contained = in_room_except(room, character)
  376. if len(contained) > 0:
  377. send(name, "You see:")
  378. for i, item in enumerate(contained):
  379. send(name, "[%s] %s" % (i, db[item]["name"]))
  380. send(name, "Exits:")
  381. if any(exit is not None for exit in db[room]["exits"]):
  382. for i, exit in enumerate(db[room]["exits"]):
  383. if exit is not None:
  384. send(name, "[%s] %s" % (
  385. Directions[i][1].upper(),
  386. db[exit]["name"]))
  387. else:
  388. send(name, " None!")
  389. else:
  390. send(name, "You don't have a body yet.")
  391.  
  392. @command("get", "g")
  393. def do_get(name, params):
  394. character = accounts[name]["character"]
  395. if character is not None:
  396. room = db[character]["position"]
  397. items = in_room_except(room, character)
  398. try:
  399. item = items[int(params.strip())]
  400. db[item]["position"] = character
  401. send(name, "You pick up %s." % db[item]["name"])
  402. broadcast_room(name, "%s picks up %s." % (db[character]["name"], db[item]["name"]))
  403. except (ValueError, IndexError):
  404. send(name, "You don't see that here.")
  405. else:
  406. send(name, "You don't have a body yet.")
  407.  
  408. @command("drop", "d")
  409. def do_drop(name, params):
  410. character = accounts[name]["character"]
  411. if character is not None:
  412. room = db[character]["position"]
  413. items = in_room(character)
  414. try:
  415. item = items[int(params.strip())]
  416. db[item]["position"] = room
  417. send(name, "You drop %s." % db[item]["name"])
  418. broadcast_room(name, "%s drops %s." % (db[character]["name"], db[item]["name"]))
  419. except (ValueError, IndexError):
  420. send(name, "You aren't carrying that.")
  421. else:
  422. send(name, "You don't have a body yet.")
  423.  
  424. @command("inventory", "inv", "i")
  425. def do_inventory(name, params):
  426. character = accounts[name]["character"]
  427. if character is not None:
  428. inventory = in_room(character)
  429. send(name, "You are carrying:")
  430. if len(inventory) > 0:
  431. for i, item in enumerate(inventory):
  432. flags = ""
  433. if db[character]["wield"] == item:
  434. flags += " (wielded)"
  435. elif db[character]["wears"] == item:
  436. flags += " (worn)"
  437. send(name, "[%s] %s%s" % (i, db[item]["name"], flags))
  438. else:
  439. send(name, " Nothing")
  440. else:
  441. send(name, "You don't have a body yet.")
  442.  
  443. @command("use")
  444. def do_use(name, params):
  445. character = accounts[name]["character"]
  446. if character is not None:
  447. room = db[character]["position"]
  448. items = in_room(character)
  449. try:
  450. item = items[int(params.strip())]
  451. if db[item]["type"] == "food":
  452. send(name, "You eat %s." % db[item]["name"])
  453. broadcast_room(name, "%s eats %s." % (db[character]["name"], db[item]["name"]))
  454. destroy(item)
  455. elif db[item]["type"] == "weapon":
  456. send(name, "You wield %s." % db[item]["name"])
  457. broadcast_room(name, "%s wields %s." % (db[character]["name"], db[item]["name"]))
  458. db[character]["wield"] = item
  459. else:
  460. send(name, "You can't use that.")
  461. except (ValueError, IndexError):
  462. send(name, "You aren't carrying that.")
  463. else:
  464. send(name, "You don't have a body yet.")
  465.  
  466. @command("remove")
  467. def do_remove(name, params):
  468. character = accounts[name]["character"]
  469. if character is not None:
  470. room = db[character]["position"]
  471. items = in_room(character)
  472. try:
  473. item = items[int(params.strip())]
  474. if db[character]["wield"] == item:
  475. send(name, "You sheathe %s." % db[item]["name"])
  476. broadcast_room(name, "%s sheathes %s." % (db[character]["name"], db[item]["name"]))
  477. db[character]["wield"] = None
  478. else:
  479. send(name, "You aren't using that.")
  480. except (ValueError, IndexError):
  481. send(name, "You aren't carrying that.")
  482. else:
  483. send(name, "You don't have a body yet.")
  484.  
  485. @command("attack", "kill", "k")
  486. def do_attack(name, params):
  487. character = accounts[name]["character"]
  488. if character is not None:
  489. room = db[character]["position"]
  490. items = in_room_except(room, character)
  491. try:
  492. item = items[int(params.strip())]
  493. if db[item]["kind"] == "mob":
  494. send(name, "You attack %s." % db[item]["name"])
  495. broadcast_room(name, "%s attacks %s." % (db[character]["name"], db[item]["name"]))
  496. #TODO Insert send to attacked
  497. send(db[item]["name"], "%s attacks you!" % name)
  498. attacker_str = db[character]["str"]
  499. damage = int(random.randint(round(attacker_str / 10), round(attacker_str / 2)))
  500. send(name, "You deal %s points of damage!" % damage)
  501. db[item]["hp"] -= damage
  502. if db[item]["hp"] <= 0:
  503. send(name, "You killed %s!" % db[item]["name"])
  504. broadcast_room(name, "%s killed %s!" % (db[character]["name"], db[item]["name"]))
  505. #TODO Insert send to attacked
  506. carried = in_room(item)
  507. for i in carried:
  508. broadcast_room(None, "%s tumbles to the ground." % db[i]["name"])
  509. db[i]["position"] = db[item]["position"]
  510. destroy(item) #TODO
  511. else:
  512. do_attack(db[item]["name"], db[character])
  513. else:
  514. send(name, "You can't attack objects.")
  515. except (ValueError, IndexError):
  516. send(name, "You don't see that here.")
  517. else:
  518. send(name, "You don't have a body yet.")
  519.  
  520. @command("@character", "@char")
  521. def do_newchar(name, params):
  522. try:
  523. new_name, new_desc = params.split("=", 1)
  524. except ValueError:
  525. send(name, "Usage: @character NAME = DESCRIPTION")
  526. return
  527. character = accounts[name]["character"]
  528. if character is None:
  529. send(name, "You shape a new body from the chaos surrounding you.")
  530. character = get_id()
  531. accounts[name]["character"] = character
  532. db[character] = {
  533. "kind": "mob",
  534. "name": None,
  535. "description": None,
  536. "position": STARTING_ROOM,
  537. "wield": None,
  538. "wears": None,
  539. "str": 10,
  540. "hp": 10,
  541. "max_hp": 10
  542. }
  543. broadcast_room(name, "A lump of chaos stuff coalesces into an humanoid form.")
  544. else:
  545. send(name, "Your body shakes and flows in a new shape.")
  546. broadcast_room(name, "%s's body shakes and flows in a new shape." % db[character]["name"])
  547. db[character]["name"] = new_name.strip()
  548. db[character]["description"] = new_desc.strip()
  549.  
  550. # Set up the server socket.
  551. server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  552. server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  553. server.setblocking(False)
  554. server.bind((HOST, PORT))
  555. server.listen(1)
  556. print("Listening on %s" % ("%s:%s" % server.getsockname()))
  557.  
  558. # Main event loop.
  559. users = {} # username -> connection (socket instance)
  560. while True:
  561. try:
  562. # Accept new connections
  563. while True:
  564. try:
  565. conn, addr = server.accept()
  566. except socket.error:
  567. break
  568. accept(conn)
  569. # Kick idles
  570. for name, conn in users.items():
  571. idle_for = time.time() - accounts[name]["last_seen"]
  572. if idle_for > TIME_KICK:
  573. conn.send("--- Kicked for being idle ---\n")
  574. users[name].close()
  575. del users[name]
  576. broadcast(name, "--- %s kicked for being idle ---" % name)
  577. # Read from connections
  578. for name, conn in users.items():
  579. try:
  580. message = conn.recv(1024)
  581. except socket.error:
  582. continue
  583. if not message:
  584. # Empty string is given on disconnect.
  585. del users[name]
  586. broadcast(name, "--- %s leaves ---" % name)
  587. continue
  588. parse_command(name, message)
  589. time.sleep(.05)
  590. except (SystemExit, KeyboardInterrupt):
  591. broadcast(None, "!!! Server is shutting down !!!")
  592. break
  593. except Exception as e:
  594. print(e)
  595. continue
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement