Advertisement

Basics of MMO event / game loop wanted

Started by August 07, 2017 07:20 AM
17 comments, last by Miraz 7 years, 3 months ago

State machines quickly become burdensome if you hardcode each case in a giant case statement. So you need state-handlers, which load the user's current state for a given quest or conversation id, and handle the transition to the next state.

Each game tick (or time round the game loop, or event) you pass any queued or incoming state change requests to the relevant state-handler. So when in the client you click an option, your client will call the server API with the id of the spoken-to NPC, and the id of the selected conversation item.

The server will validate that the player is  currently conversing with the NPC, and that they are at a state where that selected conversation item was available, then process that selected conversation item (displaying a response, giving items, etc), then update their stored state.

6 minutes ago, DewiMorgan said:

State machines quickly become burdensome if you hardcode each case in a giant case statement. So you need state-handlers, which load the user's current state for a given quest or conversation id, and handle the transition to the next state.

Each game tick (or time round the game loop, or event) you pass any queued or incoming state change requests to the relevant state-handler. So when in the client you click an option, your client will call the server API with the id of the spoken-to NPC, and the id of the selected conversation item.

The server will validate that the player is  currently conversing with the NPC, and that they are at a state where that selected conversation item was available, then process that selected conversation item (displaying a response, giving items, etc), then update their stored state.

Is there any "code" example of such handler ? I'm currently reading and working with state machine sample and i would love to see how others are implemented it.

On 7.8.2017 at 6:25 PM, hplus0603 said:

Typically, you'll run all world updates in a single thread, so that you don't need to implement locking. It's very uncommon for a full world/zone to be so complex as to overwhelm a single core, unless you also do physics simulation, which may need a multi-threaded implementation.

There is one difference to single player games! In a single-player game, you will have two entities that matter: The player, and the NPC. If the player says something to anger the NPC, the NPC may change its own state to "angry," start to attack, and so forth; this state can easily be kept on the NPC.

In a multi-player game, there are actually three things that matter: Each player, each NPC, and each relation between specific players and specific NPCs. This separate player/NPC relation is actually a different "thing," and you'd probably do best to keep it separate in the game, although you'd want to store these states keyed on the player in your database.

So, the scripting callback from the player talking to the NPC might contain five arguments:

 



def onInteraction(self, world, playerInteraction, playerRelation, player):
    if player.faction != self.faction:
        self.tell(player, "You are not my kind!")
    elif playerRelation.state == None:
        self.sendDialog(player, self.initialDialogTree)
    elif playerRelation.state == ON_FETCH_QUEST:
        if (playerInteraction.action == OFFER_ITEM and playerInteraction.item == SMALL_GRAY_ROCK):
            self.tell(player, "This is great! Here is your reward!")
            player.addInventory(FLAMING_SWORD_OF_FIRE, 1, self)
            player.removeInventory(SMALL_GRAY_ROCK, 1, self)
        elif ...

 

 

This is actual event you are talking about, OnInteract, right ? Does your clients subscribe to NPC or in larger event manager thing ?

Advertisement

This is the event handler you would write for the NPC on the server.

Presumably, the client already sees NPCs in their vincinity on their clients, and can interact with them, which would send these kinds of interaction events to the server. The server would then run sanity checks ("can player see NPC, is the distance not too large, is there no locked door in between, is NPC alive, is player alive, ...") and forward the event to the NPC in question.

Building the infrastructure that actually allows these kinds of events to be forwarded with the correct context, is one piece that sets an online RPG apart from most other kinds of games; there are more fiddly bits of state here than in "I put this bullet into the world and woe unto anyone in its path!"

 

enum Bool { True, False, FileNotFound };

That is what i'm ultimately asking, i need to write handlers after all. I will try to look information from google for it. Thank you.

I still think there's a risk of making this whole process sound more complex and mystical than it really is. If the player wants to do something to an NPC, such as speak to them, they call a function which can query the current state of the NPC, and respond to the player accordingly. The only things that have to differ when your game is networked is that the function calls go across the network and need checking for correctness - i.e. is this player actually able to interact with the NPC?

