[Home] [Downloads] [Search] [Help/forum]

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  MUDs
. -> [Folder]  General
. . -> [Subject]  Tiny MUD Server - version 2
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Tiny MUD Server - version 2

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1 2  3  4  5  6  7  8  

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Tue 27 Jul 2004 12:31 AM (UTC)

Amended on Thu 13 Oct 2005 01:22 AM (UTC) by Nick Gammon

Message

I am pleased to release an improved version of the "tiny MUD server" which I had previously released a year ago. This has compiled without errors or warnings using g++ on:

  • Linux
  • Cygwin (Windows)
  • Macintosh OS/X

This program is only a simple example (under 1500 lines of code in a single source file), however if you are interested in seeing how a server works, or wanting to write your own, you could use this as a basis for it.

The reason I wrote it is that sometimes I want to see how to make a simple server, but the main 'established' servers tend to be thousands of lines of code, and it is hard to see in the middle of all that code what is essential to writing a server, and what is an add-in.

The code is Copyright 2004 by myself, however you are permitted to copy, use, modify, sell and distribute it provided this copyright notice appears in all copies. This gets around the problem that people have by deriving a server from Diku or other MUDs which have restrictive licenses on them (eg. no pay MUDs). The Copyright notice is simply to stop people taking the code and adding their own, more restrictive, copyright to it.

Download it from http://www.gammon.com.au/files/muds/tinymudserver_v2.tgz (16 Kb) or http://www.gammon.com.au/files/muds/tinymudserver_v2.zip (18 Kb).

Description

This program demonstrates a simple MUD (Multi-User Dungeon) server - in a single file.

It does the following:

  • Accepts multiple connections from players
  • Maintains a list of connected players
  • Asks players for a name and password
  • Saves player files to disk (name, password, current room, player flags)
  • Implements the commands: quit, look, say, tell, help, goto, transfer, shutdown, setflag, clearflag
  • Implements movement commands (eg. n, s, e, w)
  • Illustrates sending messages to a single player (eg. a tell) or all players (eg. a say)
  • Handles players disconnecting or quitting
  • Illustrates a "connection dialog" - players get asked their name, then their password.
  • Allows new players to create a character by specifying a name and password.
  • Demonstrates using the Standard Template Library for lists, strings, vectors, maps and sets.
  • Illustrates periodic messages using a timer (at present it just shows a message every few seconds)
  • Illustrates rudimentary player control (eg. gagging players from talking)
  • Loads room descriptions and exits from a disk file
  • Loads messages from a disk file
  • Loads control parameters from a disk file

What you could add

As it stands the program is too simple to be used for a full MUD, however it could be the basis for writing your own. You would want to add things like this:

  • Objects (eg. swords), taking/dropping things, etc.
  • Fighting (if required)
  • Building/extending online
  • Logging events (eg. connections, disconnections, faults)
  • Colour
  • MCCP (compression)
  • MXP (MUD Extension Protocol)
  • Telnet negotiation

Easy to modify

The code has been designed to allow for easy additions, as a lot of "helper" routines have already been written. In the following posts I will describe how to add extra commands, to show the general idea.


Example session

Initial connection


Welcome to the Tiny MUD Server version 2.0.0

|-------------------- Tiny MUD Server --------------------|
|
| Written by Nick Gammon.
|
| July 2004.
|
| http://www.gammon.com.au/
|
| Welcome to tinymudserver version 2! Have fun. :)
|---------------------------------------------------------|

Enter your name, or 'new' to create a new character ...  

Creating a new player


Enter your name, or 'new' to create a new character ...  
new
Please choose a name for your new character ... 
Magnum
Choose a password for Magnum ... 
swordfish
Re-enter password to confirm it ... 
swordfish
Welcome, Magnum



Welcome to our MUD! 
Please read the help files to become familiar with our rules. :)


Message Of The Day (MOTD)

Here is where you place announcements to be given to people 
once they have joined the game.

Starting room (room 1000).
You are standing in a fabulous, fabulous, exotic room, with 
all sorts of things that go 'hum' and 'plink' in the night.

As you glance around the room, you wonder what else might exist in this world.
Exits: e n s w 
> You hear creepy noises ...

Connecting to an existing player


Enter your name, or 'new' to create a new character ...  
magnum
Enter your password ... 
swordfish
Welcome, Magnum



Welcome back! We hope you enjoy playing today.


Message Of The Day (MOTD)

Here is where you place announcements to be given to people 
once they have joined the game.

