Using Websockets in Node.js to create a real time HTML 5 canvas with fabric.js? - node.js

Has anyone worked on a real time project with fabric.js/socket.io?
My canvas works fine but somewhere along, the canvas just turns white for one user (the other user's canvas appears normal). The objects are still on the canvas but they are white. Then after a minute the canvas's objects turn back to their original color.
I am simply serializing the canvas sending it to the server and broadcasting the canvas to all clients(except user). Canvas is then cleared, de-serialized and loaded on to the canvas.
How do I achieve a smooth real time canvas between 2 clients without any of these unexpected canvas glitches! I'm not sure if I am using socket.io or fabric.js wrong. Please advice me!
//client side code
var socket = io();
socket.on('canvas', function(canvasobj){
canvas.clear();
canvas.loadFromJSON(canvasobj, canvas.renderAll.bind(canvas));
});
canvas.on('mouse:up', function(options){
var canvasStr = JSON.stringify(canvas);
socket.emit('canvas', canvasStr);
});
//node.js code
io.on('connection', function(socket){
socket.on('canvas', function(canvasobj){
socket.broadcast.emit('canvas', canvasobj);
});
});

I am not really sure, but try
canvas.on('mouse:up', function(event){
event.preventDefault();
var canvasStr = JSON.stringify(canvas);
socket.emit('canvas', canvasStr);
});

Related

Socket.IO socket protection without authentication

I've created a simple Node.js app using Express.js and socket.io (available here), where the user clicks a button, and it increments a number on the page. This number is also incremented live among all clients connected to the page. I am using web sockets and socket.io to get the client-server communication and live number updating system.
I am using the flood-protection module to limit socket emits to 5 per second, but this really doesn't make the game very fun because of the low amount of clicks per second you can have, and hackers could just use a setInterval and still make considerable progress automatically, even at such a low rate.
My issue:
I don't want the user to have to authenticate themselves - anybody should be able to play!
I want to keep the click rate around 15 clicks per second, if possible.
I don't want people to be able to send socket messages and automatically click the button from the browser console.
Here's the program:
index.js
var express = require("express");
var http = require("http");
var socketIO = require("socket.io");
var path = require("path");
var fs = require("fs");
var FloodProtection = require("flood-protection").default;
__dirname = path.resolve();
function btoa(str) {
return new Buffer(str, 'latin1').toString('base64');
};
function atob(b64Encoded) {
return new Buffer(b64Encoded, 'base64').toString('latin1');
};
var app = express();
app.get("/", function(req, res){
res.sendFile(__dirname + "/index.html");
});
var temp;
num = temp | parseInt(atob(fs.readFileSync("num.txt"))) | 0
var server = http.createServer(app);
var io = socketIO.listen(server, {log: true});
io.sockets.on("connection", (socket) => {
protector = new FloodProtection({rate: 5, per: 1})
io.sockets.emit("update", num);
socket.on("push", (value) => {
if (protector.check()) {
num++;
temp = num
io.sockets.emit("update", temp);
} else {
io.sockets.emit("update", "You can only click the button five times per second.")
socket.disconnect(2)
setTimeout(()=>{}, 3000)
}
});
socket.on("disconnect", () => {
fs.writeFile("num.txt", btoa(String(temp)), (err) => {
if (err) throw err;
console.log("saved | new num: " + temp);
})
})
});
server.listen(5000);
index.html
<html>
<head>
<title>A Button</title>
</head>
<body>
<button onclick='push();'>Click me!</button>
<p id="out"></p>
</body>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var variableFromFrontEnd = 2;
var socket = io.connect("/");
socket.on("connect", function() {
socket.on("update", function(val) {
document.getElementById("out").innerHTML = val
});
});
socket.on("disconnect", function() {
setTimeout(()=>{socket.connect();}, 1000);
});
function push() {
if (socket.connected) {
socket.emit("push");
}
}
</script>
</html>
num.txt is a base-64 encoded number.
So, is there a way to be able to do this without significant rate limiting or authentication? Or am I just going to have to use rate limiting?
There's a lot of different ways for users to cheat, and just as many ways to prevent them. The general rule is that you can only "make things harder" (if you're lucky, hard enough that the potential cheater loses interest before succeeding).
For a browser-based game, I would make sure that you are at least ensuring your game gets totally minified/tersed (so your javascript code is as unreadable as possible, and it's more difficult to just call a "click" function directly), and build in checksums in your messages (so the user can't just make socket calls directly to the server).
Once you've done that, you still have to deal with users who generate click events on the element directly with code or a plugin, or users who use a program outside the browser to generate click events above the button repeatedly. Your best defense against this is not to prevent it, but instead to detect it -- probably on the server side, by watching for users that have a sustained click rate that is not humanly possible, and then blowing up their game / temporarily banning their IP / etc.
See a related question Ways to prevent or reduce cheating for more related ideas (this question is about general client-server games, not browser games, but some of the discussion is still useful).