The key to making this manageable is understanding that the process of getting messages from a client to a server has nothing really to do with the process of handling game logic and interactions on that server. Once the message is received from the client, you can call OnInteract the same way you would on a single player game. And once the server decides what the result of that interaction is, it tells the client to display something, the same way that it would on a single player game (but that eventually goes via the network).

If it helps:


client input   -> convert input to net message  -> .... internet ... -> net message to game logic request -> server game logic
                                                                                                                     |
                                                                                                                     v 
client display <- convert net message to output -> .... internet ... <- state change to net message <- game state changes
                                                                      \ game event to net message   <- game event notifications

If you keep clear separations between input, networking, game logic, and output, then things stay simple, providing you remember that all actions are asynchronous.

3 hours ago, Kylotan said:

I still think there's a risk of making this whole process sound more complex and mystical than it really is. If the player wants to do something to an NPC, such as speak to them, they call a function which can query the current state of the NPC, and respond to the player accordingly. The only things that have to differ when your game is networked is that the function calls go across the network and need checking for correctness - i.e. is this player actually able to interact with the NPC?

The key to making this manageable is understanding that the process of getting messages from a client to a server has nothing really to do with the process of handling game logic and interactions on that server. Once the message is received from the client, you can call OnInteract the same way you would on a single player game. And once the server decides what the result of that interaction is, it tells the client to display something, the same way that it would on a single player game (but that eventually goes via the network).

If it helps:



client input   -> convert input to net message  -> .... internet ... -> net message to game logic request -> server game logic
                                                                                                                     |
                                                                                                                     v 
client display <- convert net message to output -> .... internet ... <- state change to net message <- game state changes
                                                                      \ game event to net message   <- game event notifications

If you keep clear separations between input, networking, game logic, and output, then things stay simple, providing you remember that all actions are asynchronous.

That actually is best answer so far ! You are right when you say it's "more complex and mystical than it really is" because i know it's simple but it takes some little steps before you get it right. Now i know what i'm gonna do ! Thank you for this lesson !

Advertisement

The only things that have to differ when your game is networked is that the function calls go across the network and need checking for correctness

My point is that there is also a separate piece of state for multiplayer RPGs! It is in fact not the same. Specifically, the NPC may have state, and the player may have state, but there is also state in the relation between player and NPC that is different between each player and the same NPC. In a single-player RPG, you'll almost always just tack this state onto the NPC themselves, but that doesn't work for multiplayer RPGs.

So, the pieces are:

  1. Client can see NPCs and the world, because you've arranged for the world entities and changes to them to be streamed to the clients for rendering.
  2. NPCs have some general properties known to clients, such as what they look like, where they are, and what kind of interactions they may accept (giving things, talking, etc.)
  3. Client chooses to make an interaction with NPC; this is all client-side, based on information client has. Let's say the interaction is "say 'hello'"
  4. Client sends interaction to server: "client X does interaction say,hello to NPC Y"
  5. Server receives interaction, and verifies that client is near NPC Y and such.
  6. Server retrieves the state (or creates new state if this is the first time) for the relation between this particular player and this particular NPC (<-- this is the different bit!)
  7. Server forwards the interaction event to the NPC handler. (Depending on how you write the script, you may need NPC state, player state, world state, player/NPC relation state, and data from the specific interaction event)
  8. NPC script runs whatever logic it wants, which may generate world-visible events, may send chat to the player-only, may modify inventories, and so forth.
  9. Server saves the outcome of the interaction as modifications to player, NPC, and player/NPC relation objects.
  10. Server sends outcomes of interaction back to player. This may be an explicit response message, but more likely, the client just sees the effects because of the side effects from running the NPC interaction script -- "an item was added to your inventory by Y" and "Y says 'here you go'" and "door object 22 opens" that the client knows how to render no matter how those actions were initiated. (See step 1 !)

Each of these bullets need support from the game engine, and while you could get 1, 2, 4, and 10 out of a typical multiplayer library (anything from Photon to Unreal Engine to whatever) you will generally have to build 3 and 5-9 specific to your game.

 

enum Bool { True, False, FileNotFound };

I'm programming own server so this information is very critical as i want to make sure i'm implementing stuff the right way. Awesome stuff above this comment :)

This topic is closed to new replies.

Advertisement