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){
Related
I am new in this world and I am trying things.
I was trying to build a timer bot... And now I have a little problem, I would like to stop my timer whenever I want.
bot.on('message', message => {
let timer = setInterval(() => {
if (message.content.startsWith('!start'))
message.channel.send("I print something");
}, 10 * 1000)
if (message.content.startsWith('!stop')) {
message.channel.send("Interaval Cleared");
clearInterval(timer);
}})
When I type !stop in my discord channel, it displays me the message but it doesn't stop my timer... I tried with a return; but it didn't work.
Could you help me please ?
Thanks,
Have a good day !
You're starting a new interval each time you run the function. Also, your timer value is scoped to each function call, and not "shared" between calls, you need to make it available between them. The behavior you want should look something like this instead:
bot = {}
bot.timer = 0; // ensures clearInterval doesn't throw an error if bot.timer hasn't been set
bot.listen = message =>{
if(message == 'start'){
clearInterval(bot.timer); // stops the interval if it been previously started
bot.timer = setInterval(()=>console.log('I print something'),1000)
}
if(message == 'stop') clearInterval(bot.timer);
}
I'm using web BLE. I have based my code according to the example of the heart rate measurement.
Everything is working fine most of the time. But sometimes, even if the connection is successfully made, when I try to bind to the notification, it doesn't work.
The link is made in this function :
_startNotifications(characteristicUuid) {
let characteristic = this._characteristics.get(characteristicUuid);
console.log(characteristic);
return characteristic.startNotifications().then(() => characteristic);
}
When everything is OK, I can see in the console that BluetoothRemoteGATTCharacteristic has a value : DataView(2) {}
Otherwise, when it's not working it has a value : null
I would like to be able to retry automatically, if I detect that the value is null. But I'm not familiar with Promise (I think this is it) and console.log(characteristic.value) doesn't work here.
How would you approach this ?
What I ended up doing is "bypass" the issue. So it's a more algorithmic resolution than a pure Javascript one.
I didn't change the connection function, so it is still called like this :
device._startNotifications(some_uuid).then(handleHeartRateMeasurement)
I check everything in the handleHeartRateMeasurement function :
var ready = false;
function handleHeartRateMeasurement(heartRateMeasurement) {
console.log("Hold on...");
heartRateMeasurement.addEventListener("characteristicvaluechanged", event => {
// Everytime the value change, this should be triggered
// If it did not, variable "ready" will stay false
ready = true;
var value = device.parseValue(event.target.value);
// Do something with value here
});
var check = function(){
// If we have received data from characteristic, we are ready to go !
if(ready === false){
console.log("Device connected but not receiving data");
// Stop the current notification subscription
device.stopNotificationsHeartRateMeasurement();
// Start a new one
device._startNotifications(some_uuid).then(handleHeartRateMeasurement);
setTimeout(check, 1000); // check again in a 1s
}
else{
console.log("Device connected and receiving data");
}
}
setTimeout(() => {
check();
}, 500);
}
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 :)
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 think I'm hitting a race condition on my Meteor client startup code.
Brief background
I'm using Meteor for a HTML5 game lobby interface. When a user returns to the menu by refreshing the page or returning later, I want the client to find any games in progress for that user. However, I can't access any Collections during startup.
An excerpt from the top of my client.js file is shown below.
Meteor.subscribe("players"); //Meteor.Collection("players" references Meteor.users via user_id
Meteor.subscribe("games"); // each player has a game_id, linked to Meteor.Collection("games"
if (Meteor.isClient) {
Meteor.startup(function () {
//find the player from the user_id, find game from player.game_id
var current_user_id = Meteor.userId();
var playerFromUser = Players.findOne({"user_id":current_user_id});
if(typeof playerFromUser !== 'undefined'){
var getGame = Games.findOne(playerFromUser.game_id);
if(typeof getGame !== 'undefined'){
alert('game found!'); //is not reached
}else{
alert('game not found'); //is not reached
}
}else{
alert('player not found'); //*********** IS reached. Why? ***********
}
});
}
However, from the javascript console I can run Players.findOne({"user_id":Meteor.userId()); and it can return a valid Player just fine. What's up with this race condition with my collections?
EDIT 1:
Looking deeper into the library I see that I was incorrectly assuming the data from the server would be immediately available. The subscriptions actually take a while to process, which was why they aren't available right when the client starts. I'm updating my question to express this new information.
Updated Question
Since I rely on multiple subscriptions to find a game for a user (both Players and Games), is there a Meteor way to wait for both? Having a separate subscription just for getting a possible game in progress seems wrong. So does requiring any server call at all.
I feel should be able to find the game I need from the retrieved players and games once they come in and not bother the server with any more calls.
I'll post here once I find my own solution.
Alright, here's what I have which seems to work:
Basically I added flags in the session that are set on the ready callbacks of the two subscriptions. Then the Deps.autorun function listens for those Session values to be set and does the rest.
if (Meteor.isClient) {
Meteor.subscribe("players", function(){
console.log("players_loaded ");
Session.set('players_loaded', true);
}); //linked to Meteor.users with user_id
Meteor.subscribe("games", function(){
console.log("games_loaded ");
Session.set('games_loaded', true);
}); // each player has a game_id
Deps.autorun(function(){
var gamesLoaded = Session.get('games_loaded');
var playersLoaded = Session.get('players_loaded');
if(gamesLoaded && playersLoaded &&
(typeof Players !== 'undefined') && (typeof Games !== 'undefined') ) {
console.log("both loaded! ");
//find the player from the user_id, find game from player.game_id
var playerFromUser = Players.findOne({"user_id":Meteor.userId()});
if(typeof playerFromUser !== 'undefined'){
var getGame = Games.findOne(playerFromUser.game_id);
if(typeof getGame !== 'undefined'){
console.log("game found : " + getGame);
//Do stuff with the game now that we have it
}else{
console.log("no game :(");
}
}else{
console.log("no player :(");
}
}
});
Meteor.startup(function () {
Session.set("title", "whatever else");
Session.set("games_loaded", false);
Session.set("players_loaded", false);
console.log("DOM loaded");
});
}
Here's hoping I helped somebody.