I'm using Nodejs with Socket.io to make a turn based game.
Each turn must end whenever the "Pass" button is pressed or if 5 seconds have passed after the turn began. Whichever happens first must trigger the "Pass" event.
My current counter just keeps on looping even if its not the current player turn, and in certain conditions it ends the turn prematurely.
I'm having issues on what logic should I use to have my turns work properly.
My client side has this functions
//Pressing "Pass" button
$('#pass_turn').on('click', function(){
socket.emit('pass_turn');
$('#your_turn').hide();
$('#not_turn').show();
});
//This runs everytime the server tells the player its their turn
socket.on('your_turn', function() {
$('#your_turn').show();
$('#not_turn').hide();
var counter = 0;
var interval = setInterval(function() {
counter++;
if (counter == 5) {
console.log('pass_turn');
clearInterval(interval);
$('#pass_turn').trigger("click");
}
}, 1000);
});
This is my server side code:
socket.on('pass_turn', function () {
var room = socket.rooms;
for(var item in socket.rooms){
if(item.substring(0, 7) == 'room_0.'){
//if conditions met, emit turn to opponent
socket.broadcast.to(item).emit('your_turn');
}
}
});
I implemented your logic in my way.
I have an array to store every connected players(socket)
This will be used to track which player is current turn and emit event to the player.
Server side
let players = [];
let current_turn = 0;
let timeOut;
let _turn = 0;
const MAX_WAITING = 5000;
function next_turn(){
_turn = current_turn++ % players.length;
players[_turn].emit('your_turn');
console.log("next turn triggered " , _turn);
triggerTimeout();
}
function triggerTimeout(){
timeOut = setTimeout(()=>{
next_turn();
},MAX_WAITING);
}
function resetTimeOut(){
if(typeof timeOut === 'object'){
console.log("timeout reset");
clearTimeout(timeOut);
}
}
io.on('connection', function(socket){
console.log('A player connected');
players.push(socket);
socket.on('pass_turn',function(){
if(players[_turn] == socket){
resetTimeOut();
next_turn();
}
})
socket.on('disconnect', function(){
console.log('A player disconnected');
players.splice(players.indexOf(socket),1);
_turn--;
console.log("A number of players now ",players.length);
});
});
After players are connected to server, I push them into the array, and only the game can be started with the first connected player's request by clicking a button in client side (you can modify this).
If you look at pass_turn event, only a current player can trigger events, which means other players are blocked for 5 seconds,
if any player is disconnected, we remove him from the array and decrease index as well.
next_turn function is triggered if 5 seconds are passed or the player presses a button in client side.
triggerTimeout function is used to count 5 seconds, after 5 seconds, it trigger next_turn function to turn next player.
resetTimeout function is triggered, when a current player is pressed a button in client side. time is reset and turn next player immediately
Client
<button type="button" id="turn" name="button">Next Turn</button>
$("#turn").on("click",function(e){
socket.emit('pass_turn');
})
Turn your server on, open 3~4 browsers and try it
Good luck :)
Related
I've been trying to take advantage of the Binance Websocket to trigger buys and sells based on the live price with my own logic. I know the WebSocket is sending back live data and there might be even 5-6 or more responses at the same time, but how do I make sure I get only 1 when I close the Websockets?
I am trying to make sure it will trigger only 1 buy or 1 sell when that happens.
I am using: (works great)
'node-binance-api'
The Websocket starts from here:
binance.websockets.bookTickers('BNBBUSD', (mainTicker) => {
var priceNow = mainTicker.bestBid;
liveStrategy(priceNow).then(isSold => {
// console.log('THIS IS WHEN IS CHECKED ->>>>>>');
if (isSold == true) {
console.log('JUST SOLD -> Setting the count to 10');
sleep(3000).then(() => {
console.log("Countdown finished: ", count);
profitCalculations(mainCurrency);
count = 10;
});
}
if (isSold == false) {
console.log('THIS WAS A BUY ->');
count = 10;
}
}).catch((isSoldErr) => {
console.warn('Error:', isSoldErr.message);
dumpError(isSoldErr);
});
});
The price is being sent back into the function, this function runs continuously until a buy or a sell happens. Before triggering a buy or a sell I close the WebSocket with:
let endpoints = binance.websockets.subscriptions();
for ( let endpoint in endpoints ) {
console.log("..terminating websocket: "+endpoint);
let ws = endpoints[endpoint];
ws.terminate();
}
This code stops the WebSocket, but it does take about 3 seconds so I trigger the buy/sell after this like:
if (priceNow > buyPrice) {
terminateAllWebsockets();
return new Promise((resolve) => {
sleep(5000).then(sleep => {
marketBuyFromNoLossStrategyAmount(buySymbol,buyAmount);
resolve(false);
})
})
}
When it returns it basically sends back to my liveStrategy(priceNow) to trigger the profit calculations. The profitCalculations(mainCurrency); might get triggered 3-4 times or even the buy or sell. Is it something that I can do for that not to happen? So the profit or the buy & sell will trigger only once? How do I make sure that happens with my setup?
Any thoughts are most welcome!
The main problem was that the WebSocket was real-time and when 2 or more trades were getting executed at the same time reaching the target set in the algorithm, it would trigger multi-buy or multi-sell. Writing the data within a public variable and reading with a simple timer that executed the function every second was the best solution.
I have this code here:
server:
io.emit('attack-damage', {
damage: damage,
attacker: username,
});
client:
socket.on('attack-damage', (data) => {
setTimeout(() => this.damageVisible = false, 1000);
});
What it does is when user clicks attack it emits and shows damage of the user that fades away after one second. Problem is that let's say if I click attack my damage is being shown but also if another player clicks attack then it's damage is being shown and mine damage fades out faster than after 1 second it just replaces the new damage from another player. How to show multiple damages on the screen and not just one ?
EDIT
socket.on('attack-damage', (data) => {
this.damage = data.damage;
this.aryDamage.push(data.damage, data.attacker);
if (!this.bolDamageRunning) {
if(this.aryDamage.length != 0) {
this.bolDamageRunning = true;
setTimeout(() => {
this.damageVisible = false;
this.aryDamage.splice(0,1);
this.bolDamageRunning = false;
}, 2000);
} else {
}
} else {
}
setTimeout(() => this.damageVisible = true, 2000);
When I use above code it makes appear damage after two seconds after the click. But also if I use two users, then old damage from screen is being replaced with new one
create a global array called aryDamage.
create a global boolean called bolDamageRunning.
client socket.on('attack-damage'), add a new element to the array, e.g.
aryDamage.push(data);
create a timer using setInterval to read the aryDamage every 100 ms.
function thisTimerCalledEvery100MS() {
// run Damage only when last Damage finish
if (!bolDamageRunning) {
// Check whether another damage waiting
if (aryDamage.length != 0) {
// Set running true
bolDamageRunning = true;
// call funDamage after 1 second
setTimeout(funDamage, 1000);
} else {
// No command waiting, do nothing
}
} else {
// Another command running, do nothing
}
}
function funDamage() {
// Your code to show damange, or
// do something with the first element of aryDamage
this.damageVisible = false;
// remove top element from aryDamage
aryDamage.splice(0,1);
// Set running = false
bolDamageRunning = false;
}
This technique makes sure the last 'Command' finishes before executing next 'Command'.
I've made this system so users can login to my website and play a game that requires some interval timing. When the user is done with playing I basically want to kill the interval. While everything seems to be running fine, there is something wrong with killing the interval.
Here is the problem Whenever a user is done playing the interval gets killed, not only for the user playing but for everyone. This might be because I'm assigning a variable to the interval and when a user is done playing a game I'm killing the interval, am I right that it then would kill the other intervals as well?
Here is some code I've written for this question,
var user; //this is a variable that has all info about the user. (its not usually empty)
var usersPlaying = [];
socket.on('game', function(game) {
if(game.type == "start"){
usersPlaying.push({
user_id: user.id
});
var game = setInterval(function(){
if(findUser(user.id) !== undefined){
console.log('Second passed!');
}else{
clearInterval(game); //stop the interval
}
}, 1000);
}else if(game.type == "stop"){
console.log("User has decided to quit playing the game!");
usersPlaying.splice(usersPlaying.findIndex(user => user === user.id), 1); //remove user from playing
}
});
There might be some mistakes in there since I rewritten and simplified the code otherwise it would be way to hard to help me out.
Anyways, how can I make it so it only clears the interval running for a certain specified person?
Thanks!
The setInterval call returns a unique id. You can use that id to clear that interval timer afterwards:
var uniqueId = setInterval(function () { /* ... */ }, 1000);
Later on ...
clearInterval(uniqueId);
will kill that specific timer.
I suggest storing the uniqueId for each user inside the usersPlaying array.
Store the interval for that specific socket inside its own scope :
var user; //this is a variable that has all info about the user. (its not usually empty)
var usersPlaying = [];
socket.on('game', function(game) {
if(game.type == "start"){
usersPlaying.push({
user_id: user.id
});
socket.game = setInterval(function(){
if(findUser(user.id) !== undefined){
console.log('Second passed!');
}else{
clearInterval(socket.game); //stop the interval
}
}, 1000);
}else if(game.type == "stop"){
console.log("User has decided to quit playing the game!");
usersPlaying.splice(usersPlaying.findIndex(user => user === user.id), 1); //remove user from playing
}
});
So you can also kill it when disconnections occur :
socket.on('disconnecting',function(){
if(socket.game){clearInterval(socket.game);}
});
EDIT :
var user; //this is a variable that has all info about the user. (its not usually empty)
better store all that inside the scope of the socket (each client socket object in the server will have its own "user" key instead of making use of ugly global variables
So store it like socket.user = {id:"foo"} and you can access the specific user object for that client performing the socket event request like if(findUser(socketuser.id) !== undefined){
I am creating a game where I keep a few variables such as fallingSpeed, runningSpeed etc. Right now my solution to stop the simple console editing in browser by using setTimeout(...){someFunction(...)}; to send the correct data to the client.
So what happens is that, when a player connects the speed of 1 is sent to the client. After that the server waits for a request which is sent to the it when 'space' has either been pressed or released. However, this causes problem since setTimeout(...){...} only runs once. So the client can just press 'space' once and while in the air, open the console and simply type fallingSpeed = 0; and start hovering around the map.
// use socket.io
var io = require("socket.io").listen(server);
var speedTimer;
io.sockets.on('connection', function(socket) {
socket.emit('fallingSpeed', {'fallingSpeed': 1});
// get data for if space is pushed or not
socket.on('spaceData', function(_spaceData) {
// if space has been pushed set falling speed to 8
if (_spaceData.pushed == 1) {
speedFunction(socket, 8);
}
// else fall back to speed 1
else {
speedFunction(socket, 1);
}
});
});
function speedFunction(socket, speed) {
speedTimer = setTimeout(function() {
socket.emit('fallingSpeed', {'fallingSpeed': speed});
}, 1000/60); // send data every frame.
}
I have tried using setInterval(...){...} but this only creates loops which we all know is not the greatest when multiple users are connected. This was my code using setInterval(...){...}.
// use socket.io
var io = require("socket.io").listen(server);
var speedTimer;
io.sockets.on('connection', function(socket) {
socket.emit('fallingSpeed', {'fallingSpeed': 1});
// get data for if space is pushed or not
socket.on('spaceData', function(_spaceData) {
// if space has been pushed set falling speed to 8
if (_spaceData.pushed == 1) {
clearInterval(speedTimer);
speedTimer = setInterval(function() {
socket.emit('fallingSpeed', {'fallingSpeed': 8});
}, 1000/60); // send data every frame.
}
// else fall back to speed 1
else {
clearInterval(speedTimer);
speedTimer = setInterval(function() {
socket.emit('fallingSpeed', {'fallingSpeed': 1});
}, 1000/60); // send data every frame.
}
});
});
So what I need is somehow to check if the value is the correct value when it changes on the clientside.
If you're writing a multiplayer game, you should never trust the client.
Rethink your architecture. Yes, some information should be given to the client to perform prediction and render the physics, but your server must be the authority on what's going on in the game.
I'm trying to use Twitter API in order to retweet.And because Twitter has limitation to 2400 actions per day I decided to limit the retweet to one time every 40 seconds.
I'm using https://www.npmjs.com/package/twit using Streaming API.
My problem is that it continuously streams console.log instead of using setInterval.
By stream I mean it outputs console.log multiple times instead of one time.
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
function twInterval() {
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
};
setInterval(twInterval, 1000 * 40);
});
}
Any way of getting the variales data retweetId,retweetId_str,tweetTextRetweet outside stream.on beside inserting them into a mysql table so that every 40 seconds it checks the variables fixed data and executes console.log?
There are multiple problems here:
You're trying to start an interval timer in an event handler. That means that every time the event fires you would be attempting to start a new interval timer and they would pile up.
Even if you did sucessfully start up an interval timer, each one would never change it's output since the variables in scope for it never change as it is started up within a given function and the arguments to that function are what they were when it was first called. Subsequent calls of the function will start a new function not change the arguments on the prior call of the function.
You aren't even starting your interval correctly. As it is, all you're doing is calling the function and passing it's return value to setInterval() which does nothing.
If the goal is just to output to the console each stream tweet event, then probably what you want is just this:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
});
}
You cannot get these variables outside the .on() handler. It's an async callback and the only place they reliably exist is within that handler.
If you can describe in more detail what the end result you're trying to achieve is, we can likely help more specifically. If you want to do something every 40 seconds, then maybe you need to collect data in some collection (probably an array) and then every 40 seconds evaluate what you have just recently collected.
Here's a method of collecting the events in an array and then evaluating them every 40 seconds:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']});
var tweets = [];
stream.on('tweet', function (tweet,error) {
tweets.push({id: tweet.id, str: tweet.id_str, text: tweet.text});
});
setInterval(function() {
// evaluate the tweets in the tweets array every 40 seconds
// do something with them
for (var i = 0; i < tweets.length; i++) {
// tweets[i].id
// tweets[i].str
// tweets[i].text
}
// reset the tweets array
tweets.length = 0;
}, 40 * 1000);
}
Please note that once you call hastagRetweet(), it will run forever.