Starting room (room 1000).
You are standing in a fabulous, fabulous, exotic room, with 
all sorts of things that go 'hum' and 'plink' in the night.

As you glance around the room, you wonder what else might 
exist in this world.
Exits: e n s w 
> You hear creepy noises ...

Moving and talking


n
You go n
Room 1004.
This is the training room. One day you might receive all 
sorts of interesting training here. 
Perhaps you might even purchase shields and armour. But not today.
Exits: s 
You also see Nick.
> 
say hi there
You say, "hi there"
> 

The room descriptions and exits have been read from a rooms.txt file. Most of the messages are in a messages.txt file (eg. the message-of-the-day).


- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #1 on Tue 27 Jul 2004 12:50 AM (UTC)

Amended on Tue 27 Jul 2004 06:19 AM (UTC) by Nick Gammon

Message
Adding to the MUD

Adding commands is easy. Basically you add a "command handler", and add the command to a "command map". Here is an example, of adding the "save" command.

First, write the command handler. This is easy in this case because the save routine is already written (it automatically saves players when they disconnect).


void DoSave  (tPlayer * p, istream & sArgs)
{
  p->Save ();
  *p << "Saved.\n";  
}


We place this routine somewhere logical, like after DoSay or in a similar place.

Each command handler is passed the current player (the one who typed the command), and the arguments that player typed, as an input stream. In this case we don't use the arguments.

Using the operator << sends a message to that player.

Finally we add the save command to the command map in the LoadThings function, so it recognises the word "save", like this:


 commandmap ["setflag"]  = DoSetFlag;
 commandmap ["clearflag"]= DoClearFlag;
 commandmap ["save"]     = DoSave; // <-- add this here





Another example, chatting

To chat, we need to process the command argument (to find what to chat), and send that to all players. We'll also illustrate using a player flag. You can set any number of flags on players, you either need a flag set (eg. can_shutdown) to permit an action, if you want, or not set (eg. gagged) to inhibit an action.

First we will test the "gagged" flag, so that gagged players cannot chat. This will throw an exception if the player cannot chat.

Next, we use the input stream "sArgs" to find *what* to chat. This also throws an exception if nothing is there, the message "Chat what?" is then sent to the player.

If we pass both tests we use SendToAll to send the chat message to all connected players.


void DoChat (tPlayer * p, istream & sArgs)
{
  p->NeedNoFlag ("gagged"); // can't if gagged
  string what = GetMessage (sArgs, "Chat what?");  // what  
  SendToAll (p->playername + " chats, \"" + what + "\"\n");  // chat it
}


Then we add the new command to the command map also:


  commandmap ["save"]     = DoSave;      // save a player
  commandmap ["chat"]     = DoChat;      // chat

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Tue 27 Jul 2004 01:06 AM (UTC)

Amended on Tue 27 Jul 2004 01:29 AM (UTC) by Nick Gammon

Message
Another new command - emote

The emote command is used to show emotions or actions, eg.


emote sits down
Nick sits down.


This is shown to everyone in the current room.

To do this we will use a variation of the SendToAll routine, which lets you specify which room to send to. By specifying the current player's room, only the people in that room will see it:


void DoEmote (tPlayer * p, istream & sArgs)
{
  string what = GetMessage (sArgs, "Emote what?");  // what  
  SendToAll (p->playername + " " + what + "\n", 0, p->room);  // emote it
}


Once again we add the new command to the command map:


 commandmap ["save"]     = DoSave;      // save a player
 commandmap ["chat"]     = DoChat;      // chat
 commandmap ["emote"]    = DoEmote;     // emote

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #3 on Tue 27 Jul 2004 01:27 AM (UTC)

Amended on Tue 27 Jul 2004 03:25 AM (UTC) by Nick Gammon

Message
A more complex command - who

Now I'll illustrate a more complex command - the "who" list. This is more complicated because we need to iterate over the connected player list, showing each one to the one who requested it. There are a number of ways you could to it, but I'll choose to use a functor combined with the for_each STL algorithm.


// functor for doing who list
struct whoList
{
  tPlayer * sendto;
  int count;
 
  // ctor
  whoList (tPlayer * p) : sendto (p), count (0) {}
  // who list item
  void operator() (tPlayer * p) 
    {
    if (p->IsPlaying ())
      {
      *sendto << "  " << p->playername << " in room " << p->room << "\n";
      ++count;
      }
    } // end of operator() 
  int GetCount () const { return count; }
  
};  // end of whoList

