简体   繁体   中英

How to stop socket.io emit from firing more then once

I am currently working on making an rpg online using node.js and socket.io. Everything was working fine until I started making a collision detection system. I looked online for a solution and found people that had similar issues but could not understand how to fix my issue. The following is my code.

client side:

function move_character(x){
        //socket.io start
        var collision = io.connect('/collision');
        collision.emit('detection', x);
        collision.on('col', function (msg) {
            if(msg == true) {
                if (x == 'up') {
                    $('#sprite').animate({'top': '-=50px'},150);
                } else if (x == 'down') {
                    $('#sprite').animate({'top': '+=50px'},150);
                } else if (x == 'left') {
                    $('#sprite').animate({'left': '-=50px'},150);
                } else if (x == 'right') {
                    $('#sprite').animate({'left': '+=50px'},150);
                }
            }
        });
        //socket.io end
    }

server side:

var collision = io.of('/collision');
collision.on('connection', function (socket) {
    socket.on('detection', function (x) {
        pool.getConnection(function (err, con) {
            con.query('SELECT class_state FROM gmaps LIMIT 1',
                function (err, result) {
                    if (err) throw err;
                    else {
                        var mapState = result[0].class_state.split(",");
                        var x_coord = 6;
                        var y_coord = 5;

                        var id = ((y_cord -1)*10)+(x_cord -1);
                        var index;
                        if(x == 'up'){
                            index = id-10;
                        }else if(x == 'down'){
                            index = id+10;
                        }else if(x == 'left'){
                            index = id-1;
                        }else if(x == 'right'){
                            index = id+1;
                        }
                        collision.emit('col', true);
                    }
                    });
                con.release();
            });
    });
});

On the server side the emit is always sending true and the coordinates are at a fixed point, I did that to help with the debugging. You may also find code that is not being used right now and I will be using it in the future but I want to fix this bug first.

My actual problem is that when I move the character the 1st time, everything works but the 2nd move does the 1st again followed by the 2nd with the 3rd doing the 1st, 2nd then 3rd move. By the the time I have moved the sprite 10 times, it is repeating the 1st 9 steps before step 10 and it is going all over the screen.

I did try to find the solution myself by changing the code and adding console.log's everywhere. I found that if I emit [true,x] on the server side and fix the client side appropriately, the sprite will move in the direction I want but n number of time with n being the number of moves I tried (1 for 1st move, 4 for 4th move in the same direction). Refreshing does set everything back to normal as it should. I also tried moving io.connect('/collision') outside of the function but that did not work either. It seems the problem is the emit on the server side is keeping a history of past data that has been sent and then resending it each time a call is made. It could also be the collision.on on the client side that is causing the bug.

Also, on the server side, what it does is checks the DB if the tile in question is able to be moved on to, if yes then true and if not then false. It is set to true again because of the bug. When the bug is gone I will finish the server side code.

Your issue is that every time you call move_character(x) , you create a new socket.io connection with it's associated event handlers. So, the first time you call it, it connects and sets up an event handler, does the emit('detection', x) , then sets up an event handler to listen for the response.

The second time you call move_character(x) , you create another socket.io connection and do the same. Now, when your server gets the message, it does some database work and then broadcasts the response to all connected sockets. So, now that you have two socket.io connections in the same browser window, each connection gets the response and it gets processed multiple times.

The third time, you set up yet another and so on...


To fix, socket.io works best if you create one socket.io connection and then use it for all your communication to that namespace. So, move the creation of the socket.io connection and the addition of the event handler outside of your move_character(x) function so it only happens once.

And on the server, I don't quite understand why you are broadcasting the response after your database work to ALL connected clients. I would think you would want to respond to only the connection that sent the query. You can do that by changing:

collision.emit('col', true);

to

socket.emit('col', true);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM