socket.io show multiple users data after click - javascript

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'.

Related

how to download data from axios in advance in vue?

In my app, I need to run my app offline because drivers have to use that app and in remote areas, they might not find internet for that purpose I need to download all data in advance through Axios request at the time they have internet. so that request can get data from the cache instead of server.
At the moment i tried this but this doesn't seems to be good practice
tryOffileWorkSheet: async function () {
Network.addListener("networkStatusChange", (status) => {
if (status.connected) {
setInterval(function () {
let worksheet = JSON.parse(localStorage.getItem("worksheet"));
if (worksheet == null) {
worksheet = [];
}
// localStorage.removeItem('worksheet')
for (let i = 0; i <= worksheet.length; i++) {
if(worksheet.length > 0){
setTimeout(function () {
if(worksheet[i]?.work_order_id){
ApiService.get(
`/api/gangBoss/work-sheet/${worksheet[i].work_order_id}/${worksheet[i].column_name}/${worksheet[i].value}`
).then((response) => {
if(response.data.status){
worksheet.splice(i,1)
localStorage.setItem("worksheet", JSON.stringify(worksheet));
}
console.log('After', worksheet)
// this.closeModal();
});
}
},i* 3000);
}
}
}, 3000);
}
});
},
also for this, it is necessary for the user to behave on this page when the internet available it means that we need to download a lot of data.
Can you please tell me the best practice to do that or plugin for vue which can help for that
The question is sort of unclear and is not maybe the best fit for SO, but I'll try to answer.
The first thing I noticed here is the use of setInterval, not that you are using it, but rather how. There's nothing that stops the interval from running. Here's a scenario that I hope illustrates the problem
networkStatusChange fired: status.connected === true
setInterval creates interval #1
3 seconds later interval #1 fires
... this keeps happening for the next 2 hours
networkStatusChange fired: status.connected === false
... interval#1 keeps firing
networkStatusChange fired: status.connected === true
setInterval creates interval #2
within 3 seconds interval #1 fires
3 seconds later interval #2 fires
... intervals #1 and #2 keep firing (twice within 3 seconds)
networkStatusChange fired: status.connected === false
networkStatusChange fired: status.connected === true
setInterval creates interval #3
within 3 seconds interval #1 fires
within 3 seconds interval #2 fires
3 seconds later interval #3 fires
... intervals #1, #2 and #3 keep firing (three within 3 seconds)
So there's two problems. The interval keeps firing regardless of whether the system is still connected.
You might be better of just firing every 3 seconds with a single timer, and exiting if the connection is not available. This also allows using window.navigator.onLine, which has much better browser support.
I don't know how long you expect worksheet to be, but if you have the ApiService called through setTimeout 3 seconds apart, and then call the parent function every 3 seconds, you're going to be sending a lot of requests.
tryOffileWorkSheet: function () {
// (need to create `intervalId` in data)
if (this.intervalId) clearInterval(this.intervalId);
this.intervalId = setInterval(() => {
if (window.navigator.onLine) {
this.getWorkSheet();
}
}, 3000);
},
getWorkSheet: function () {
let worksheet = JSON.parse(localStorage.getItem("worksheet"));
if (worksheet == null) {
worksheet = [];
}
for (let i = 0; i <= worksheet.length; i++) {
if (worksheet.length > 0) {
setTimeout(() => {
if (worksheet[i]?.work_order_id) {
ApiService.get(
`/api/gangBoss/work-sheet/${worksheet[i].work_order_id}/${worksheet[i].column_name}/${worksheet[i].value}`
).then((response) => {
if (response.data.status) {
worksheet.splice(i, 1);
localStorage.setItem("worksheet", JSON.stringify(worksheet));
}
console.log("After", worksheet);
});
}
}, i * 300);
}
}
},

How do I run the code in a loop until a click has been performed?

