4

I'm trying my hand at building a MUD (multiplayer interactive-fiction game)

I'm in the design/conceptualizing phase and I've run into a problem that I can't come up with a solution for. I'm hoping some more experienced programmers will have some advice.

Here's the problem as best I can explain it. When the player decides to perform an action he sends a command to the server. the server then processes the command, determines whether or not the action can be performed, and either does it or responds with a reason as to why it could not be done. One reason that an action might fail is that the player is busy doing something else. For instance, if a player is mid-fight and has just swung a massive broadsword, it might take 3 seconds before he can repeat this action. If the player attempts to swing again to soon, the game will respond indicating that he must wait x seconds before doing that. Now, this I can probably design without much trouble. The problem I'm having is how I can replicate this behavior from AI creatures. All of the events that are being performed by the server ON ITS OWN, aka not as an immediate reaction to something a player has done, will have to be time sensitive. Some evil monster has cast a spell on you but must wait 30 seconds before doing it again... I think I'll probably be adding all these events to some kind of event queue, but how can I make that event queue time sensitive?

dash-tom-bang
  • 17,383
  • 5
  • 46
  • 62
mudder
  • 167
  • 2
  • 9

7 Answers7

4

MUDs actions are usually performed on 'ticks' rather than immediately - this allows for limited affect of latency and for monster's commands to be inserted in the queue and processed fairly.

Personally, I don't like this approach, but pretty much 99% of MUDs use it. You need to design a robust command queue & event queue which can handle both AI and user commands. You can then add "virtual latency" to AI commands which may be predefined or an average of all users latency, or whatever you like.

3

AI's are clients.

They're "part of the server" only in the most distant view. They're actually outside the main game engine. They're specialized clients with no human being.

An AI client has the same interface with the server that a human's client would.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 1
    That isn't how they should be implemented, it would cause huge and unecessery overheads. He means AI in the sense of NPC AI, they are part of the server. –  May 01 '10 at 18:38
  • AI makes me think to an actual AI that acts like a player through a player-like interface, like S.Lott says. I guess the OP wanted to say "mobs". – o0'. May 04 '10 at 09:51
  • 1
    @Steven Jackson: "Huge Overheads"? Really? How so? The computation must be done anyway, so it can't involve any more CPU. The connection can be a "localhost" network connection which is simply a shared buffer. What overheads are you talking about? Can you be specific? I can't see any. What am I missing? – S.Lott May 04 '10 at 11:26
  • 2
    Huge is relative. I don't mean the computation itself, you'd be converting logic into human-readable text just to be converted into logic again - why? You're proxying the AI through the network code just to be executed where it should be in the first place. Lo'oris you are correct that the OP should have specified mobs. It's not unheard of for a MUD to have 10,000+ mobs (NPCs). –  May 04 '10 at 12:11
  • 1
    @Steven Jackson: "human-readable text"? Why? Why not calls from the AI to the same methods the human interface uses after handling GUI's and parsing and all that front-end stuff an AI doesn't need. This is the Web Services philosophy. Human front-end and web services use the same underlying API's to do the same things. Why doesn't that apply here? – S.Lott May 04 '10 at 18:42
  • 1
    Because it's a MUD, unless stated otherwise you can assume it operates over Telnet and that all inputs are human readable. –  May 05 '10 at 02:57
  • 2
    @Steven Jackson: Why assume "it operates over Telnet and that all inputs are human readable"? That seems to be a fairly silly assumption. It makes the AI's harder to write (as evidenced by the question). Why not have a multi-layered architecture? Why assume a bad architecture when it's relatively easy to have an architecture that exposes a machine-friendly interface layer? – S.Lott May 05 '10 at 10:04
  • 1
    http://en.wikipedia.org/wiki/MUD this is the topic. Your AI/protocol suggestions make sense for a custom implementation, but I'm not assuming it's telnet based because I'm in love with the protocol. It's just how almost all MUDs operate. –  May 07 '10 at 16:14
  • 2
    @Steven Jackson: "It's just how almost all MUDs operate" While that may be true, how do you know it applies to this question? – S.Lott May 07 '10 at 17:56
2

Well your AI-controlled entities have some sort of "what am I going to do next?" method, right? Just have that method return "I'm busy, keep doing what I was doing" result while another action is underway.

E.g.

class ToughGuy(AI):
   Action_Idle, Action_BroadswordSwing, Action_CastingMagic = range(3)

   MagicRange = 10
   MagicTime = 8
   MeleeRange = 4
   MeleeTime = 2

   def __init__(self):
      self.action = ToughGuy.Action_Idle
      self.actiontimer = 0

   def Update(self, timestep):
      if self.actiontimer <= 0:
         self.action = ToughGuy.ActionIdle
      else
         self.actiontimer -= timestep

      if self.action == ToughGuy.Action_Idle:
         global player # don't do this
         if self.AmIFacing(player):
            distance = DistanceBetween(self, player)
            if distance < ToughGuy.MeleeRange:
               self.action = ToughGuy.Action_BroadswordSwing
               self.actiontimer = ToughGuy.MeleeTime
            elif distance < ToughGuy.MagicRange:
               self.action = ToughGuy.Action_CastingMagic
               self.actiontimer = ToughGuy.MagicTime

etc. Sorry for the variable coding standards... ;)

dash-tom-bang
  • 17,383
  • 5
  • 46
  • 62
2

You could use threads to handle specific types of Mobs, and put all the instances into an array of some sort. Then, the thread simply goes through the list repeatedly applying logic. DelayTimeStart and Delay could be attributes of the parent Mob class, and when the thread goes through the loop, it can put off processing any instances of the Mob in which there is time remaining in the delay.

Diginess
  • 21
  • 1
0

I'll provide you an answer from an LPMud/LDMud point of view.

Every player in the MUD is an instance of player.c. Player.c inherits from living.c. Things that are living have a heartbeat. The heartbeat function is processed once every 2 seconds for every living object on the mud (or anything that has a heartbeat() function). Timing events is not typically done based on seconds, but instead based on the number of heartbeats that have elapsed through counters within the object.

The wonderful news that applies to your question is that monsters also inherit from living.c. This means that if you had actions that can only be performed periodically this can all be managed within the heartbeat based on the number of ticks that have occurred.

David Hoelzer
  • 15,862
  • 4
  • 48
  • 67
0

A basic approach would be to have a data structure representing the evil monster's instance of its spell, with a cooldown timer on it. When the power is used, the cooldown is set (presumably to a number of seconds defined by the master definition of the spell); an attempt to use it again will fail just like with the player ability. This can be integrated with an event queue by having the queue check cooldown, and if it hasn't expired, to wait for it to expire, or abort the action, or reschedule the action, as appropriate.

chaos
  • 122,029
  • 33
  • 303
  • 309