HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Hey all! There's been talk lately that there hasn't been enough "hardcore" programming discussion on here. There's also been a number of people requesting help with MUD programming. So here's a thread to talk about both of those topics. I recently decided to help RifleFire work on a Christian MUD he's been wanting to do for a long time, and so I've been working on a MUD engine for him. There are a lot of good MUD codebases out there already on SourceForge, and I'm learning from them, but I decided to try making one from scratch myself just for the learning experience. I decided to write it in C#, and hopefully get it to run on a Linux server using Mono or DotGNU. I've never done a MUD before, and so I'm having a lot of fun trying new things, because I don't know what I'm not supposed to try. So basically I'm building a really generic framework. We're talking so generic, that the engine doesn't even have verbs built into it -- you have to define the verbs in the database and assign scripts to them. For instance, you want to have the command "look" in your game's vocabulary. Great! So you just attach a script to the verb "look", and a simple version of the script would look something like:
So this basically says, message the player with the description of the target. If you wanted to make the "look" script aware of a player being blind, you could have a slightly more complex version of the "look" script:
All of these scripts are located in the database, and are compiled in dynamically at runtime using Reflection. This means that if you wanted add that blindness feature, you would do it while the game was up and running, and you could compile the script in, and it would take immediate effect! You wouldn't have to recompile the server, you wouldn't even have to boot the players off and interrupt their game playing! This makes bug-fixes less annoying for players, because you don't have to take the server down for every little tweak and change. This all sounds well and good, but the trouble is implementing all of this. I'll probably post more about this, but this is enough for now. As far as coding all of this, so far I've got the database structure for logins and objects (working on scripts next), I've got my program talking to the database, and I've got my program able to compile scripts into assemblies using Reflection. Nothing too complicated, but just fitting it all together and trying to make it simple and elegant. Hope some of you found that interesting! If there are many other people interested in learning how to do stuff like this, I'm willing to make the code open and work on it with other people! In Christ, [This message has been edited by HanClinto (edited May 16, 2005).] |
|||
Jari![]() Member Posts: 1471 From: Helsinki, Finland Registered: 03-11-2005 |
Yeah that sounds fun and interesting. But I have couple questions though, mind if I ask them? Ok. ![]() 1) 2) Thats all. ------------------ |
|||
kiwee![]() Member Posts: 578 From: oxfordshire, england Registered: 04-17-2004 |
since i can't code, is there a MUD maker on the faithful interweb? or am i just putting my hopes up too high, also, can you code MUD's for websites? ------------------ |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Hey Jari! Thanks for the thoughtful reply!
quote: ![]()
quote: Ah, nope. I forgot that MUDs can display text in color. I'm thinking this MUD would just use a simple Telnet client, and having color totally slipped my mind. I was just assuming it would send simple black-and-white text to the user. So the transaction on the user's screen would look something like: quote: That's all I was picturing. You're right though, when I add color support, I'll add another parameter for the message type.
quote: All of the scripts are compiled at runtime. For example, here's a sample piece of code that, in C#, compiles a string of code which is more C#, and then you can call that compiled piece of code from within the code that compiled it. Boy that was a mouthful. Anyway, here's a function from my code:
So then, you can use that function like so:
And create a .NET assembly from the string of code stored in the database under the tag "LOOK_SCRIPT". If there are errors, it will notify you, and fail to create the new assembly, and continue to use the old one until you resolve the errors in the new one. Because this all happens at runtime, you wouldn't even have to kick players off, because you're just hot-swapping these assemblies in at runtime. Before you update the LOOK_SCRIPT, it uses the old one, and then when you change it, it updates it, and from there on out, it just uses the new one. It's all compiled on the fly, and linked in. It's really cool, and it's one of the things that I've been learning how to do since I've been sick at home for most of the past week. I've been learning that, and database access. Speaking of database access, here's a SQL select that I wrote yesterday to select the inventory of a player. It's pretty complicated, and I'm really happy that it works.
And Kiwi, I'm not sure about there being a MUD maker on the faithful interweb. I plan on letting other people use this MUD engine when I'm done with it. And the cool thing about this engine is that for people to use it, they have to be able to code, but only a *little bit*. I'll have taken care of all of the diffucult network and database coding, all that the MUD creators would have to make is the gameplay code (which is the fun stuff). Like deciding how alignment and combat and other such things work. Code MUDs for websites? You mean like a MUD that you can access through a web browser? I don't know how to do that yet, no. I imagine this system could be adapted for such a purpose -- all you would probably need is just a javascript telnet client that could connect to the normal MUD server via telnet. In Christ, [This message has been edited by HanClinto (edited May 16, 2005).] |
|||
Jari![]() Member Posts: 1471 From: Helsinki, Finland Registered: 03-11-2005 |
Ok, I'm glad to see you have it figured out. ![]() I wish you well with the project. ------------------
|
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
heh, hope I didn't put you to sleep with that last post. I re-read it, and it's all extremely confusing. I understand what I'm talking about, but I'm not sure that anyone else who doesn't have an solid grasp of .NET and reflection would be able to understand what I'm talking about. Basically C# lets you compile code on the fly, and then lets you access that code. Built-in compiled scripts. I'm storing all of my game logic in dynamic scripts, so that it can be changed whenever I want.
quote: Thanks! I pray it's useful to Christian mudders. It should really allow people to create MUDs that don't follow the "norm". Entirely new systems and concepts can be built directly into the game logic, all while maintaining the original core engine. I'll keep everyone posted as far as my progress goes. Today and yesterday I've been working on object inheritance, so that you can have a generic "bottle" object with a bunch of descriptions and attributes associated with it (such as the ability to fill the bottle), and then you can have different types of bottles that inherit everything from the original, plus whatever you want to change/add. I'm worried that this system will be overly complex, and not really useful to anyone except myself. I guess it means I'll just have to write really good tutorials, neh? --clint ------------------ |
|||
Jari![]() Member Posts: 1471 From: Helsinki, Finland Registered: 03-11-2005 |
quote: Nah, I think it's going to be fine if you keep the object hierarchy and methods for scripting simple. Did you plan that the objects derived from the bottle's base class would all be in script files? ------------------
|
|||
HeardTheWord![]() Member Posts: 224 From: Des Moines, IA Registered: 08-16-2004 |
Sounds like a pretty flexible engine! I'm just curious if you have some of the server code done. I started writing a managed dll in C++ that can be used for networking purposes. Right now it is extremely basic because the person I wrote it for just wanted a peer to peer chat program. So right now it is a bit feature deprived. In case you are wondering... I am using WinSock's asynchronous sockets. I have also created some event functions that are triggered when data is received, a client connects, the server accepts, and other basic socket messages. I plan to add multiple clients that are easy to keep track of and send messages to. Also broadcasting is a feature I would like to add as well. Let me know if this could be of use to you and I will continue to add features to it. |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Jari: Thanks for the encouragement! The objects derived from the base bottle class are defined in the database. In the database are stored the object's attributes and its actions (scripts). So not *just* in scripts, but also in the database. I'm rethinking how all of the object inheritance works, and man, it's a pain for it to be all nested. I'm thinking I might not deal with it for now, and just let everything be copied different places or something -- it's crazy to try and do multiple levels of inheritance in SQL.
No, I haven't taken time to do any of the networking yet, so it would be extremely interesting to me to have someone help me out with it. I need to make this support Telnet, and ideally it would support a few different MUD client standards, as well as just straight Telnet color to "spice things up". I know that's a little beyond the scope that you were talking about, but I'm interested! You said you're doing this in managed C++? I'm doing all of the other stuff in C#, but if we can compile it all into a .NET assembly, I suppose it should all work together. Winsock eh? Hrm. I'm trying to use System.Net.Sockets, so that it can be Mono compatible and be able to be run on Linux. However, they're very similar in structure. Anyway, that's where I stand. If you're interested in getting involved, that would be fantastic, but if this is outside of your scope, I understand. BTW, the URL for RifleFire's discussion group about the MUD is at http://games.groups.yahoo.com/group/thewaymud/ . In Christ, ------------------ |
|||
HeardTheWord![]() Member Posts: 224 From: Des Moines, IA Registered: 08-16-2004 |
I am interested in helping you out. I know the dll will work in C# but only in Windows. If it needs to work in Linux it may take some time to convert. I will keep you posted on what I get accomplished. As for telnet color, I believe all it takes is the correct Ascii code. I will see if that can be easily implemented as well as some of the other telnet features. |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Hey HeardTheWord! Okay, great. I'd be grateful for whatever help I could get. If I had my choice, I would want the whole engine to be coded in C# just for consistency's sake, but either way is great! Glad to see you joined the forums over on Yahoo -- we'll keep talking about this. In Christ, ------------------ |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Sample of progress: Okay! Well it's now loading scripts from the database, compiling them on the fly, and having the scripts interact with the database and the player. It's late, and I need to go to bed, but I wanted to post this so that people could dink around with it. Sorry, but the database is still in Microsoft Access. Unless you've got Access, you won't be able to edit this run of the database. Later, I'll put it into a better format like MySQL, but this was the easiest for me to do at the moment. So to give a rundown of the scripts, and the way they work, here's a current "test" script that's in the database:
Nothing special. Basically it just says, "Tell the player, 'This is a test script! Hello world!'", nothing too fancy. A more complex script is the "inventory" script. That looks like this currently:
So basically all that's saying is, "for each item in the player's contents, display the LONGNAME of that item". I realize it's a little confusing, but I really think people can learn it after a little while. Regardless, most of the hard scripting like this will be done by me, and all people will have to do to create MUDs is add in objects and rooms and the room descriptions. Once you've wrapped your teeth around the inventory script and want to bite into something bigger, here's the "look" script:
Part of that code should look familiar, as it's basically the inventory script, except instead of looking at the player's contents, it looks at the contents of the target (what you're pointing at). It's pretty dumb if you type in a target that doesn't exist, it doesn't give you an error message or anything. So these scripts aren't final, they're just a quick example I threw together of what you can do with this engine scheme. So if you want to check it out, you can download the engine and the small sample database here! Please check it out, and try playing with the database if you have Microsoft Access. In Christ, ------------------ |
|||
CoolJ![]() Member Posts: 354 From: ny Registered: 07-11-2004 |
I didn't know you could do stuff like that with C#! Store code in a database then compile it on the fly. That's really cool! How do you use the script code once you get it compiled? I noticed you get a reference to it, but how is it executed when someone types the command in? Are you storing it's reference in an array with all the other compiled scripts? |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
There is a table called "Verbs". It has entries that look like this:
So let's say you wanted to add a new command called "ls" as a joke to the Linux ppl who play the game. So you add a new verb to the Verbs table:
Then now you've just linked the verb "ls" to script number 5 in the Scripts table. Over in the Scripts table, you can define a script with ID 5, and with the name of whatever you want. For simplicity sake, you can also name it "ls". The entry would look something like: ScriptID: 5 And if you wanted to make "dir" do the same thing as "ls", you could then just add another verb with the name "dir" and have it also point to the "ls" script. I hope that makes sense! I think I'll be switching this all over to MySQL so that more people can edit the database and play with creating scripts. This was just the first proof of concept demo, and I'm really pleased that it works as well as it does. In Christ, ------------------ |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
I just realized that you might have been referring to how I actually store the script inside the engine, and call it. That's done like so:
And ScriptMethod is defined right here:
I realize it's not commented and is probably pretty confusing, but that's the heart and soul of being able to execute the assembly. I can post code on how to get the compiled assembly if anyone's interested, but there's how, once you have a compiled assembly (in my code the instance is named "asm"), then you can get the method and execute it like so. I'm actually storing all of the compiled assemblies in a hash table, with the key of the hashtable being the name of the script. When someone wants to execute a script, my main script manager looks to see if that script is loaded, if it isn't, it goes out to the database, grabs it, compiles it, and then uses that and executes it. If the script has already been loaded, it just uses the existing instance that was already saved in the hashtable and goes from there. I *think* that's probably what you were asking. My next step is to have it check to see if the script changed in the database, so that if you change the script, it will reload it if possible. The other option is to create a "build" verb, so that you can rebuild scripts from within and get your error messages right there in the console. If the new version doesn't compile, the new assembly shouldn't replace the old one that does work. Ideally, it will continue to use the old assembly until the new one is built, at which point it will use the new one and forget the old one. In Christ, [This message has been edited by HanClinto (edited May 20, 2005).] |
|||
CoolJ![]() Member Posts: 354 From: ny Registered: 07-11-2004 |
That's brilliant! Thanks for explaining it. I'll have to look at the C# api to understand it completely. Just a thought, but I guess it could be possible to allow admins to add elements to the game from the MUD command prompt. Something like: That would be cool, an admin could be walking around the world and go, hmmm..this room needs something..I think I'll add a tree! |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
quote: Yup! That's totally the idea! I'm just chilling out at a coffeeshop tonight, and I'll be working on it. Looking forward to getting more stuff done on it! In Christ, ------------------ |
|||
oneinch Junior Member Posts: 1 From: Registered: 07-31-2007 |
There is a Christian MUD out there called Ark of the Covenant. Found at http://www.aotcmud.org the server aotcmud.org:4200 |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Hey oneinch -- welcome to CCN! I've played a bit of AoTC -- it seems like a neat MUD. It's got a lot of leftovers from its ROM codebase, but on the whole it seems like a neat community of players. Welcome to our forums! In Christ, |
|||
samw3![]() Member Posts: 542 From: Toccoa, GA, USA Registered: 08-15-2006 |
Clint, This is a very interesting approach. I amazed to see that .NET includes a compiler as a part of the reflection system. If you are looking for telnet terminal control sequences (color, cursor positioning, etc.) Here is a quick synopsis. Have you thought about how the server will scale? It seems like all these dynamic hits and compiles will flat-line your server pretty quick. What actually does results.CompiledAssembly return? An object, anonymous function? If so, maybe create a action cache that will only recompile if the script has changed. Maybe write an associated md5 when posting the script to the db. Anyways, just some ideas for you. Keep up the good work. God Bless! ------------------ |
|||
HanClinto![]() Administrator Posts: 1828 From: Indiana Registered: 10-11-2004 |
Hey, Sam!
quote: Yeah, isn't it cool? ![]() I found out about this from a project we were working on at work, where we do a similar thing to provide dynamic code generation for user-customizable forms.
quote: Perhaps this will be answered by my response to the next paragraph.
quote: This was all originally done in C# 1.1, before anonymous methods came about. results.CompiledAssembly returns a pointer to an assembly. DLLs and EXEs are kinds of assemblies -- basically what I'm doing is dynamically compiling code into a DLL stored in memory, which I then link into. I cache those assemblies in memory, and so long as the program is running, I only recompile those assemblies if the scripts for them change. If the script breaks and doesn't compile, I still have the compiled binary in memory that I can load up until you work out the compile errors in the script. So the performance hits on the scripts are negligible -- aside from the reflection needed to get the method, it's just as fast as calling normal compiled C# code. Thanks for the feedback! BTW, the code has been heavily modified since the original 2005 post -- RifleFire and I put it up on SourceForge, so if you're curious, you're welcome to peruse all of the code in the SVN tree -- for instance, here's miteScript.cs -- one of the files that handles some of the nitty gritty stuff regarding run-time code comilation/linking. In Christ, |
|||
samw3![]() Member Posts: 542 From: Toccoa, GA, USA Registered: 08-15-2006 |
I didn't even realize this thread was resurrected. I thought this was something you were active on. I'm not active in C# as of late, but I'll tuck that info away for a rainy day. Strangely enough, I think your first post's sentence about not enough "hardcore" programming discussion still stands as of late. Still, reading through this stale thread was kind of refreshing. hmm. God Bless! ------------------ |