I'm trying to book a vaccine in my country. This code applies the filters and then clicks a slot if vaccines are available.
The first 3 lines select the filters and the last line clicks on an available slot.
document.querySelector('.pin-search-btn.district-search.md.button.button-solid.ion-activatable.ion-focusable.hydrated').click()
setTimeout(() => {
document.querySelector('#c1').click()
}, 1000);
setTimeout(() => {
document.querySelector('#c5').click()
document.querySelectorAll('.vaccine-box').forEach(function(item) {
if (item.getElementsByTagName('a')[0].innerHTML !== ' Booked ' && item.getElementsByTagName('a')[0].innerHTML !== ' NA ') {
item.getElementsByTagName('a')[0].click()
}
})
}, 2000);
<-- html needed -->
I want to run this code in a loop over a 2 second time interval until the final click has been performed. item.getElementsByTagName('a')[0].click()
P.S : I'm running this in Developer Tools on Chrome, I don't know if that info is relevant here or not.
Intuitively, I would suggest keeping a boolean hasClickedSlot = false, which you update once a slot has been clicked. Before calling setTimeout for 2 more seconds, ensure that !hasClickedSlot still holds.
That could look something like:
let hasClickedSlot = false;
function clickButton() {
document.querySelector('.pin-search-btn.district-search.md.button.button-solid.ion-activatable.ion-focusable.hydrated').click();
// after 1 second, click on the C1 button
setTimeout(clickC1, 1000);
}
function clickC1() {
document.querySelector('#c1').click();
// after 2 seconds, try to click on a slot
setTimeout(tryClickSlot, 2000);
}
function tryClickSlot() {
document.querySelector('#c5').click();
document.querySelectorAll('.vaccine-box').forEach(function(item) {
if (item.getElementsByTagName('a')[0].innerHTML !== ' Booked ' && item.getElementsByTagName('a')[0].innerHTML !== ' NA ') {
item.getElementsByTagName('a')[0].click()
hasClickedSlot = true;
}
});
// if no slot was clicked yet, do it again
if (!hasClickedSlot) {
clickButton();
}
}
// start the process
clickButton();
The disadvantage of this code specifically is that a stack of calls is built, as the functions do not return but rather keep stacking.
Edit: Since the functions return after calling setTimeout no stack call is built (as it would during recursion, for instance).

