So I am trying to make a node multiplayer game using socket.io and the HTML5 canvas.
I have gotten the objects to appear on each other's client canvas, but their positions are way off. How do I position the objects so that they will show up properly on the client side?
Here is my code.
Client:
var socket;
var ship;
var cnv;
function setup() {
cnv = createCanvas(1000, 1000);
translate(120, 120);
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
background(51);
socket = io('http://localhost:3000');
ship = new Avatar();
socket.on('pos', newDrawing)
var data = {
x:ship.pos.x,
y:ship.pos.y,
r:ship.r,
heading:ship.heading
}
socket.emit('start', data)
}
function newDrawing(data){
for(var i =0; i< data.length;i++){
if(data[i].id != socket.id){
rotate(data[i].h+ PI/2)
var x=data[i].x;
var y= data[i].y
var r = data[i].r
console.log(r)
triangle(x+ -data[i].r, y+data[i].r, x+data[i].r,y+data[i].r,x+ 0,y+-data[i].r)
}
}
}
function keyPressed(){
if(keyCode == 65){
ship.setRotation(-0.1)
//ship.vel.x += -speed;
}
//Right
if(keyCode == 68){
ship.setRotation(0.1)
}
if(keyCode == 87){
ship.boosting(true);
}
}
function keyReleased(){
ship.setRotation(0);
ship.boosting(false);
}
function draw() {
//background(51);
//console.log(cnv)
ship.render();
ship.turn()
ship.update();
ship.edges();
var data = {
x:ship.pos.x,
y:ship.pos.y,
r:ship.r,
heading:ship.heading
}
socket.emit('pos', data)
//clear()
}
Object
class Avatar{
constructor(){
this.pos=createVector(1000,1000);
this.r=50;
this.vel=createVector(0,0)
this.color = 'red';
this.move=false;
this.heading=0;
this.rotation=0;
this.isBoost=false;
}
update(){
if(this.isBoost){
this.boost();
}
this.pos.add(this.vel);
this.vel.mult(.99)
}
setRotation(a){
this.rotation=a;
}
boosting(a){
this.isBoost=a;
}
boost(){
var force= p5.Vector.fromAngle(this.heading);
this.vel.add(force);
}
turn(){
this.heading+= this.rotation;
}
edges(){
if(this.pos.x>width+this.r){
this.pos.x=-this.r;
} else if(this.pos.x<-this.r){
this.pos.x=width +this.r;
}
if(this.pos.y>height+this.r){
this.pos.y=-this.r;
} else if(this.pos.y<-this.r){
this.pos.y=height +this.r;
}
}
render(){
//clear()
//translate(this.pos.x, this.pos.y)
rotate(this.heading+ PI/2)
//rect(this.r,-this.r,this,this.r)
fill(this.color)
triangle(-this.r,this.r,this.r,this.r,0,-this.r)
}
}
Server
var express=require('express');
var app=express();
var server = app.listen(3000)
var ships=[];
function Avatar(id,x,y,r,h){
this.id=id;
this.x = x;
this.y= y;
this.r=r;
this.h=h;
}
console.log("Server on %s", server.address().port)
// function listen(){
// var host= server.address().address;
// var port= server.address().port;
// console.log('Server running on %s',port )
// }
app.use(express.static('public'));
var socket=require('socket.io');
var io=socket(server);
io.sockets.on('connection',function(socket){
console.log("New Socket:" + socket.id)
socket.on('start',function(data){
ships.push(new Avatar(socket.id, data.x, data.y, data.r, data.heading));
//console.log(ships)
})
socket.on('pos',function(data){
var ship;
for(var i =0; i< ships.length; i++){
if(ships[i].id == socket.id){
ships[i].x=data.x;
ships[i].y=data.y;
ships[i].r=data.r;
ships[i].h=data.heading;
}
}
//console.log(ships[0])
//console.log(ships)
//console.log(socket.id)
//io.sockets.emit('pos',ships)
socket.broadcast.emit('pos',ships)
})
socket.on('disconnect' , function(){
for(var i =0; i< ships.length; i++){
if(ships[i].id == socket.id){
console.log(socket.id + " Has Left")
ships.splice(i,1);
}
}
})
})
Part of the issue I am seeing is that the other clients objects are being drawn as though their point of origin is not in the center of its own object.
I figured out why it was an issue. I was unaware that the translate and rotate functions were not object specific. Meaning, that when I was rotating for my client, it was rotating the perspective of the canvas, not the object. So any other objects would be visibly rotating around it.
Related
I have inserted a mute button into my WebRTC Video chat page but I cannot get it to work. If I click it in the browser I get a console message that the sound has been muted but there is still sound.
The Constraints variables:
var constraints = {
video: true,
audio: true,
};
If I change audio to false here there will be no sound.
Code on Mute Button:
function muteVideoBtnClick() {
if(constraints.audio == true) {
constraints.audio = false;
console.log('Audio: ' + constraints.audio);
} else {
constraints.audio = true;
console.log('Audio: ' + constraints.audio);
}
}
The Only other place where the constraints variables are used:
function pageReady() {
uuid = uuid(); //CB Universal Unique Identifier
//CB Create the variables for local and remote video
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
//CB Create the connection using websocket (443 as it is a secure connection)
serverConnection = new WebSocket('wss://' + window.location.hostname + ':443');
serverConnection.onmessage = gotMessageFromServer;
// CB Checks thats getUserMedia works and then runs getUserMedia if it works and displays an error
//if it doesnt work
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(getUserMediaSuccess).catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API');
}
}
I would be very grateful if anyone has any suggestions.
Kind regards,
Claire
The Full code:
var localVideo;
var remoteVideo;
var peerConnection;
var uuid;
var rooms = [];//CB 31/07
var constraints = {
video: true,
audio: true,
};
var peerConnectionConfig = {
'iceServers': [
{'urls': 'stun:stun.services.mozilla.com'},
{'urls': 'stun:stun.l.google.com:19302'},
]
};
function pageReady() {
uuid = uuid(); //CB Universal Unique Identifier
//CB Create the variables for local and remote video
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
//CB Create the connection using websocket (443 as it is a secure connection)
serverConnection = new WebSocket('wss://' + window.location.hostname + ':443');
serverConnection.onmessage = gotMessageFromServer;
// CB Checks thats getUserMedia works and then runs getUserMedia if it works and displays an error
//if it doesnt work
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(getUserMediaSuccess).catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API');
}
}
//CB if it is possible to run gerUserMedia then gets the local video stream
function getUserMediaSuccess(stream) {
localStream = stream;
localVideo.src = window.URL.createObjectURL(stream); //Depreciated!!!!!
//localVideo.srcObject = stream;
}
//CB this function starts the call
function start(isCaller) {
peerConnection = new RTCPeerConnection(peerConnectionConfig);
peerConnection.onicecandidate = gotIceCandidate;
peerConnection.onaddstream = gotRemoteStream;
//peerConnection.ontrack = gotRemoteStream;
peerConnection.addStream(localStream);
if(isCaller) {
peerConnection.createOffer().then(createdDescription).catch(errorHandler);
}
}
//Added by CB for Pause Button 20/07
function pauseVideoBtnClick() {
var btn = document.getElementById("pause_video_btn");
if (isVideoPaused()) {
pauseVideo(false);
btn.innerHTML = "Pause Video";
} else {
pauseVideo(true);
btn.innerHTML = "Resume Video";
}
}
//Added by CB for Pause Button 20/07
function isVideoPaused() {
return !(localStream.getVideoTracks()[0].enabled);
}
//Added by CB for Pause Button 20/07
function pauseVideo (pause) {
localStream.getVideoTracks()[0].enabled = !pause;
};
//Added by CB for mute button 29/07 - DOESNT WORK YET
function muteVideoBtnClick() {
if(constraints.audio == true) {
constraints.audio = false;
console.log('Audio: ' + constraints.audio);
} else {
constraints.audio = true;
console.log('Audio: ' + constraints.audio);
}
}
//End of added code
function gotMessageFromServer(message) {
if(!peerConnection) start(false);
var signal = JSON.parse(message.data);
// Ignore messages from ourself
if(signal.uuid == uuid) return;
if(signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function() {
// Only create answers in response to offers
if(signal.sdp.type == 'offer') {
peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
}
}).catch(errorHandler);
} else if(signal.ice) {
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
}
}
function gotIceCandidate(event) {
if(event.candidate != null) {
serverConnection.send(JSON.stringify({'ice': event.candidate, 'uuid': uuid}));
}
}
function createdDescription(description) {
console.log('got description');
peerConnection.setLocalDescription(description).then(function() {
serverConnection.send(JSON.stringify({'sdp': peerConnection.localDescription, 'uuid': uuid}));
}).catch(errorHandler);
}
function gotRemoteStream(event) {
console.log('got remote stream');
remoteVideo.src = window.URL.createObjectURL(event.stream);
//remoteVideo.src = event.stream;
}
function errorHandler(error) {
console.log(error);
}
// CB A UUID (Universal Unique Identifier) is a 128-bit number used to uniquely identify some object or entity on the Internet.
// Taken from http://stackoverflow.com/a/105074/515584
// Strictly speaking, it's not a real UUID, but it gets the job done here
function uuid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
You are changing the constraints to the getUserMedia call (after doing the call). You are not changing the resulting stream which is stored in the localStream variable. Try this:
localStream.getAudioTracks()[0].enabled = false;
var feedData = [];
var listFeedQuery = pgFormat("select * from feedList where shopId=%L",shopId);
model.client.query(listFeedQuery,function(err,result){
if(result.rows.length > 0){
var triggerImageQuery = function(start,length,callback) {
var feedInfo = result.rows[start];
var imgQuery = pgFormat("select * from feedImages where feedId=%L",feedInfo.feedid);
model.client.query(imgQuery,function(err,result){
if(result.rows.length > 0){
var imgArr =[];
for(var j=0;j<result.rows.length;j++){
var image = "http://"+config.host+":"+config.port+"/"+result.rows[j].imageurl;
imgArr.push(image);
}
feedData.push(feedInfo);
feedData.push(imgArr);
}
else{
feedData.push(feedInfo);
}
console.log(feedInfo); // feedInfo have value
});
console.log(feedInfo); // feedInfo gets empty here
callback({'success':'1','result':{feedData},errorMessage:'No'});
if(start < length) {
start++;
triggerImageQuery(start, length-1);
}
}
triggerImageQuery(0, result.rows.length,function(result){
res.json(result);
});
}
else{
res.json({'success':'0','result':{},'errorMessage':'No feed available from the shop'});
}
});
feedData array is looks empty outside of a function?
its into looping(var feedInfo = result.rows[start];), so i unable to use the callback inside model.client.query. if i have callback inside model.client.query it will show the first set of details and return typeError : callback is not a function.
Try below codes. I have replaced the for...loop with a function logic.
var feedData = [];
var verifyShopQuery = pgFormat("select * from feedList where shopId=%L",shopId);
model.client.query(verifyShopQuery,function(err,result){
if(result.rows.length > 0){
var triggerImageQuery = function(start, length) {
var feeds = result.rows[start];
var imgQuery = pgFormat("select * from feedImages where feedId=%L",feeds.feedid);
model.client.query(imgQuery,function(err,result){
if(result.rows.length > 0){
var imgArr =[];
for(var j=0;j<result.rows.length;j++){
imgArr.push(result.rows[j].imageurl);
}
console.log(feeds,imgArr);
if(start < length) {
start++;
triggerImageQuery(start, length);
}
}
});
};
triggerImageQuery(0, result.rows.length);
}
else{
res.json({'success':'0','result':{},'errorMessage':'invalid shopId'});
}
hey guys im working on a project with some friends and we want our server on openshift it runs without errors but it always gives cannot get /
i tried to look for solutions and tried many but it just doesent fix it.
any ideas ?
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io').listen(server);
//
app.use(express.static(__dirname + '/public'));
app.use('/static', express.static(__dirname + '/public'));
server.listen(process.env.OPENSHIFT_NODEJS_PORT, process.env.OPENSHIFT_NODEJS_IP);
io.on('connection', onSocketConnection);
var players = [];
var npc1 = new Player(1049, 980);
npc1.id = "Johan Kruijs";
npc1.color = "gold";
npc1.name = "[NPC] Johan Kruijs";
players.push(npc1);
function onSocketConnection(client) {
console.log("Player has connected: ");
client.on("disconnect", onClientDisconnect);
client.on("new player", onNewPlayer);
client.on("move player", onMovePlayer);
};
function onClientDisconnect() {
var removePlayer = playerById(this.id);
if (!removePlayer) {
console.log("Player not found: " + this.id);
return;
}
console.log(removePlayer.name + " has disconnected.");
players.splice(players.indexOf(removePlayer), 1);
this.broadcast.emit("remove player", {
id: this.id
});
};
function onNewPlayer(data) {
var newPlayer = new Player(data.x, data.y);
newPlayer.id = this.id;
newPlayer.name = data.name;
newPlayer.color = data.color;
this.broadcast.emit("new player", {
id: newPlayer.id,
x: newPlayer.x,
y: newPlayer.y,
name: newPlayer.name,
color: newPlayer.color
});
var i, existingPlayer;
for (i = 0; i < players.length; i++) {
existingPlayer = players[i];
this.emit("new player", {
id: existingPlayer.id,
x: existingPlayer.x,
y: existingPlayer.y,
name: existingPlayer.name,
color: existingPlayer.color
});
};
players.push(newPlayer);
console.log(" - name: [" + newPlayer.name + "]")
console.log(" - id: [" + newPlayer.id + "]");
};
function onMovePlayer(data) {
var player = playerById(data.id);
player.x = data.x;
player.y = data.y;
player.id = data.id;
io.emit("move player", {
id: data.id,
x: data.x,
y: data.y
})
};
function playerById(id) {
var i;
for (i = 0; i < players.length; i++) {
if (players[i].id == id)
return players[i];
};
return false;
};
function Player(xpos, ypos) {
var result = {
x: xpos,
y: ypos,
id: 0
}
return result;
}
path --
In the screenshot you shared, the folder name is Public and not public, in osX (I assume that is what you are using from the screenshot), Public and public are different.
If you write this,
app.use(express.static(__dirname + '/Public'));
Things should start working.
Plus if you wanna set a default page, i.e. when user visits / and you want your /index.html to be served, you can do it like
app.use('/', express.static(__dirname + '/Public', {index: "index.html"}));
I hope this resolves your issue!!
Is there a setsockopt/getsockopt-like socket options manipulation functionality in node.js?
I'm expanding on the comment left by socketpair which shows getsockopt. You can accomplish this by using ffi and ref. I've reformatted this to allow it to be easily manipulated.
I edited my comment because I had to make some changes to make the code work on both Linux and Win32. I had to create a node library for Windows to get the socket handle and pass it to setsockopt. Be aware that Linux and Windows may have different values for socket options
Edit: Here's a cleaned up piece of of production code I'm using:
var net = require("net");
var ffi = require("ffi");
var ref = require("ref");
var getSocketHandleAddress;
var SOL_SOCKET = 0x1;
var SO_OOBINLINE = 0xA;
var _setsockopt;
if (process.platform == "win32") {
SOL_SOCKET = 0xffff;
SO_OOBINLINE = 0x0100;
}
var setSocketOption = function (handle, level, option, value) {
if (!_setsockopt) {
var library;
var paramTypes;
if (process.platform === "win32") {
library = "ws2_32.dll";
paramTypes = [
ref.types.int,
ref.types.int,
ref.types.int,
ref.refType(ref.types.void),
ref.types.int
];
} else {
paramTypes = [
ref.types.int,
ref.types.int,
ref.types.int,
ref.refType(ref.types.void),
ref.refType(ref.types.int)
];
}
var lib = new ffi.DynamicLibrary(library);
_setsockopt = ffi.ForeignFunction(
lib.get("setsockopt"),
ref.types.int,
paramTypes);
}
var refType;
var length;
if (typeof value === "boolean") {
refType = ref.types.bool;
} else {
refType = ref.types.int;
}
if (process.platform !== "win32") {
return _setsockopt(
handle.fd,
level,
option,
ref.alloc(refType, value),
ref.alloc(ref.types.int, refType.size)
);
}
if (!getSocketHandleAddress) {
getSocketHandleAddress = require("getsockethandleaddress");
}
return _setsockopt(
getSocketHandleAddress.getAddress(handle),
level,
option,
ref.alloc(refType, value),
refType.size
);
};
var tcpserver = net.createServer(function (socket) {
var ret = setSocketOption(socket._handle, SOL_SOCKET, SO_OOBINLINE, true);
if (ret !== 0) {
console.error("OOB Inline socket option failed: " + ret);
}
});
This is my getsockopt:
var ffi = require('ffi');
var net = require('net');
var StructType = require('ref-struct');
var ref = require('ref');
var current = ffi.Library(null, {
'getsockopt': [ 'int', [ 'int', 'int', 'int', 'pointer', 'pointer']],
'ntohs': ['uint16', ['uint16']],
// const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
});
var SOL_IP = 0;
var SO_ORIGINAL_DST = 80;
var AF_INET = 2;
var sockaddr_in = StructType([
['int16', 'sin_family'],
['uint16', 'sin_port'],
['uint32', 'sin_addr'],
['uint32', 'trash1'],
['uint32', 'trash2'],
]);
function get_original_dst(client) {
var dst = new sockaddr_in;
var dstlen = ref.alloc(ref.types.int, sockaddr_in.size);
var r = current.getsockopt(client._handle.fd, SOL_IP, SO_ORIGINAL_DST, dst.ref(), dstlen);
if (r === -1)
throw new Error("getsockopt(SO_ORIGINAL_DST) error");
if (dst.sin_family !== AF_INET)
throw new Error("getsockopt(SO_ORIGINAL_DST) returns unknown family: " + dst.sin_family );
// TODO: inet_ntop. inet_ntoa is _UNSAFE_
var ipaddr = dst.ref(); ipaddr = ipaddr[4] + "." + ipaddr[5] + "." + ipaddr[6] + "." + ipaddr[7];
return [ipaddr, current.ntohs(dst.sin_port)];
}
module.exports.get_original_dst = get_original_dst;
Kind of late but here is a pacakge on npm https://www.npmjs.com/package/net-keepalive
Provides high-level access to socket options like TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_KEEPCNT
var Net = require('net')
, NetKeepAlive = require('net-keepalive')
;
// Create a TCP Server
var srv = Net.createServer(function(s){>
console.log('Connected %j', s.address())
// Doesn't matter what it does
s.pipe(s)
});
// Start on some port
srv.listen(1337, function(){
console.log('Listening on %j', srv.address())
});
// Connect to that server
var s = Net.createConnection({port:1337}, function(){
console.log('Connected to %j', s.address())
//IMPORTANT: KeepAlive must be enabled for this to work
s.setKeepAlive(true, 1000)
// Set TCP_KEEPINTVL for this specific socket
NetKeepAlive.setKeepAliveInterval(s, 1000)
// and TCP_KEEPCNT
NetKeepAlive.setKeepAliveProbes(s, 1)
});
For education purposes I am creating a little chat with node.js using TCP.
I am using the windows console to connect with my node server but when I am typing all the characters are streamed one by one. They don't arive as strings. How can I manage to handle those streams so my users don't can write complete words.
My Code:
var net = require("net");
Array.prototype.remove = function(e) {
for (var i = 0; i < this.length; i++) {
if (e == this[i]) { return this.splice(i, 1); }
}
};
function Chatter(stream) {
this.name = null;
this.stream = stream;
}
var chatters = [];
var server = net.createServer(function(stream) {
var chatter = new Chatter(stream);
chatters.push(chatter);
stream.setTimeout(0);
stream.setEncoding("utf8");
stream.addListener("connect", function(){
stream.write("Hallo, wer bist du?:\n");
});
stream.addListener("data", function (data) {
if(chatter.name == null) {
chatter.name = data.match(/\S+/);
stream.write("....................\n");
chatters.forEach(function(c){
if (c != chatter) {
c.stream.write(chatter.name + " ist dem Chat beigetreten!\n");
}
});
return;
}
var command = data.match(/^\/(.*)/);
if (command) {
if (command[1] == 'users') {
chatters.forEach(function(c) {
stream.write("- " + c.name + "\n");
});
}
else if (command[1] == 'quit') {
stream.end();
}
}
chatters.forEach(function(c) {
if(c != chatter) {
c.stream.write(chatter.name + ": " + data);
}
});
});
stream.addListener("end", function(){
chatters.remove(chatter);
chatters.forEach(function(c) {
c.stream.write(chatter.name + " hat den Chat verlassen.\n");
});
stream.end();
});
});
server.listen(8000);
For the record that code is from this site
ADDITION:
setEncoding('utf8') is supposed to change the emiting of data, but it doesn't work for me :-(
The solution to your problem is to store all received characters in a buffer and when an END_OF_NICK character is encountered (say, \n), use the buffer as the name.
var buffer = ""; // stores received characters
stream.addListener("data", function (data) {
if(chatter.name == null) { // still receiving characters for the name
buffer += data; // append received characters to the buffer
if (buffer.indexOf('\n') == -1) return; // if there's no END_OF_NICK character, keep waiting for it
chatter.name = buffer.match(/\S+/); // use the name in the buffer
// ...
}