Use the same socket.io connection in multiple HTML pages in node js

-Follwing is the jquery code I have written in my (dashboard.html) file
<script>
$(function(){
$("#username").hide();
var socket= io.connect();
$(document).on("click", ".help", function () {
alert( $(this).attr('id'));
socket.emit('help',{helper:$username.val(),requester:$(this).attr('id')});
});
});
---On clicking help button socket will emit an event "help" as you can see in the code.
---Following is the app.js file on server
io.sockets.on('connection',function(socket){
socket.on('help',function(data){
console.log('rohi is goood',data.helper);
socket.emit('loadList' , {helper:data.helper,requester:data.requester});
});
});
---On "help" event socket is emitting an event "loadList" in app.js file.
Now I want to use "loadList" event in some other html file like "chat.html".
The code I have written is as follows for chat.html.
<script>
$(function(){
// var socket= io.connect();
// var socket= io.connect('http://localhost:3000/', { 'force new //connection': true });
socket.on('loadList',function(data){
alert('inside help',$('#usernam').val());
console.log('tatai',$('#usernam').val());
if($('#usernam').val()== data.helper){
$('#chatList').append('<p>'+data.requester+'</p>'+'<button value="chat"></button>');
}
else if($('#usernam').val() == data.requester){
$('#chatList').append('<p>'+data.helper+'</p>'+'<button value="chat"></button>');
}
else {
alert('fuck off');
}
});
The above code is not working. Please tell me how can I use same socket connection in the chat.html file.(loadList event is not working).
As your question is not complete so i am assuming you want to know if socket.io connection can be used on different html pages . Yes you can access it on every html page of your application as long as you have one server on which socket.io is used .
Every time a new user comes a socket session is created for that particular user and that user can access any page of your application .

Could not initialize canvas element using Express Application Framework

Just a spinnin' my wheels on this one. I'm trying to get a start into the node.js/fabric/express world and can't seem to get past this wall of "Could not initialize canvas element".
I've created a new express.js application using the generator:
https://expressjs.com/en/starter/generator.html
Here's a simple working example of what I'm trying to accomplish within my express application framework:
https://jsfiddle.net/6dcgakb6/
I haven't touched most of the auto generated files, except to add jquery and a public/javascripts/myApp.js file for client side javascript. It's within this file I think I'm having the issues or I'm not understanding basic principles.
I'm including scripts jquery.js, fabric.js, and myApp.js in <head> of my HTML layout in that order. I added routes to routes/index.js so that I can include them using <script src="fabric/fabric.js">.
router.get('/jquery/jquery.js', function(req, res, next) {
res.sendFile(path.resolve(appDir + '/../node_modules/jquery/dist/jquery.min.js'));
});
router.get('/fabric/fabric.js', function(req, res, next) {
res.sendFile(path.resolve(appDir + '/../node_modules/fabric/dist/fabric.js'));
});
The body simply contains a <canvas id="c"></canvas> element.
Then, in myApp.js:
$(document).ready(function(){
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20,
angle: 45
});
canvas.add(rect);
});
When visiting the page, I receive the error message:
jquery.js:2 Uncaught Error: Could not initialize `canvas` element
(anonymous function) # fabric.js:5989
(anonymous function) # fabric.js:7633
After playing around for a bit, I did find that I can run var canvasEl = fabric.document.createElement('canvas') without any error. I can also run var canvas = new fabric.Canvas(); without error as well (notice the missing constructor parameter value of 'c').
I would really appreciated some help on this one. Again, I wonder if I'm missing a key design principle for including and initializing client side javascript using the express framework or if I'm missing something else terrible simple.
I've searched for a solution, but have been unsuccessful in doing so. Here are some interesting links I found along the way in case they are helpful to others.
https://github.com/kangax/fabric.js/issues/573
https://groups.google.com/forum/#!topic/fabricjs/Qix_Z-2AJ2o
Facepalm
Okay, it was a type-o. I wrote <cavnas id="c"> instead of <canvas id="c">.