Killing a "personalized" Interval

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){

Typescript: Moving an image to the left and then back to the right

First disclaimer: I am still extremely new to Typescript/javascript/front-end development
Background: I have a set of images (which represent a hand of cards). When it is the AI's turn to play a card, I am trying to show a simulation of "thinking" instead of immediately playing the card. My simulation is to iterate through the hand of cards and "select" each one (by "select", I mean move the image slightly to the left and then back to the right).
I am using Visual Studio Code and I am debugging in Chrome. My current code is below. The premise is that an API call is made (which is really where the AI logic is performed). Then the presentation iterates through the hand of cards and for each one it waits 1/4 second, shifts the card to the left, waits another 1/4 second, and shifts the card back to the right. After all cards have been "selected", then the actual card is played. I currently have everything in callback functions to keep it synchronous, but I'm not sure that I even need to do that.
// Call in to the API, which will perform the AI logic
// The API will return a slot that was played, which contains the card placed into that slot
opponentTurn(callback)
{
this.boardService.opponentTurn()
.subscribe(
cardPlayed =>
{
// Set the card played
let cardData = [];
cardData = JSON.parse(cardPlayed.toString());
// To add a slight hint of "thinking", let's "select" (slightly move the card to the left) each card down, up, and back down to the card that the API selected
this.selectCard(function(_thisagain) {
// Done
// Now simulate the move
_thisagain.dragService.simulateDragDrop(cardData);
});
callback(this);
}
);
}
// Call this to "select" a card; used by the AI "thinking" simulation
selectCard(callback)
{
for (var i = 0; i < this.players[1].cardHand.length; i++)
{
let imgObj = document.getElementById(this.players[1].name + i);
if (imgObj != null)
{
this.moveCard(imgObj, '400px', function(_thisagain) {
_thisagain.moveCard(imgObj, '350px', function() {
// Done
});
});
}
}
callback(this);
}
moveCard(imgObj, position, callback)
{
this.wait(250, function() {
imgObj.style.right = position;
});
callback(this);
}
wait(ms, callback)
{
var start = new Date().getTime();
var end = start;
while(end < start + ms)
{
end = new Date().getTime();
}
callback(this);
}
So the struggle I am having is that the code works, but only when I put a breakpoint on it. For example, if I put a breakpoint on the "_thisagain.moveCard(imgObj, '350px', function() {" line and then debug it, I can see each card shift to the left and back to the right as I would expect every time I 'Continue'. If I remove the breakpoint, the cards don't shift at all (yet I still get the wait before the card is played on the board).
Still being new to Typescript/javascript, I'm not really sure what is going on. It seems that when I have the breakpoint set, a redraw occurs to show the card shift. Without the breakpoint, it seems that no redraw is occurring. I'm not sure how to correct that, though, thus why I am here.
So after more research and a lot of trial-and-error, I have it working. The post that really got me on the right track is this:
DOM refresh on long running function
In that question, there was an answer posted which stated:
"Webpages are updated based on a single thread controller, and half the browsers don't update the DOM or styling until your JS execution halts, giving computational control back to the browser."
Turns out that was the key I was missing. I was processing far too much in a single statement and the js was not releasing back to the browser, thus the DOM/html was not being updated to show the "animation".
I then used the jsfiddle example also posted in an answer there to cobble together a "worker" thread that breaks up the process into chunks based on a procedural "status" and gives the control back to the browser with setTimeout(). Each worker thread is still set up as a callback function to ensure that the processing in each chunk finishes before moving on. For the intent of my original question, everything is working. I'm still refactoring and I'm sure there is probably a better way to achieve the results, but for now I am content.
For the sake of maybe helping someone in the future that might stumble across my question, this is what I have in my worker thread. Of course, my choice of terminology may be incorrect, but the gist of it is this. When I want to start a process flow, I can set the "status" text and call in to the worker. The worker then processes the flow as needed.
doHeavyWork() {
if (this.status == 'finish')
{
// All steps done
}
else
{
let _this = this;
switch (_this.status)
{
case 'start':
_this.status = 'working';
_this.opSelectCard = 0;
this.opponentTurn(function(response) {
_this.opCardData = response;
_this.status = 'selectCard';
});
break;
case 'selectCard':
_this.status = 'working';
this.selectCard(_this.opSelectCard, function(response) {
_this.opImgObj = response;
_this.status = 'moveCardLeft';
});
break;
case 'moveCardLeft':
_this.status = 'working';
this.moveCardLeft(_this.opImgObj, '-25px', function(root) {
_this.status = 'moveCardRight';
});
break;
case 'moveCardRight':
_this.status = 'working';
this.moveCardRight(_this.opImgObj, '1px', function(root) {
_this.opSelectCard++;
if (_this.opSelectCard < _this.players[1].cardHand.length)
{
_this.status = 'selectCard';
}
else
{
_this.status = 'simulateDragDrop';
}
});
break;
case 'simulateDragDrop':
_this.status = 'working';
this.dragService.simulateDragDrop(_this.opCardData, function(root) {
_this.status = 'refreshDisplay';
});
break;
case 'refreshDisplay':
_this.status = 'working';
this.refreshCardDisplay(function(root) {
_this.status = 'refreshDisplay';
});
break;
case 'refreshBoard':
_this.status = 'working';
this.refreshBoard(_this.boardCards, function(root) {
_this.status = 'updateScore';
});
break;
case 'updateScore':
_this.status = 'working';
this.updateScore(_this.boardCards, function(_this) {
_this.status = 'switchTurns';
});
break;
case 'switchTurns':
_this.status = 'working';
this.switchTurns(function(_this) {
if (_this.turn == 1)
{
_this.status = 'finish';
}
else
{
_this.status = 'start';
}
});
break;
}
setTimeout(function() {
_this.doHeavyWork();
}, 0);
}
}

Updating component every 5 seconds once socket is emitting an event (?)

I am working with React and socket.io, every time an event happens there is a socket listening to any changes.
I have this in my actions
socket.on('playerCount', (data) => {
PlayerSlotActions.updatePlayerAmount({
playerAmount : data.cardCount,
position : data.position,
});
})
where the data param returns this
{
playerAmount : 10,
position : 2
}
sometimes from the socket, comes from 4 to 7 events in one shot in order to change the playerAmount key to another value. Every time I receive that event, I should change the number of the playerAmount to the new number sent from the socket.
My issue:
lets say in the view I have something like Player Amount: 10, and then the socket sends the numbers 1, 3 and 5 in one shot, so the new amount would change to 19 which is the sum of the new number, and that is OK, but that change shouldn't happen quick, between the sum of one number and another, there should be 5 seconds of difference, like:
Player Amount: 10
after 5 seconds...
Player Amount: 11
after 5 seconds...
Player Amount: 14
after 5 seconds...
Player Amount: 19
... and so on.
so I am trying to figure out which is the best method to use here. With setTimeout, it does what I want but only the first attempt, the rest of the sums takes around 1 second of difference even if you put a timeout for 5 seconds.
I am using lodash, so I thought maybe _.debounce, _.throttle or _.delay methods could help, but I was wrong. Only delay works the same way as setTimeout
I did it like this
socket.on('playerCount', (data) => {
setTimeout(function() {
PlayerSlotActions.updatePlayerAmount({
playerAmount : data.cardCount,
position : data.position,
});
}, 5000);
});
I am just learning this. Is there a way like to store the new numbers in an array or something like this ?
Tell me your recommendations.
Just in case you want to see my code
the code above is in my actions, from the actions, it goes to the stores
#bind(PlayerSlotActions.updatePlayerAmount)
updatePlayerAmount (data) {
this.state.playerSlots[data.position - 1].playerAmount = data.playerAmount;
}
and from there, it goes directly to the component
componentDidUpdate () {
let playerAmount = this.props.playerAmount;
this.refs.playerAmount.getDOMNode().innerHTML = playerAmount;
}
UPDATE
connect () {
socket = io.connect('localhost:1101/server');
socket.on('playerCount', (data) => {
console.log('1');
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
})
setTimeout(function() {
if (queue.length > 0) {
var data = queue.splice(0, 1)
PlayerSlotActions.updatePlayerAmount(data);
}
}, 5000);
}
So you had problems putting in my code from this question: How to do a queue in order to wait 5 seconds to execute a function
I don't know if you solved it with Will Newton now, but in case you didn't, try this:
// variables for the queue and a boolean to
// indicate whether the queue gets worked on or not
var queue = [];
var notRunning = true;
connect () {
socket = io.connect('localhost:1101/server');
socket.on('playerCount', (data) => {
console.log('1');
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
startQueue();
})
// Initiating function, has to be seperate because else each socket connect
// would trigger a new instance of processQueue, which would result
// in shorter times than 5s between the updates
function startQueue(){
if(notRunning){
notRunning = false;
processQueue();
}
}
// Recursive function, calls itself every 5 seconds as long as there are
// items in the queue
function processQueue(){
if(queue.length > 0){
setTimeOut(function(){
// passes the first item of the queue in your function
PlayerSlotActions.updatePlayerAmount(queue[0]);
// removes first item from the queue
queue.shift();
processQueue();
}, 5000)
}else{
notRunning = true;
}
}
}
Yes, your idea about storing data in an array will work.
We can treat the array like a queue.
However, you are limited to it only updating every 5 seconds.
var queue = [];
socket.on('playerCount', (data) => {
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
});
setInterval(function() {
if (queue.length > 0) {
var data = queue.shift();
PlayerSlotActions.updatePlayerAmount(data);
}
}, 5000);

Categories