void DoWho (tPlayer * p, istream & sArgs)
{
  NoMore (p, sArgs);  // check no more input
  *p << "Connected players ...\n";
  *p << for_each (playerlist.begin (), playerlist.end (), 
                  whoList (p)).GetCount () 
     << " player(s)\n";  
} // end of DoWho


This also uses NoMore to verify that the player didn't type something after "who" (like "who nick").

The for_each algorithm walks the entire player list, passing each item in it to the whoList functor. The constructor for whoList remembers which player wants to see the who list, and then the operator() is repeatedly called for each player in the list. We use IsPlaying to check if the player in the list is playing yet (and not just entering their name and password, for example), and if so, we display their name and current room.

We also add one to a counter for each connected player. Finally the for_each algorithm returns a reference to the whoList function, which we use to call GetCount, to get at this counter, and display the count to the player, like this:


who
Connected players ...
  Nick in room 1000
  Magnum in room 1000
2 player(s)


The only remaining thing to do is to add the DoWho command to the command map ...


  commandmap ["emote"]    = DoEmote;     // emote
  commandmap ["who"]      = DoWho;       // who is on?




An alternative way of doing "who"

Below is an alternative implementation for DoWho. It is all in a single function, and might look a bit more "natural" if you are used to C-style loops ...


void DoWho (tPlayer * p, istream & sArgs)
{
  NoMore (p, sArgs);  // check no more input
  *p << "Connected players ...\n";
  
  int count = 0;
  for (tPlayerListIterator iter = playerlist.begin ();
       iter != playerlist.end ();
       ++iter)
    {
    tPlayer * pTarget = *iter;    // the player  
    if (pTarget->IsPlaying ())
      {
      *p << "  " << pTarget->playername << 
            " in room " << pTarget->room << "\n";
      ++count;
      } // end of if playing
    } // end of doing each player
  
  *p << count << " player(s)\n";  
} // end of DoWho

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Tue 27 Jul 2004 06:17 AM (UTC)

Amended on Thu 13 Oct 2005 01:24 AM (UTC) by Nick Gammon

Message

I have now done a slightly different version of the above server, this time broken up from a single source file into various .cpp and .h files, to make it easier to modify and expand. This has the functionality of the earlier version, plus the extra commands described above (save, chat, emote, who).

The purpose of the various files are:

.cpp files

  • commands.cpp - commands handler (eg. DoQuit, DoLook etc.)
  • comms.cpp - communications stuff - handle new connections, read and write to connected players
  • globals.cpp - global data (player list, room list etc.)
  • load.cpp - load things (rooms, messages, control file)
  • player.cpp - player-related functions (load, save, read, write)
  • room.cpp - room-related functions
  • states.cpp - connection states (get player name, password etc.)
  • strings.cpp - string utilities (find-and-replace, trim spaces etc.)
  • tinymudserver.cpp - main program

.h files

  • constants.h - various constants (prompt, version, port etc.)
  • globals.h - external declarations for global data (player list, etc.)
  • player.h - player class
  • room.h - room class
  • strings.h - string utilites declarations
  • utils.h - assorted other utilities

There is also an enhanced Makefile which correctly compiles the various .cpp files if you type "make". It also has dependency code in it, so if you change a .h file, the appropriate .cpp files will be automatically recompiled.

Download it from http://www.gammon.com.au/files/muds/tinymudserver_v2_2.tgz (18 Kb).