Problems with socket.removeAllListeners(); (socket.io)

I encounter an issue with my code when using socket.removeAllListeners();
I'm using express, socket.io and socket.io-client: this server is linked to another one, represented by matchmaking which is sending the information that the game has stopped.
var express = require('express');
var url = require('url');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
var matchmaking = require("socket.io-client")('http://localhost:1610');
io.on('connection',function(socket){
var page = 'lobby';
show_page(page);
matchmaking.on('ChangePage',function(value){
//switch lobby to game
show_page(value.Page);
});
function show_page(page){
//The user goes here to login and sends info to the matchmaking server
if(page == 'lobby')
{
//If more than 2 players are waiting , the matchmaking server generate a game server, put the waiting players in it and sends it to this server.
}
//If the player joined a game, he's redirected here
else if(page == 'game')
{
socket.on('PlayerMove',function(value){
// The game Code
})
matchmaking.once('END',function(value){
//The game is ended and the user go back to lobby in the players queue where he will be put in another game
socket.removeAllListeners("PlayerMove");
show_page('lobby');
})
}
}
})
When the game is ended, the user will join another game and will pass by (page == 'game')where he will be affected with another listener. So the first time , the game works perfecly but then each time he joins a new server, the code inside the listenner is multiplied.
The only way I found to fix it is to make the code like that which doesn't fit with my idea.
socket.on('PlayerMove',function(value){
// code inside
}
socket.removeAllListeners("PlayerMove");
matchmaking.once('END',function(value){
})
Is there any solution to my problem ?
Thanks in advance
I've finaly resolved the problem on my own : this multiplication was caused because i forgot to remove socket on the clients too.

How to write custom emitEvent for proessing in socket.io?

I am writing a multi-player game using processing.js, node.js and socket.io.
Question #1:
In the client, I use p5.js to create a class Ball.
I want the server to send parameter to create an array using that class (balls.push(new Ball(x, y));), so every client can have a bunch of balls moving on the canvas.
I know I should use socket.io to emit the parameter to the client but i have no clue.
Normally the array is created inside the setup function inside p5...so how could socket do that?
Question #2:
How could the client send the mouseX and mouseY to the server? And then how could the server send back others' mouseX and mouseY to every client?
I try to make p5.js into normal js like this:
(function () {
"use strict";
function sketchProc(processing) {
var p=processing,
var ...,
var ...;
function ball(){...}
p.setup=function(){}
p.draw=function(){}
}
var canvas = document.getElementById("canvas1"),
p = new Processing(canvas, sketchProc);
}());
But i don't know if this helps...
The balls should be passed to the client only in the connection event. That's where node's beauty lies, code sharing.
To create a socket server, you can use either node's native Net module or Socket.io
On the server:
var server = net.createServer();
server.listen(PORT, HOST);
server.on('connection', function(socket) {
socket.write({ type: "BallsArray", data: BallsArray });
});
On the client, you use WebSockets with Socket.io:
var client = io.connect('http://localhost:3000');
var BallsArray = null;
client.on('message', function(msg) {
if(msg.type == "BallsArray")
BallsArray = msg.data;
});

Resources