From 2421c23521d7287e635e6fbe2040b57fb6f959e7 Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Mon, 20 Nov 2006 18:54:10 +0000 Subject: [PATCH] Initial import. --- evennia/ABOUT | 74 +++++++++++ evennia/README | 16 +++ evennia/__init__.py | 0 evennia/__init__.pyc | Bin 0 -> 128 bytes evennia/ansi.py | 36 ++++++ evennia/ansi.pyc | Bin 0 -> 1109 bytes evennia/apps/__init__.py | 0 evennia/apps/__init__.pyc | Bin 0 -> 133 bytes evennia/apps/config/__init__.py | 0 evennia/apps/config/__init__.pyc | Bin 0 -> 129 bytes evennia/apps/config/models.py | 27 ++++ evennia/apps/config/models.pyc | Bin 0 -> 1636 bytes evennia/apps/config/views.py | 1 + evennia/apps/objects/__init__.py | 0 evennia/apps/objects/__init__.pyc | Bin 0 -> 141 bytes evennia/apps/objects/models.py | 92 +++++++++++++ evennia/apps/objects/models.pyc | Bin 0 -> 3598 bytes evennia/apps/objects/views.py | 1 + evennia/cmdhandler.py | 207 ++++++++++++++++++++++++++++++ evennia/cmdhandler.pyc | Bin 0 -> 6989 bytes evennia/commonfuncs.py | 0 evennia/manage.py | 11 ++ evennia/prepenv.sh | 3 + evennia/server.py | 94 ++++++++++++++ evennia/server.pyc | Bin 0 -> 2594 bytes evennia/sessions.py | 117 +++++++++++++++++ evennia/sessions.pyc | Bin 0 -> 4126 bytes evennia/settings.py | 85 ++++++++++++ evennia/settings.pyc | Bin 0 -> 2079 bytes evennia/urls.py | 9 ++ 30 files changed, 773 insertions(+) create mode 100755 evennia/ABOUT create mode 100755 evennia/README create mode 100755 evennia/__init__.py create mode 100755 evennia/__init__.pyc create mode 100755 evennia/ansi.py create mode 100755 evennia/ansi.pyc create mode 100755 evennia/apps/__init__.py create mode 100755 evennia/apps/__init__.pyc create mode 100755 evennia/apps/config/__init__.py create mode 100755 evennia/apps/config/__init__.pyc create mode 100755 evennia/apps/config/models.py create mode 100755 evennia/apps/config/models.pyc create mode 100755 evennia/apps/config/views.py create mode 100755 evennia/apps/objects/__init__.py create mode 100755 evennia/apps/objects/__init__.pyc create mode 100755 evennia/apps/objects/models.py create mode 100755 evennia/apps/objects/models.pyc create mode 100755 evennia/apps/objects/views.py create mode 100755 evennia/cmdhandler.py create mode 100755 evennia/cmdhandler.pyc create mode 100755 evennia/commonfuncs.py create mode 100755 evennia/manage.py create mode 100755 evennia/prepenv.sh create mode 100755 evennia/server.py create mode 100755 evennia/server.pyc create mode 100755 evennia/sessions.py create mode 100755 evennia/sessions.pyc create mode 100755 evennia/settings.py create mode 100755 evennia/settings.pyc create mode 100755 evennia/urls.py diff --git a/evennia/ABOUT b/evennia/ABOUT new file mode 100755 index 0000000000..5f980a783f --- /dev/null +++ b/evennia/ABOUT @@ -0,0 +1,74 @@ +Evennia Proof-of-Concept +------------------------ +Evennia is a proof-of-concept MUD server written entirely in Python, backed +by SQL. The project rises from a general dissatisfaction with the limitations +of softcode in MUX and MUSH, and the generally inflexible Diku-derivatives and +relatives. + +Evennia represents a combination of several technologies, and most importantly +of all, my first venture into codebase design. You may find things within +the source that look strange to you, perhaps not ideally designed. I'm open +to suggestions, but this really is largely an experiment and a learning +experience. + +Design Objectives +----------------- +1) To create a MU* server that serves as a great foundation for capable admins +to craft into their respective games. It is not my intention to provide a +full-fledged, ready-to-run base, I'm releasing the means to make such games. + +2) Development of games on Evennia must be easy for anyone with some degree +of Python experience. Building needs to be easy, and per-room, per-object, +and environmental customizations need to be simple to do. + +3) The server must utilize SQL as a storage back-end to allow for web->game +integration. See the details on Django later on in the document for more +details. + +4) Any and all game-specific configuration must reside in SQL, not +external configuration files. The only exception is the settings.py file +containing the SQL information. + +How it all Works +---------------- +Python (Including the SQL driver of your choice) + |-asynchat (included with Python2) + |-SQL (MySQL, SQLite, Postgresql) + |-Django (http://djangoproject.com) + +Evennia is built on top of asynchat, an asynchronous TCP conversation/chat +library. This makes the actual socket/connection handling an absolute +no-brainer. + +Serving as our storage medium, SQL is one of the more important and unique +features of the codebase. It allows for very simple code in many cases, and +can lead to a game being a lot more scalable due to the inherent speed of +most modern SQL servers. Another extremely important benefit is that by +storing everything in SQL, we make the entire game accessible from other +means, such as a website. Which leads us to the next component. + +Django is perhaps one of the most interesting introductions to the codebase, +since I'm not aware of any other server using it to run MU*'s. Django is +technically a Python web framework, but it also includes a great data modeling +and database abstraction module. This means that things like Players or +Objects can be represented by a very short class, then related to one another. +This allows us to add, remove, delete, and manipulate things in our database +very easily. Another huge benefit is the admin interface that Django more +or less automatically generates for us. Instead of a bunch of clunky admin +commands, you can fire up your web browser and administer pretty much +everything from there, although equivalent in-game commands may be offered. +The possibilities for developing your game's website are nearly endless with +this tandem of MU* server, SQL, and Django. + +Support +------- +At this time, I am offering no formal support for Evennia. It is not ready for +use and is subject to change in a major way from week to week. I can't hope +to support such a young product. However, if you have questions or ideas, +please direct them to squishywaffle@gmail.com. + +Reporting Bugs +-------------- +Feel free to contact me by email at squishywaffle@gmail.com with as much +details on the bug that you can find. Copy/pasting server logs is generally +a good idea. diff --git a/evennia/README b/evennia/README new file mode 100755 index 0000000000..cda28f7992 --- /dev/null +++ b/evennia/README @@ -0,0 +1,16 @@ +Starting the Server +------------------- +Prior to starting up Evennia, you'll need the following environmental variable +set. + +export DJANGO_SETTINGS_MODULE="settings" + +You may wish to put this in your .bashrc file, or you can simple copy/paste +it before each startup. I'll fix this later so you don't have to, but it'll +do for now. + +Once you've got the evar set, simply enter the following: + +python server.py + +The default port is 4000. diff --git a/evennia/__init__.py b/evennia/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/evennia/__init__.pyc b/evennia/__init__.pyc new file mode 100755 index 0000000000000000000000000000000000000000..16a63f6823fd7fbf35b1d0083b58b8224fb9f238 GIT binary patch literal 128 zcmd1(#LM-h)y_4U0SXv_v;z7VYIzk8Ja-LilG`Sm>%)u)dCulSXFmJpE;KZO`0S|YN9M68L= zQJrZ8w93=~HJR2x>r6IigJ~1A#dHgFn`s+#hv_cp9@Bl$4$}kBL#9Wd$4pN^Ev8-2 zQ>H!8Gp06ZpXoX11=CB=E2h_=H%t!bEz>*Dd!`Sdk4znq%X9$hG97|?OdhDubOaiR zGv^b{X$Qgnc6xTwE@ToY8MR9_y}0PuB?c&(Y6bCJ$_YQVR0EH)41{ZuT9kKB3nX%H*H_dx6PglVuOC5OC z<+09foH{k+asR#_<)QEMc+hj~(2MtD7s0_#2el7!$9XErCc~D!(d}dx|NqoFsL1hWZOnZ=##UKIzKXn^} literal 0 HcmV?d00001 diff --git a/evennia/apps/config/__init__.py b/evennia/apps/config/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/evennia/apps/config/__init__.pyc b/evennia/apps/config/__init__.pyc new file mode 100755 index 0000000000000000000000000000000000000000..dd805c3d3ff157b9b90bc0886716d7d01c168e4e GIT binary patch literal 129 zcmd1(#LIQH)6q4V0SXv_v;zH6{UnR%Hd@$q^El_ena>_NsAg9rfMa~hfe literal 0 HcmV?d00001 diff --git a/evennia/apps/config/models.py b/evennia/apps/config/models.py new file mode 100755 index 0000000000..464437ce5b --- /dev/null +++ b/evennia/apps/config/models.py @@ -0,0 +1,27 @@ +from django.db import models + +class CommandAlias(models.Model): + """ + Command aliases. + """ + user_input = models.CharField(maxlength=50) + equiv_command = models.CharField(maxlength=50) + + class Admin: + list_display = ('user_input', 'equiv_command',) + +class Config(models.Model): + """ + Although we technically have the ability to create more than one Config + object via the admin interface, we only really need one. This also leaves + the possibility for multiple games hosted on the same codebase or database + in the future, although this is not a priority. In any case, this model + contains most of the game-specific configuration. + """ + site_name = models.CharField(maxlength=100) + site_description = models.TextField(blank=True) + site_website = models.URLField(blank=True) + player_start_dbnum = models.IntegerField() + + class Admin: + list_display = ('site_name', 'site_website',) diff --git a/evennia/apps/config/models.pyc b/evennia/apps/config/models.pyc new file mode 100755 index 0000000000000000000000000000000000000000..09311ec112abe66d74bcf8b6c712db0cd5fad904 GIT binary patch literal 1636 zcmah}O_LKh5Y^cG0md6rdkQ(}=mW8%dx7)w>FOU8%L@-R1Ajd z-uHG}ltZO6got;&;@4hd_Dc77C<<>HDhdf)6x_PrVTtBB8&gr)*gCVD@md{*^n{m- zIyAJ{B(rm&TU4}N(3ZTnW-*`dr>1S=q6~g*H;dt7=It&AN`Q=*oMbaV$Hf6jsvI@s=!!%;V3S!c5qVR=En+nttL!{GA4$YZL9}l?oTTxv z{Fo9#13G1LX3Kj*lKaBY!7>rc15kH41yw+p<2`53aGR(qUNdDujtiIS&~Iw}fpns> z_O>+6?R0InY^X__m321lbPBrc$RyHD=&)tH4xX5C=r2Nr)isq#Z><>)f?nGCMBUny z^cWd}=g=Gl89Y&i2y=Z^+sKTKK|A8KQAo?F+YqC@Nxu%AZhDt&>qu{yH&NFiCJ7?s-I^+W4I=`S@c# z$sWQVgknA=V?8V}>9Cf-0Q6gIskCcb!i8JXcP823MS?jV2+K+zl(%xmzZ2@<&gBC# z3Jbm~KQJ)bM4}oIiL5@*3jc%n?)XFq>5k8s4F{^{p!zqt3oyUpFeWS!V7he1Un@go*%loU@&BE_9HjJ+J*1i!%#;lc0!!y?h4%Ti ziOF>Q7OZ^Vd|z!S6;F-SJUtb)vErtqSMol}6IfjvzX|hdCD`Q&{0q-@&gWE)2s3Gr YREDK&92z9LPPoaZ^4_D!v(cl;|7OZv8~^|S literal 0 HcmV?d00001 diff --git a/evennia/apps/config/views.py b/evennia/apps/config/views.py new file mode 100755 index 0000000000..60f00ef0ef --- /dev/null +++ b/evennia/apps/config/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/evennia/apps/objects/__init__.py b/evennia/apps/objects/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/evennia/apps/objects/__init__.pyc b/evennia/apps/objects/__init__.pyc new file mode 100755 index 0000000000000000000000000000000000000000..8776a7bd018cffa4e1baf29d52e14434c8c5907f GIT binary patch literal 141 zcmd1(#LKnC)5$fN0SXv_v;z*QdVkmNwI!>d}dx|NqoFsL1hWZYcdeaqz!Lx9 zP5Ll>hdx4o_66E=?k={0s8p)dz)t4#&bfE)IWrmjxw@SE_GBwn^-mMOzs2Vr3ZvAK zQdy+-BNdrQ9W_+eRQpXd8p=k>ol?C>nWlQJ)GMW)?$0T6YGz<=GBB^q{LH|@WMDy= zMYY>n!fvgI`_7{n6vk%0YM(;;CvVGe8kL=cXEqI+S>pX$h&Qr&yO%yxSrSe-Fj7)_N;R`?`?5b;`|kn&i5l99wN% zuV+h}2Yq1C;fm7hQ0Qbh%*L#^(58hz2M5WE%;txo@BTt7rMvtX@=bjH0t&!=sg$eA zhUzucrQKEo1KSdrTfTM6e9_{Pz{q%;^`w#~0~|0gJj_AIff*(f72eqA-W@t{J#IWt%@7*udw{ zps3FPiPB&s33EFZ#57{8PfIrpuE@nN&vByn?~clnMT~a49Pf6+vNV8il(D&jX6Ah8 z8s~>uGM>6CwU{_#`ao0yem-i^M$FTQS*u+%MGAY<)RW`d>?h?tXERfQ=bAnHB2-<( z6?1`XaN^DwRB}e#Xa%HMk5(E>O|gA9gwh?1f}Mnr-bx66LFL0~Je$~H77|-h`W3h; zDg7ErzQnBVA>{KDx-IiW7!v<0QjeKALE?2%T8-q)50S$CBuLv2ZR&b1WiC&1J&cFe z>!Mea!ngIkqNFwZA{h)bdq*eQlrBHgX_1G-<+jwhwMMw%LxGK4@Ve)+OlL^K?2dlk z2aOVd$YI0<8arV{F|>In%?fW#Co9q;{2Jv|Mf;Eh+tz!1>#blnMQ-)6OK2;36L5b= zmr1~8^fA|i` z9m(fb6a#~u(hdsf#uN_?7CCL{9y9|X!0gN&SrLwy3L$zS=~)Tjf0Ygq2g&M~33`%X zRODfTf68pBm@m0L$=H8Mdb031$wu}ax=;I=`cq2A)PWNT#k{?N?+ukXab_LmAYNrb zzE|$8qb9O}{Iu>ce`v=N$4cuyK;W}jd=8(d<&{Mecs*JJg=6+k;y@`7=Kg`oEOX6A z%6zQMC(3-P%yre9gak6(*4qWgUoz^ov3U`= zTdiZa{qzm z_49+h874E4(|!T0US=O;Nq!`xlsm1o8I;V(ttYod0fkF=V~VkSlx32EF&_+)-IGZ8 z|5X~k!v3{1qzuHHZrTrNq2e2`O>HyFA_jjN|vMdjPRw#cV~oE@xh%j2U8tegdMqPon4aK6jgUlTnI;a-m>9y2AxV~*hcR(p99ts2v=#buXFlZ>{e7%@+M00ZHA z8wA7_>x`pnzOBc^vBANaM_8kNgf-D$J;J81YVn-|GaCAj+WU~kxig+o{3Yy&FQQg8 z2_U{SX>v+@dD4`fu1uN?R(zFBu9$&9u@xHZZ2Yjbx!2u$`gm*i9bFF;;N1bVsT;4$xGW^a6*g*@^;W9=pj za(9{{QmgJtLi!w^r;jUhs(Epd_r-d2ga2@AEG;a}Z?_0TNNt`a`C-vE2jW|}hW@&X zAn-bDC!^4BSFdq#Nd64swfTsDAH}Gs3MaR2ai9v546rZ0!-B3%5m!mgqxy!0C!W=4 LwQ+Uz>iNF`6J4fd literal 0 HcmV?d00001 diff --git a/evennia/apps/objects/views.py b/evennia/apps/objects/views.py new file mode 100755 index 0000000000..60f00ef0ef --- /dev/null +++ b/evennia/apps/objects/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/evennia/cmdhandler.py b/evennia/cmdhandler.py new file mode 100755 index 0000000000..c4c7ec5235 --- /dev/null +++ b/evennia/cmdhandler.py @@ -0,0 +1,207 @@ +from django.contrib.auth.models import User +from apps.objects.models import Object +import settings +import string +from ansi import * +""" +This is the command processing module. It is instanced once in the main +server module and the handle() function is hit every time a player sends +something. +""" + +class GenCommands: + """ + Generic command class. Pretty much every command should go here for + now. + """ + def __init__(self): pass + + def do_look(self, cdat): + """ + Handle looking at objects. + """ + session = cdat['session'] + server = session.server + player_loc = session.player_loc + player_loc_obj = Object.objects.filter(id=player_loc)[0] + + retval = "%s%s%s%s\n\r%s\n\r" % ( + ansi["normal"], + ansi["hilite"], + player_loc_obj.name, + ansi["normal"], + player_loc_obj.description, + ) + session.push(retval) + + def do_quit(self, cdat): + """ + Gracefully disconnect the user as per his own request. + """ + session = cdat['session'] + session.push("Quitting!\n\r") + session.handle_close() + + def do_who(self, cdat): + """ + Generic WHO command. + """ + session_list = cdat['server'].session_list + session = cdat['session'] + + retval = "Player Name\n\r" + for player in session_list: + retval += '%s\n\r' % (player,) + retval += '%d Players logged in.\n\r' % (len(session_list),) + + session.push(retval) + + def do_say(self, cdat): + """ + Room-based speech command. + """ + session_list = cdat['server'].session_list + session = cdat['session'] + speech = cdat['uinput'][1:] + players_present = [player for player in session_list if player.player_loc == session.player_loc and player != session] + + retval = "You say, '%s'\n\r" % (''.join(speech),) + for player in players_present: + player.push("%s says, '%s'\n\r" % (session.name, speech,)) + + session.push(retval) + + def do_sa(self, cdat): + """ + Temporary alias until we come up with a command alias system. + """ + self.do_say(cdat) + + def do_version(self, cdat): + """ + Version info command. + """ + session = cdat['session'] + retval = "-"*50 +"\n\r" + retval += "Evennia %s\n\r" % (settings.EVENNIA_VERSION,) + retval += "-"*50 +"\n\r" + session.push(retval) + +class StaffCommands: + """ + Restricted staff commands. + """ + def do_dig(self, cdat): + """ + Digs a new room out. + """ + session = cdat['session'] + uinput= cdat['uinput'] + roomname = ''.join(uinput[1:]) + + if roomname == '': + session.push("You must supply a room name!") + else: + newroom = Object() + newroom.name = roomname + newroom.type = "Room" + + def do_nextfree(self, cdat): + """ + Returns the next free object number. + """ + session = cdat['session'] + server = cdat['server'] + + nextfree = server.nextfree_objnum() + retval = "Next free object number: %d" % (nextfree,) + + session.push(retval) + +class UnLoggedInCommands: + """ + Commands that are available from the connect screen. + """ + def __init__(self): pass + def do_connect(self, cdat): + """ + This is the connect command at the connection screen. Fairly simple, + uses the Django database API and User model to make it extremely simple. + """ + session = cdat['session'] + uname = cdat['uinput'][1] + password = cdat['uinput'][2] + + account = User.objects.filter(username=uname) + user = account[0] + + autherror = "Invalid username or password!\n\r" + if account.count() == 0: + session.push(autherror) + if not user.check_password(password): + session.push(autherror) + else: + uname = user.username + session.login(user) + + def do_create(self, cdat): + """ + Handle the creation of new accounts. + """ + session = cdat['session'] + uname = cdat['uinput'][1] + email = cdat['uinput'][2] + password = cdat['uinput'][3] + account = User.objects.filter(username=uname) + + if not account.count() == 0: + session.push("There is already a player with that name!") + elif len(password) < 3: + session.push("Your password must be 3 characters or longer.") + else: + session.create_user(uname, email, password) + + def do_quit(self, cdat): + """ + We're going to maintain a different version of the quit command + here for unconnected users for the sake of simplicity. The logged in + version will be a bit more complicated. + """ + session = cdat['session'] + session.push("Disconnecting...\n\r") + session.handle_close() + +# We'll use this for our getattr() in the Handler class. +gencommands = GenCommands() +staffcommands = StaffCommands() +unloggedincommands = UnLoggedInCommands() + +class Handler: + def __init__(self): pass + def handle(self, cdat): + """ + Use the spliced (list) uinput variable to retrieve the correct + command, or return an invalid command error. + + We're basically grabbing the player's command by tacking + their input on to 'do_' and looking it up in the GenCommands + class. + """ + session = cdat['session'] + uinput = cdat['uinput'] + + if session.logged_in: + # If it's prefixed by an '@', it's a staff command. + if uinput[0].find('@') == -1: + cmdtable = gencommands + else: + cmdtable = staffcommands + else: + cmdtable = unloggedincommands + cmd = getattr(cmdtable, 'do_' + uinput[0].lower(), None) + + if callable(cmd): + cmd(cdat) + else: + session.push("Unknown command.\n\r") + diff --git a/evennia/cmdhandler.pyc b/evennia/cmdhandler.pyc new file mode 100755 index 0000000000000000000000000000000000000000..dc065d14695d293f8830d87af0c6833aaba67d33 GIT binary patch literal 6989 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 schedule['heal']: + heal() + lastrun['heal'] = time.time() + +# +## End: Time Functions +# + +class Server(dispatcher): + """ + The main server class from which everything branches. + """ + def __init__(self, port): + dispatcher.__init__(self) + self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.set_reuse_addr() + self.bind(('', port)) + self.listen(100) + self.session_list = [] + self.object_list = {} + self.game_running = True + print '-'*50 + self.load_config() + self.load_objects() + print ' Server started on port %i.' % (port,) + print '-'*50 + + def load_config(self): + """ + Loads our site's configuration up for easy access. + """ + self.config = Config.objects.all()[0] + print ' Configuration Loaded.' + + def load_objects(self): + """ + Load all of our objects into memory. + """ + object_list = Object.objects.all() + for object in object_list: + dbnum = object.id + self.object_list[dbnum] = object + print ' Objects Loaded: %i' % (len(self.object_list),) + + def handle_accept(self): + """ + What to do when we get a connection. + """ + conn, addr = self.accept() + session = PlayerSession(self, conn, addr) + session.game_connect_screen(session) + print 'Connection:', str(session) + self.session_list.append(session) + print 'Sessions active:', len(self.session_list) + +if __name__ == '__main__': + server = Server(4000) + + try: + while server.game_running: + asyncore.loop(timeout=5, count=1) # Timer() called every 5 seconds. + Timer(time.time()) + + except KeyboardInterrupt: + print 'Interrupted' diff --git a/evennia/server.pyc b/evennia/server.pyc new file mode 100755 index 0000000000000000000000000000000000000000..2de910e811102ffd38b542335dca0a30fb19c5e6 GIT binary patch literal 2594 zcmZuyZEqVz5S~51BzEH@P1?|afRhTyRFH%UK2VjHHk7ogFz6DLVu6m!-8#PHeCOV7 z(ppMCr4pa`5Bw+o3BQ2n*>i1Lan_FaW@l$-pJ!%n^iQWf`s3>du}q%^{=daAA1jeW zl@U`gP$%qz_w68q#mLNmKewNosPsAlahy7qOu(r!C3a z(r=48#*QnZm!#j3q#*{%j1_Qw+_4IF1hlfu;RCWE9oV2A_5``yOm%WG3swX*d@npYy4Ulz#n3y(1lA3X3 z%k>N?av8(msX4z#f|YUutxhPeu1fdy`^ z92TAg`&|#(UQDA`W0%HslL$hjw%3KI0(Jh4&IXl=dV3RMdsvM0_OXp7Sy65$`g~iT z>pV}RZ3K0$%dPRmF2XJh(>%3dxQw~MkZuiPx|ByF9fp=zgyE=2&N93PG?<^}W&xuP zFobXzzBQE#ZK$Jc(D-a;XKN?sIv-wP7vp~~MT5Eb%5f9A4Q`-gQ4Spf!Yp@={=@`O zrizrmQ1ZMd7fQbS?R$CQ$z)X!%7sUmH#gN`{toH4uQFu5b{Bc@5#EJpsbVHyv8q-Hs>!!$qkkIEGce@xSgQ$oGPpobH zrQoK{a;I#{rB`Ve96?-PB=g9Jfj8UIX0+U&Anv+c+k1!xwg($bW*U|ok?7$>xB;)? z*!eWQjj6c-;a4d6#zv*piC^UYxG1gPy+Y&Hn}{o%fH+!m6_+})Iy6Oms;xtOIwke@ zz7HSu9voP@+5d6(akzgFJh=CRb!Dn;Sn4yQ!zf9rR5(iW#H9%jcfPXJSe?6W;*JbR z`5XD*0@{JztU`$Ds14Ot>-cZsH>)ycmjnNIOo*qevR^$xZJ^e0E|e$`pmzz4$5I0i zkPcvAifpdl_8y`J($;Lv!&;kl0ZWJcewU{i;>!fUbBI>!+rm!@B#X}dXWBp3){p$S z$a5WAbX=D!Ff~MLcc#AMt~Kq-#*biouJ805H5CmcH;Q9DwhlacbQ6awIeM+JyP7mr zleg@E zYP)8Iu8+FKH{!3AsXw-#;>@DjprD8gbSR_*UJ3uB94)Op8>1MyED-0$eKdAUaQ_(+t}==C9j5-zJQwE^p@4Xgb}`y literal 0 HcmV?d00001 diff --git a/evennia/sessions.py b/evennia/sessions.py new file mode 100755 index 0000000000..51dc40735f --- /dev/null +++ b/evennia/sessions.py @@ -0,0 +1,117 @@ +from asyncore import dispatcher +from asynchat import async_chat +import socket, asyncore, time, sys +from cmdhandler import * + +chandler = Handler() + +class PlayerSession(async_chat): + """ + This class represents a player's sesssion. From here we branch down into + other various classes, please try to keep this one tidy! + """ + def __init__(self, server, sock, addr): + async_chat.__init__(self, sock) + self.server = server + self.address = addr + self.set_terminator("\n") + self.name = None + self.data = [] + self.sock = sock + self.logged_in = False + self.user = None + # The time the user last issued a command. + self.cmd_last = time.time() + # Total number of commands issued. + self.cmd_total = 0 + # The time when the user connected. + self.conn_time = time.time() + # Player's room location. Move this to a player sub-class. + self.player_loc = 4 + + def collect_incoming_data(self, data): + """ + Stuff any incoming data into our buffer, self.data + """ + self.data.append(data) + + def found_terminator(self): + """ + Any line return indicates a command for the purpose of a MUD. So we take + the user input, split it up by spaces into a list, and pass it to our + command handler. + """ + line = (''.join(self.data)) + line = line.strip('\r') + uinput = line.split(' ') + self.data = [] + + # Increment our user's command counter. + self.cmd_total += 1 + # Store the timestamp of the user's last command. + self.cmd_last = time.time() + # Stuff anything we need to pass in this dictionary. + cdat = {"server": self.server, "uinput": uinput, "session": self} + chandler.handle(cdat) + + def handle_close(self): + """ + Break the connection and do some accounting. + """ + async_chat.handle_close(self) + self.logged_in = False + self.server.session_list.remove(self) + print 'Sessions active:', len(self.server.session_list) + + def game_connect_screen(self, session): + """ + Show our banner screen. + """ + buffer = '-'*50 + buffer += ' \n\rWelcome to Evennia!\n\r' + buffer += '-'*50 + '\n\r' + buffer += """Please type one of the following to begin:\n\r + connect \n\r + create \n\r""" + buffer += '-'*50 + '\n\r' + session.push(buffer) + + def login(self, user): + """ + After the user has authenticated, handle logging him in. + """ + self.user = user + self.name = user.username + self.logged_in = True + self.conn_time = time.time() + self.push("Logging in as %s.\n\r" % (self.name,)) + print "Login: %s" % (self,) + + def create_user(self, uname, email, password): + """ + Handles the creation of new users. + """ + # print uname, email, password + user = User.objects.create_user(uname, email, password) + self.login(user) + print 'Registration: %s' % (self,) + self.push("Welcome to the game, %s.\n\r" % (self.name,)) + + def nextfree_objnum(self): + """ + Returns the next free object number. + """ + + def __str__(self): + """ + String representation of the user session class. We use + this a lot in the server logs and stuff. + """ + if self.logged_in: + symbol = '#' + else: + symbol = '?' + return "<%s> %s@%s" % (symbol, self.name, self.address,) + +# def handle_error(self): +# self.handle_close() diff --git a/evennia/sessions.pyc b/evennia/sessions.pyc new file mode 100755 index 0000000000000000000000000000000000000000..fdc3392524edd67219e82d908e47ef98400bd369 GIT binary patch literal 4126 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 literal 0 HcmV?d00001 diff --git a/evennia/settings.py b/evennia/settings.py new file mode 100755 index 0000000000..c033e6e3c0 --- /dev/null +++ b/evennia/settings.py @@ -0,0 +1,85 @@ +# Django settings for evennia project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASE_ENGINE = 'mysql' # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. +DATABASE_NAME = 'evennia' # Or path to database file if using sqlite3. +DATABASE_USER = 'evennia' # Not used with sqlite3. +DATABASE_PASSWORD = 'CvAPpy:FFRTmTMHf' # Not used with sqlite3. +DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. +DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3. + +# Local time zone for this installation. All choices can be found here: +# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE +TIME_ZONE = 'America/New_York' + +# Language code for this installation. All choices can be found here: +# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes +# http://blogs.law.harvard.edu/tech/stories/storyReader$15 +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = False + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '/home/gtaylor/dev/evennia/media' + +# URL that handles the media served from MEDIA_ROOT. +# Example: "http://media.lawrence.com" +MEDIA_URL = '/media/' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/media/amedia/' + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'fsd&lkj^LKJ8398200(@)(38919#23892(*$*#(981' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.load_template_source', + 'django.template.loaders.app_directories.load_template_source', +# 'django.template.loaders.eggs.load_template_source', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.middleware.doc.XViewMiddleware', +) + +ROOT_URLCONF = 'evennia.urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.admin', + 'evennia.apps.config', + 'evennia.apps.objects', +# 'django.contrib.sites', +) + +# +## Evennia Config +# +EVENNIA_VERSION = 'Pre-Alpha 0.0' diff --git a/evennia/settings.pyc b/evennia/settings.pyc new file mode 100755 index 0000000000000000000000000000000000000000..4a6fbbe5bcc0e5a01c01a3e1edd0a3e99cef0bcb GIT binary patch literal 2079 zcmds2+iu%N5FObPeMy|ex!OsJ7DyW~2140Rf!alZh7nf|8;KH@w3F~5ETdi9bVO3+ zu59Q(`XPPmfAtGGL)nsCw|#992^=oZ&YA0CHvMB|`M2MHW<5Cn8TfvQkNOpb1PCi2 zD-h1XA+F~^&O=xQf$JIw#EnuefS7}D5d`iom2w$G6~b#EY7kxrVL-S7@&<%AK`cOc z3&bLXt00yjybWR*!aE=yLUG1)G#}KZ8Sc7mK#5(xq7Z{kXp-4yS zm%d)0I8SAkC9%4N>&B_w9gMzjHhGW+?bnAIF(`NJR2E4uu6yz1c6d94(^Px&A zr1K?;`f;Ah`jL)D{k*6Pd0IcWT~DPz59DpUtg9Q-bzJV&)doU`N<8gUvE4ycXzhbcc?B-;_N85v`h#6SWBy)$3hm$I!yXf zjTGV~6kRApDjya-sebquZaf%7A}M4~=S3pb-y3)~HIODk^yOJxNUN8pX`We)^3|T; z>f5PyFVA$5e6*BQD#KSUEBBHcDQqy`MTo=myvFV2YLyaf!k(L*g5yrvc0{13@2)I z99GU`-^IXPr%b6C_mwPzM#qu6wd3h!G|^^`Ye&4(8Cx8G?(>$ujhkhr(HQG;)^y*M z9G^8f3!*pdy{2hDDN(CqJ5&s!wq3_*v9~tIa9Wrb^EJ%}aulKYM#pQGNlnO(i%hCG z);6$PE#^daw>z#ZJ7AuNVIH7$x8s>>WQnc;{+I+6pHn29_P6LTkKRJyoPNn74X->Bjr#|v)Bxv}U_>6_7(fH4o6;@j(*^#maBTaYP kkMk&$g~F&9b4C@P%H&@)Y9&@hyk^Yf-!K*sF^rWz0YnNrI{*Lx literal 0 HcmV?d00001 diff --git a/evennia/urls.py b/evennia/urls.py new file mode 100755 index 0000000000..4811fa01bf --- /dev/null +++ b/evennia/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls.defaults import * + +urlpatterns = patterns('', + # Example: + # (r'^evennia/', include('evennia.apps.foo.urls.foo')), + + # Uncomment this for admin: + (r'^admin/', include('django.contrib.admin.urls')), +)