4
votes

I have built a multi player game(to be exact, 4 players) using the Message passing construct of erlang. I have followed the tictactoe game on the following link as a example but what really is similar is the Message passing construct as shown in the game:link

I then chose to run this game on ejabberd Multi user Chatroom, I did write a ejabberd hook for this. But if you look at the NewGameState in the file tictactoe.erl on the above link, you will find that there is no way of retrieving it out in a variable.

So I used mnesia and wrote each new gamestate generated to this mnesia table. Now Inside my ejabberd hook I call my game function (i.e on each call a series of modules -> "gen_server, game_modules,mnesia_modules" are executed) and inside the hook just below the call of game function I am reading from mnesia table for the gamestate as follows(here the function myMessage is the function inside ejabberd hook):

myMessage({#message = Msg, C2SState})->
    some_other_module:game_func(Args),
    State=mnesia_module:read(key),

    {Msg, C2SState};
myMessage(Acc) ->
    Acc.

Now my problem is that the read operation is giving me a empty table when the order of execution is

some_other_module:game_func(Args),
 GameState=mnesia_module:read(key),

and when I insert a delay between these two lines as timer:sleep/1 as below(the value 200 is chosen randomly after some trial with different values):

some_other_module:game_func(Args),
timer:sleep(200)
 GameState=mnesia_module:read(key),

I am getting the correct value of GameState thus suggesting me that the reading operation in line

GameState=mnesia_module:read(key),

is getting performed/executed before the line some_other_module:game_func(Args) (which is a series of modules -> "gen_server, game_modules,mnesia_modules") is able to execute the mnesia modules and write the GameState into mnesia table.

How can I resolve this issue as I don't want to use timer:sleep/1 as it is not dependable solution.

Can anyone suggest me a work around here.What I mean is can anyone suggest me a way to retrieve the GameState inside the hook by any other means other than mnesia so I do not have a race condition at all.

Or either there is some way that ejabberd provides some functionality that I can use here?

Thanks in advance.

2
Are you using mnesia:dirty_* functions ?Pouriya
no @Pouriya I am using mnesia:write/1 to write the data while some_other_module:game_func(Args) is running and mnesia:read/3 function to read the data when GameState=mnesia_module:read(key) is called.abhishek ranjan
Can you show the code ?Pouriya
Most of the code is on the same line as given on the link.What is different is that in the link,inside tictactoe.erl file you will find a NewgameState after which I call the mnesia:write/1 and store it with the current Player id as key.abhishek ranjan

2 Answers

5
votes

Realtime consistency of data between distributed nodes is a hard problem and you're going to need to tailor your solution to your needs. You don't say what kind of transaction you're using with mnesia, so maybe that would solve your problems.

However, here's a simple solution that may help you think about the problem:

First, let's call one of your nodes master. On the master node, start a gen_server which handles the game-state. Now, anyone who wants to read or write game-state needs to make an rpc:call/4 to the master node (unless they're already there) into a gen_server:call/2. Now all interaction with the game state is synchronous.

If you update the game state no more than a few times a second, this solution should work quite well for you. If games are independent, then each game is a different gen_server.

1
votes

I am trying to give the solution that worked for me. Hope it helps someone.

Here is what I did:

First I removed mnesia from the picture.

I first registered the Pid of the base Module as soon as it is created inside the start/2 function(you can think of tictactoe.erl present on the link provided in the question) and then I created a get_gs/0 function inside that module only to retrieve the GameState as follows(server is the alias I have used to register the Pid):

get_gs()->
    server ! {get_gs, self()},
     receive
        GameState ->
            GameState
    end.

And then Inside the loop() function I have:

{ get_gs, From } ->
           From ! GameState,

           loop(FirstPlayer, SecondPlayer, CurrentPlayer, GameState)

Then created a module implementing gen_server architecture and called a function in the following order(where ->represents function calls like A->B means From A i call B):

My custom hook on ejabberd->gen_server based module->gameclient:get_gs/0->gameserver:get_gs/0->tictactoe:get_gs/0

And I got the current GameState.

Thank you @Nathaniel Waisbrot for your valuable suggestion.