The total number of lines has increased to 1863, partly because of some duplication (multiple #include directives for the same file, in different source files), and partly because of the extra commands which have been added. There is also an author and copyright notice at the start of each file, which adds about 13 lines per file. The breakdown for each file is:


    354    1632   11336 commands.cpp
    335    1235    9403 comms.cpp
     41     170    1229 globals.cpp
    154     566    4128 load.cpp
    236     988    6544 player.cpp
     33     109     816 room.cpp
    203     790    6206 states.cpp
    118     493    3278 strings.cpp
     67     230    1603 tinymudserver.cpp
     28     192    1463 constants.h
     28     125     978 globals.h
    133     641    4528 player.h
     28      87     615 room.h
     71     316    2288 strings.h
     34     146     929 utils.h
   1863    7720   55344 total

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Tue 27 Jul 2004 07:09 AM (UTC)

Amended on Tue 27 Jul 2004 07:16 AM (UTC) by Nick Gammon

Message
Adding more questions for new players

The next step will be an illustration of how to add more questions for newly-created players. For instance, what class they want (eg. mage, warrior etc.).



Step 1 - add a list of classes

In globals.cpp, add this:


// classes
set<std::string, ciLess> classset;


In globals.h, add this:


// classes
extern std::set<std::string, ciLess> classset;




Step 2 - read the available classes from the control file

In load.cpp, add this:


  LoadSet (fControl, blockedIP);    // blocked IP addresses
  LoadSet (fControl, classset);     // class names




Step 3 - add the new connection state

In player.h, add a new connection state:


  eAwaitingNewName,     // they have typed 'new'    
  eAwaitingNewPassword, // we want a new password
  eConfirmPassword,     // confirm the new password
  eAwaitingClass,       // we want the class name




Step 4 - add the player "class" to the player class

In player.h, add the new variable to the player:


  bool closing;     // true if they are about to leave us
  std::set<string, ciLess> flags;  // player flags
  string playerclass;   // what class the player is


Note that we cannot call it "class" because that is a reserved word in C++.



Step 5 - process the new connection state

In states.cpp, we need a new include file:


#include <stdexcept>
#include <fstream>
#include <iostream>
#include <iterator>


In states.cpp, in the function LoadState, we need to add the new state to the state map:


  statemap [eAwaitingNewName]     = ProcessNewPlayerName; // new player
  statemap [eAwaitingNewPassword] = ProcessNewPassword;
  statemap [eConfirmPassword]     = ProcessConfirmPassword;
  statemap [eAwaitingClass]       = ProcessClass;


In states.cpp, once they have confirmed their password, they are not playing yet, so in function ProcessConfirmPassword change:


 // New player now in the game
 PlayerEnteredGame (p, messagemap ["new_player"]);


to:


  // now get their class
  p->connstate = eAwaitingClass;
  ostringstream os;
  copy (classset.begin (), classset.end (), 
        ostream_iterator<string> (os, " "));
  p->prompt = MAKE_STRING ("Classes are: " << os.str () 
              << "\nPlease choose a class ... ");


This sets the player "state" to "awaiting class", and then uses a "copy" to create a list of available classes. The prompt is changed to display that, and the player is asked for their class.

Finally, we add the handler for the eAwaitingClass state:


void ProcessClass (tPlayer * p, istream & sArgs)
{
   string playerclass;
   sArgs >> playerclass;
  
  if (playerclass.empty ())
    return;   // can't be empty, this will make it reprompt for it
  
  if (classset.find (playerclass) == classset.end ())
    throw runtime_error ("That class is not in the list of classes.");
  
  p->playerclass = playerclass;
  
  // New player now in the game
  PlayerEnteredGame (p, messagemap ["new_player"]);
         
} /* end of ProcessClass */


This checks that the entered class is not empty, and is in the valid list. If so, it stores it in the player's playerclass field, and lets the player enter the game.



Last, but not least, we enter the list of classes in the control.txt file:


n s e w u d ne nw se sw enter leave
new god admin quit n s e w u d look me self
10.1.2.3
Mage Cleric Thief Warrior Vampire Druid Ranger


The four lines above are:


  1. Valid directions (n, s, e, w etc.)

  2. Disallowed new player names

  3. Banned IP addresses

  4. List of class names





This might look complicated, but it is only a few lines of code, and could be extended along similar lines to ask other questions. Now, to see it in action ...


Enter your name, or 'new' to create a new character ...  
new
Please choose a name for your new character ... 
Ceritram
Choose a password for Ceritram ... 
swordfish
Re-enter password to confirm it ... 
swordfish
Classes are: Cleric Druid Mage Ranger Thief Vampire Warrior 
Please choose a class ... 
blah
That class is not in the list of classes.
Classes are: Cleric Druid Mage Ranger Thief Vampire Warrior 
Please choose a class ... 
cleric
Welcome, Ceritram

Welcome to our MUD! 

... and so on ...

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #6 on Thu 13 Oct 2005 01:26 AM (UTC)
Message
Amended posts above because they contained ftp download URLs which are no longer supported, and replaced them by http ones instead.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Conap   (13 posts)  [Biography] bio
Date Reply #7 on Sat 26 Nov 2005 11:03 PM (UTC)
Message
Can you add an invisible flag ?
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Sun 27 Nov 2005 07:10 PM (UTC)
Message
You can do anything you want. This is an example of coding a MUD server. You need to understand how the code works and make such changes that you want.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Conap   (13 posts)  [Biography] bio
Date Reply #9 on Sun 18 Dec 2005 06:29 PM (UTC)

Amended on Sun 15 Jan 2006 01:01 AM (UTC) by Conap

Message
Hello ! I succeeded add some little command and colors, but how can I add an other %r like in the following code:


while (!(fMessages.eof ()))
{
string sMessageCode, sMessageText;
fMessages >> sMessageCode >> ws;
getline (fMessages, sMessageText);
if (!(sMessageCode.empty ())){
messagemap [tolower (sMessageCode)] =
FindAndReplace (sMessageText, "%r", "\n");
}


For exemple %yellow% replaced by "\33[1;32m"

Thank
[Go to top] top

Posted by Nick Gammon   Australia  (21,321 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Wed 21 Dec 2005 03:35 AM (UTC)
Message
Sure, you could do something like:


messagemap [tolower (sMessageCode)] =
  FindAndReplace (messagemap [tolower (sMessageCode)],
       "%yellow%", 
       "\33[1;32m");


However if you are planning to replace %red% by the code for red, remember that %r is already a newline.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Conap   (13 posts)  [Biography] bio
Date Reply #11 on Thu 22 Dec 2005 03:35 PM (UTC)
Message
Thank !
[Go to top] top

Posted by Conap   (13 posts)  [Biography] bio
Date Reply #12 on Thu 09 Feb 2006 11:46 PM (UTC)

Amended on Thu 09 Feb 2006 11:47 PM (UTC) by Conap

Message
I have added all that commands:

// commands
commandmap ["look"] = DoLook; // look around
commandmap ["l"] = DoLook; // synonymm for look
commandmap ["quit"] = DoQuit; // bye bye
commandmap ["say"] = DoSay; // say something
commandmap ["\""] = DoSay; // synonym for say
commandmap ["tell"] = DoTell; // tell someone
commandmap ["shutdown"] = DoShutdown; // shut MUD down
commandmap ["help"] = DoHelp; // show help message
commandmap ["goto"] = DoGoTo; // go to room
commandmap ["transfer"] = DoTransfer; // transfer someone else
commandmap ["setflag"] = DoSetFlag; // set a player's flag
commandmap ["clearflag"]= DoClearFlag; // clear a player's flag
commandmap ["shout"] = DoShout; // shout
commandmap ["emote"] = DoEmote; // emote
commandmap ["who"] = DoWho; // who
commandmap ["gecho"] = DoGecho; // gecho
commandmap ["echo"] = DoEcho; // echo
commandmap ["invisible"] = DoInvisible; //invisible
commandmap ["invis"] = DoInvisible; //invisible
commandmap ["board"] = DoBoard; //board
commandmap ["recall"] = DoRecall; //recall
commandmap ["reboot"] = DoReboot; //reboot
commandmap ["order"] = DoOrder; //order
commandmap ["gecho_blue"] = DoBlueGecho; //blue gecho
commandmap ["gecho_red"] = DoRedGecho; //red gecho
commandmap ["gecho_green"] = DoGreenGecho; //green gecho
commandmap ["gecho_yellow"] = DoYellowGecho; //yellow gecho
commandmap ["at"] = DoAt; // commande at
commandmap ["wizhelp"] = DoWizhelp; //commande wizhelp
commandmap ["title"] = DoTitle; // commande title
commandmap ["openportal"] = DoOpenPortal;
commandmap ["portal"] = DoPortal;
commandmap ["where"] = DoWhere;
commandmap ["teleport"] = DoTeleport;
commandmap ["news"] = DoNews;
commandmap ["kick"] = DoKick;
commandmap ["roomflag"] = DoRoomFlag;

(And colors)

But for the fight system ... the bots and the items ... I have NO idea

Please, help me :'(
[Go to top] top

Posted by David Haley   USA  (3,881 posts)  [Biography] bio
Date Reply #13 on Fri 10 Feb 2006 12:27 AM (UTC)
Message
Out of curiosity, is there a reason in particular why you're trying to build from nearly scratch, instead of using an existing code-base? Writing from scratch or from nearly scratch is very difficult, and should be undertaken only by fairly advanced programmers. After all, implementing the fight system, mobs and objects is about 75% of the work that goes into a MUD.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
[Go to top] top

Posted by Conap   (13 posts)  [Biography] bio
Date Reply #14 on Fri 10 Feb 2006 10:59 AM (UTC)
Message
Yes, I know ...
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


115,914 views.

This is page 1, subject is 8 pages long: 1 2  3  4  5  6  7  8  [Next page]

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]