Search the Community

Showing results for tags 'networking'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • HTML5 Game Coding
    • News
    • Game Showcase
    • Coding and Game Design
  • Frameworks
    • Phaser
    • Pixi.js
    • Babylon.js
    • Panda.js
    • melonJS
    • Haxe JS
    • Kiwi.js
  • General
    • General Talk
  • Business
    • Collaborations (un-paid)
    • Jobs (Hiring and Freelance)
    • Services Offered

Found 10 results

  1. Hey everyone! I've just released version 0.5.0 of Colyseus! If you haven't heard of it, it's a multiplayer game server for Node.js that has been designed to be really easy to develop real-time multiplayer games as a solo developer. Read more details of this release here: https://medium.com/@endel/colyseus-html5-multiplayer-games-made-simple-v0-6-0-alpha-update-d5d0e5eba4a0 Repository URL: https://github.com/gamestdio/colyseus Cheers!
  2. Many of you know games like agar.io, slither.io and many others. Most of them are implemented on top of WebSockets, which is based on TCP. Due to that pretty much all of them suffer from some latency issues making them less responsive and limiting over what multiplayer games can be implemented in the web today. So here is public demand for Web UDP: https://github.com/Maksims/web-udp-public Which is collaborative effort to shape requirements by developers as well as awareness about need of UDP, so to motivate W3C members and browser vendors for work on specs and implementations that will allow use of UDP in server-client cases. Please contribute, participate and share to raise awareness and get the momentum.
  3. Hi! I'm in the process of developing an online multiplayer snake (4-8 simultaneous players in a grid based arena) with the stack involving Phaser in the client, Node in the backend with the networking protocol being websockets through socket-io. The progress made so far has been a prototype consisting of a dumb client and an authoritative server. The server proceeds game logic by 100ms and emits the result to the client at the end of each iteration of this game loop. The client is able to tell the server to change its direction to up, down, left and right, respectively, and the server will use this in order to calculate the next x and y position for this client (which is on the next tick). If the server notices clients colliding with walls, other clients or fruits, the involved client(s) is affected accordingly. The issue with this "dumb client" approach is that the responsiveness for the client seems to be lost in not actually simulating anything locally. From what I understand, to mitigate this lag for the client, client prediction can be applied. Since the movement is actually not caused as a direct action from a client but is always constant (you only influence direction in snake), this would mean that this 100ms game loop would have to be simulated in the client as well, with directional client input being respected and simulated directly in the client at the same time as it is being sent to the server for "verification". For some reason I cannot seem to wrap my head around this topic of the game having "constant speed". The examples I've read are mostly based on games where the movement is based directly on a fixed action, say "move me 10 px to the right", and not constant in the way snake is. Does this “constant speed” design change how I should tackle lag mitigation techniques such as client prediction? What about entity interpolation/extrapolation? Any help or guidance would be greatly appreciated.
  4. Twitter - Subreddit - Dev Blog - Discord Hello my name is Sam, I'm also the lead artist for Super Combat Squadron. We're a small indie dev team trying to put out a lightweight browser RTS. It's been just myself and one other programmer been developing the game for about a year now but, due to unforeseen circumstances, the lead programmer has had to take a step away from the project for personal reasons. I'm still in contact with him for help in the transitionary period but I'm looking to replace him on the team with some new programmers. Currently we just completed our first internal playtest and got some good feedback from that, and the roadmap for the future was to push towards the first public demo, and then if the feedback looks good and things work out, were considering running a Kickstarter or other crowdfunding attempt to get the funding to take the game further. The ideal skill set we're looking for would include: Phaser and JavaScript (goes without saying). A previous title or some example of your work with Phaser. Possessing a strong focus on: Networking Pathfinding AI Loves and plays RTS or MOBA games a bonus. Your position in all this would be helping tackle some bugs and featured we identified and fixing some issues blocking a public demo release. We're trying to cut the fat and make it purely a bee-line to public demo release. As previously mentioned, I'd love for it to transition into a longstanding paying role through crowdfunding, worst case scenario we stop at public demo release if the interest just isn't there. If you are interested, you can contact me here, or at our email address: laxvikinggames@gmail.com
  5. As I don't have an external server at the moment, I'm trying to fake a bit of latency using SetTimeout. The code is based on Gabriel Gambetta's code for handling an authoritative server: http://www.gabrielgambetta.com/fpm_live.html And yeah. I know the code is very ugly and prototypish, no handling of multiple users, etc. etc.. I am sending inputs as well as the time the key is pressed, as a way of making it time-independent, but I have no sanity checks. If I keep the networkLatency at 0, it seems to run very smoothly, and both reconciliation and prediction works very well. However, if I add a delay, of say, 200 ms, it behaves strangely, and I can't really see why. This is the code handling the updating of the position, and the prediction based on previously saved inputs, and as far as I can see, this should work: iosocket.on('updatePOS', function(message) { setTimeout(function () { sphereBody.position.x = message.x; sphereBody.position.y = message.y; sphereBody.position.z = message.z; ghostSphere.position.x = message.x; ghostSphere.position.y = message.y; ghostSphere.position.z = message.z; lastSequence = message.last; console.log(lastSequence); for (var x in pending_inputs){ if (x <= lastSequence){ console.log(x); delete pending_inputs[x]; pending_inputs.splice(x, 1); delete inputsToSend[x]; inputsToSend.splice(x, 1); } else { //console.log(x); // console.log(pending_inputs[x]); applyInputs(pending_inputs[x]); } } }, networkLatency); }); Even with a delay/latency, the function should still take into consideration the saved inputs, and apply these when the new positional updates are received. So even when apllying updates as old as 200ms, the result should be the same as the predicted state. If someone can spot where the issue is, that would be awesome. If someone has a VPS with some real latency, a test of that woulld be appreciated too. index.html: <!doctype html> <html> <head> <meta charset="utf-8"> <title>Babylon - Basic scene</title> <style> html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; } #renderCanvas { width: 100%; height: 100%; touch-action: none; } </style> <script src="https://cdnjs.cloudflare.com/ajax/libs/babylonjs/2.4.0/babylon.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.js"></script> <!-- optional physics engine --> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> </head> <body> <canvas id="renderCanvas"></canvas> <script type="text/javascript"> var sphere, inputs = []; var globalGravity = new CANNON.Vec3(0,-30,0); var camera; var lastSequence = 0; var client = {}; client.left = false; client.right = false; client.forward = false; client.back = false; pending_inputs = []; inputsToSend = []; input_sequence_number = -1; var networkLatency = 0; // var keys; // keys.left=1; this.playerDirection = [0,0,0,0]; quat = new CANNON.Quaternion(); var iosocket = io.connect("http://localhost:8080"); iosocket.on('connect', function () { console.log("connect"); iosocket.on('message', function(message) { // console.log(message); }); iosocket.on('updatePOS', function(message) { setTimeout(function () { sphereBody.position.x = message.x; sphereBody.position.y = message.y; sphereBody.position.z = message.z; ghostSphere.position.x = message.x; ghostSphere.position.y = message.y; ghostSphere.position.z = message.z; lastSequence = message.last; console.log(lastSequence); for (var x in pending_inputs){ if (x <= lastSequence){ console.log(x); delete pending_inputs[x]; pending_inputs.splice(x, 1); delete inputsToSend[x]; inputsToSend.splice(x, 1); } else { //console.log(x); // console.log(pending_inputs[x]); applyInputs(pending_inputs[x]); } } }, networkLatency); }); iosocket.on('spawnPlayer', function(player) { console.log("Spawning Player"); }); iosocket.on('disconnect', function() { }); }); var canvas = document.querySelector("#renderCanvas"); var engine = new BABYLON.Engine(canvas, true); // setTimeout(sendInput, 2000); function sendInput(){ console.log("2 sek"); for (var x in inputsToSend){ iosocket.emit('input', inputsToSend[x]); } setTimeout(sendInput, 2000); } var createScene = function () { this.playerDirection = [0,0,0,0]; // Now create a basic Babylon Scene object var scene = new BABYLON.Scene(engine); scene.debugLayer.show(); // Change the scene background color to green. scene.clearColor = new BABYLON.Color3(0, 1, 0); // This creates and positions a free camera camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene); // This targets the camera to scene origin camera.setTarget(BABYLON.Vector3.Zero()); // This attaches the camera to the canvas camera.attachControl(canvas, false); // This creates a light, aiming 0,1,0 - to the sky. var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene); // Dim the light a small amount light.intensity = .5; // Let's try our built-in 'sphere' shape. Params: name, subdivisions, size, scene this.sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene); this.sphere.quaternion = new CANNON.Quaternion(0,0,0,0); this.box = BABYLON.MeshBuilder.CreateBox("box", {height: 5}, scene); // Move the sphere upward 1/2 its height this.sphere.position.y = 2; this.box.position.y = 2.5; this.box.position.z = 2; this.box.parent = this.sphere; ghostSphere = BABYLON.Mesh.CreateSphere("ghost", 16, 2, scene); var ghostMat = new BABYLON.StandardMaterial("texture1", scene); ghostMat.diffuseColor = new BABYLON.Color3(0.5, 0.2, 0.7); ghostMat.alpha = 0.6; ghostSphere.material = ghostMat; // Let's try our built-in 'ground' shape. Params: name, width, depth, subdivisions, scene this.ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene); this.ground.rotation = new CANNON.Vec3(-0,0,0); BABYLON.SceneLoader.ImportMesh("", "https://raw.githubusercontent.com/RaggarDK/Baby/baby/", "height.babylon", scene, function (newMeshes) { setTimeout(function () { ground = newMeshes[1]; var material = new BABYLON.StandardMaterial("texture1", scene); //window.ground = ground; //ground.parent = undefined; ground.scaling.copyFromFloats(.3, .3, .3); ground.bakeCurrentTransformIntoVertices(); material.diffuseTexture = new BABYLON.Texture("https://raw.githubusercontent.com/RaggarDK/Baby/baby/plan.png", scene); ground.material = material; //ground.physicsImpostor = new BABYLON.PhysicsImpostor(ground, BABYLON.PhysicsEngine.HeightmapImpostor, { mass: 0 }, scene); createHeightmap(newMeshes[1]); }, 2000) }); // window.addEventListener("keydown", handleKeyDown, false); // window.addEventListener("keyup", handleKeyUp, false); window.addEventListener("mousemove", function(e) { // console.log("mouse"); event = e; var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; //this.sphereBody.quaternion.y += movementX * 0.002; // this.sphereBody.quaternion.x += movementY * 0.002; sphereBody.rotation.y += movementX * 0.008; sphereBody.rotation.x += movementY * 0.008; }); seq = 0; function handleKeyDown(evt){ seq += 1; if (evt.keyCode==16){ if(inputs){ console.log(inputs); } } if (evt.keyCode==65){ inputs[seq] = {left:1, rot:sphereBody.rotation}; this.playerDirection[0] = 1; applyInput(seq, "left", sphereBody.rotation); console.log(seq); // iosocket.emit('input', { left:1, rot:sphereBody.rotation}); // shootBullet(sphereBody.rotation.y, sphereBody.position); } if (evt.keyCode==68){ sphereBody.position.x += .1; inputs[seq] = {right:1, rot:sphereBody.rotation}; this.playerDirection[1] = 1; //applyInput(seq, "right", sphereBody.rotation); //iosocket.emit('input', { right:1, rot:sphereBody.rotation}); } if (evt.keyCode==87){ inputs[seq] = {forward:1, rot:sphereBody.rotation}; applyInput(seq, "forward", sphereBody.rotation); this.playerDirection[2] = 1; // iosocket.emit('input', { forward:1, rot:sphereBody.rotation}); } if (evt.keyCode==83){ inputs[seq] = {back:1, rot:sphereBody.rotation}; this.playerDirection[3] = 1; applyInput(seq, "back", sphereBody.rotation); // iosocket.emit('input', { back:1, rot:sphereBody.rotation}); } } function handleKeyUp(evt){ if (evt.keyCode==65){ this.playerDirection[0] = 0; // iosocket.emit('input', { left:0}); } if (evt.keyCode==68){ // iosocket.emit('input', { right:0}); this.playerDirection[1] = 0; } if (evt.keyCode==87){ // iosocket.emit('input', { forward:0}); this.playerDirection[2] = 0; } if (evt.keyCode==83){ // iosocket.emit('input', { back:0}); this.playerDirection[3] = 0; } } var keyHandler = function(e) { e = e || window.event; if (e.keyCode == 65) { console.log("Lefty"); client.left = (e.type == "keydown"); } if (e.keyCode == 68) { console.log("righty"); client.right = (e.type == "keydown"); } if (e.keyCode == 87) { console.log("forward"); client.forward = (e.type == "keydown"); } else if (e.keyCode == 83) { client.back = (e.type == "keydown"); console.log("back"); } }; window.addEventListener("keydown", keyHandler, false); window.addEventListener("keyup", keyHandler, false); return scene; }; var createPhysics = function(){ console.log("createPhysicsFunction Called!"); world = new CANNON.World(); world.gravity.set(0,-30,0); world.broadphase = new CANNON.NaiveBroadphase(); var mass = 5, radius = 1; sphereShape = new CANNON.Sphere(radius); // Step 1 sphereBody = new CANNON.Body({mass: 1, shape: sphereShape}); // Step 2 sphereBody.position.set(0,2,2); sphereBody.rotation = new CANNON.Vec3(); world.add(sphereBody); // Step 3 console.log("sphereBodyPosition0: " + sphereBody.position); sphereBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); groundShape = new CANNON.Plane(); groundBody = new CANNON.Body({ mass: 0, shape: groundShape }); console.log(groundBody.rotation); groundBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); console.log(groundBody.quaternion); world.add(groundBody); timeStep = 1.0 / 60.0; // seconds velocity = sphereBody.velocity; sphereBody.velocity = new CANNON.Vec3(0,0,0); return world; } var scene = createScene(); this.world = createPhysics(); var zeroVel = new CANNON.Vec3(0,0,0); var inputVelocity = new CANNON.Vec3(); var lastTime = (new Date()).getTime(); var currentTime = 0; var delta = 0; engine.runRenderLoop(function () { currentTime = (new Date()).getTime(); delta = (currentTime - lastTime) / 1000; // console.log(delta); processInputs(); scene.render(); world.step(1.0 / 60.0); velocity = sphereBody.velocity; this.sphere.position = sphereBody.position; this.sphere.rotation = sphereBody.rotation; moveSpeed = .1; // camera.position.x = sphereBody.position.x // camera.position.z = sphereBody.position.z // camera.rotation = sphereBody.rotation; //sphereBody.position.z += moveSpeed; //console.log(sphereBody.position.x); velocity.x = inputVelocity.x; velocity.z = inputVelocity.z; lastTime = currentTime; }); window.addEventListener("resize", function () { engine.resize(); }); function processInputs() { // Compute delta time since last update. var now_ts = +new Date(); var last_ts = this.last_ts || now_ts; var dt_sec = (now_ts - last_ts) / 1000.0; this.last_ts = now_ts; // Package player's input. var input; if (client.right) { input = {dir:"right", press_time: dt_sec }; }else if (client.forward && client.left) { input = {dir:"sfLeft", press_time: dt_sec }; } else if (client.left) { input = {dir:"left", press_time: dt_sec }; }else if (client.forward) { input = {dir:"forward", press_time: dt_sec }; }else if (client.back) { input = {dir:"back", press_time: dt_sec }; } else { // Nothing interesting happened. return; } // Send the input to the server. input.input_sequence_number = input_sequence_number++; console.log(input.input_sequence_number); // input.entity_id = this.entity_id; applyInputs(input); inputsToSend.push(input); iosocket.emit('input', input); // Save this input for later reconciliation. pending_inputs.push(input); } function applyInputs(input) { // console.log(input.dir); if (input.dir == "left"){ sphereBody.position.x += -input.press_time*5; } if (input.dir == "right"){ sphereBody.position.x += input.press_time*5; } if (input.dir == "forward"){ sphereBody.position.z += input.press_time*5; } if (input.dir == "back"){ sphereBody.position.z += -input.press_time*5; } if (input.dir == "sfLeft"){ sphereBody.position.z += input.press_time*2.5; sphereBody.position.x += -input.press_time*2.5; } } function createHeightmap (object, pointDepth) { var pos = object.getVerticesData(BABYLON.VertexBuffer.PositionKind); var matrix = []; //For now pointDepth will not be used and will be automatically calculated. //Future reference - try and find the best place to add a reference to the pointDepth variable. var arraySize = pointDepth || ~~(Math.sqrt(pos.length / 3) - 1); var dim = Math.min(object.getBoundingInfo().boundingBox.extendSize.x, object.getBoundingInfo().boundingBox.extendSize.z); var elementSize = dim * 2 / arraySize; var minY = object.getBoundingInfo().boundingBox.extendSize.y; for (var i = 0; i < pos.length; i = i + 3) { var x = Math.round((pos[i + 0]) / elementSize + arraySize / 2); var z = Math.round(((pos[i + 2]) / elementSize - arraySize / 2) * -1); var y = pos[i + 1] + minY; if (!matrix[x]) { matrix[x] = []; } if (!matrix[x][z]) { matrix[x][z] = y; } matrix[x][z] = Math.max(y, matrix[x][z]); } for (var x = 0; x <= arraySize; ++x) { if (!matrix[x]) { var loc = 1; while (!matrix[(x + loc) % arraySize]) { loc++; } matrix[x] = matrix[(x + loc) % arraySize].slice(); } for (var z = 0; z <= arraySize; ++z) { if (!matrix[x][z]) { var loc = 1; var newValue; while (newValue === undefined) { newValue = matrix[x][(z + loc++) % arraySize]; } matrix[x][z] = newValue; } } } var shape = new CANNON.Heightfield(matrix, { elementSize: elementSize }); console.log(elementSize); //Output Matrix Info for Node.js console.log(matrix.length); for (i=0;i<matrix.length;i++){ //GET MATRIX DATA //console.info(JSON.stringify(matrix[0], null, ' ')) console.log("\n" + "matrix2[" + i + "] = " + JSON.stringify(matrix[i], null, ' ')) } heightBody = new CANNON.Body({mass: 0, shape: shape}); // Step 2 heightBody.position.y = -4.5; heightBody.position.x = -30; heightBody.position.z = 30; heightBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); console.log(heightBody.position); world.add(heightBody); }; </script> </body> </html> server.js: /* Copyright (c) 2012 Sven "FuzzYspo0N" Bergström http://underscorediscovery.com MIT Licensed. See LICENSE for full license. Usage : node simplest.app.js */ var gameport = process.env.PORT || 4004, CANNON = require('cannon'), io = require('socket.io'), socketio = require('socket.io'), express = require('express'), UUID = require('node-uuid'), fs = require('fs'), gameloop = require('node-gameloop'), verbose = false; var fs = require('fs') , http = require('http') , socketio = require('socket.io'); var server = http.createServer(function(req, res) { res.writeHead(200, { 'Content-type': 'text/html'}); res.end(fs.readFileSync(__dirname + '/index.html')); }).listen(8080, function() { console.log('Listening at: http://localhost:8080'); }); timeStep = 1.0 / 60.0; moveSpeed = .1; lastProcessedInput = -1; socketio.listen(server).on('connection', function (socket) { socket.emit('spawnPlayer'); var testLat = 0; var networkLoop = gameloop.setGameLoop(function(delta) { //socket.emit('news', { x: sphereBody.position.x, y: sphereBody.position.y, z: sphereBody.position.z, last:lastProcessedInput}); socket.emit('updatePOS', { x: sphereBody.position.x, y: sphereBody.position.y, z: sphereBody.position.z, last:lastProcessedInput}); }, 1000 / 10); socket.on('input', function (inp) { applyInput(inp); console.log(inp.input_sequence_number); }); sphereBody.velocity = new CANNON.Vec3(0,0,0); /*socket.on('arrayInput', function (ainput) { for (var input in ainput){ if (ainput.input_sequence_number > lastProcessedInput){ applyInput(ainput[input]); } } }); */ }); function applyInput(inp){ sphereBody.velocity.x = 0; if (inp.dir == "right"){ sphereBody.position.x += inp.press_time*5; } if (inp.dir == "sfLeft"){ sphereBody.position.z += inp.press_time*2.5; sphereBody.position.x += -inp.press_time*2.5; } if (inp.dir == "left"){ sphereBody.position.x += -inp.press_time*5; } if (inp.dir == "forward"){ sphereBody.position.z += inp.press_time*5; } if (inp.dir == "back"){ sphereBody.position.z += -inp.press_time*5; } lastProcessedInput = inp.input_sequence_number; console.log(lastProcessedInput); } var initPhysics = function() { world = new CANNON.World(); world.gravity.set(0,-30,0); world.broadphase = new CANNON.NaiveBroadphase(); var mass = 5, radius = 1; sphereShape = new CANNON.Sphere(radius); sphereBody = new CANNON.Body({mass: 1, shape: sphereShape}); sphereBody.position.set(0,2,2); sphereBody.rotation = new CANNON.Vec3(); world.add(sphereBody); sphereBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); createHeightfield(); } var frameCount = 0; var mainLoop = gameloop.setGameLoop(function(delta) { // console.log('Hi there! (frame=%s, delta=%s)', frameCount++, delta); world.step(timeStep); // console.log(sphereBody.position); if (sphereBody){ velocity = sphereBody.velocity; //sphereBody.velocity.z = 0; var inputVelocity = new CANNON.Vec3(); sphereBody.velocity.x = 0; sphereBody.velocity.z = 0; velocity.x = inputVelocity.x; velocity.z = inputVelocity.z; } }, 1000 / 60); function createHeightfield(){ matrix2 = []; elementSize = 2.9999426007270813; matrix2[0] = [ 10.294134842548655, 10.294077302543254, 10.294019762537854, 10.293962222532453, 10.293904682527053, 10.293847142521653, 10.293789602516252, 10.293732062510852, 10.293674522505452, 10.293616982500051, 10.29355944249465, 10.29350190248925, 10.29344436248385, 10.29338682247845, 10.29332928247305, 10.293271742467649, 10.293214202462249, 10.293156662456848, 10.293099122451448, 10.293041582446047, 10.292984042440647 ]; matrix2[1] = [ 10.294134842548655, 10.294077302543254, 10.294019762537854, 10.293962222532453, 10.293904682527053, 10.293847142521653, 10.293789602516252, 10.293732062510852, 10.293674522505452, 10.293616982500051, 10.29355944249465, 10.29350190248925, 10.29344436248385, 10.29338682247845, 10.29332928247305, 10.293271742467649, 10.293214202462249, 10.293156662456848, 10.293099122451448, 10.293041582446047, 10.292984042440647 ]; matrix2[2] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[3] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[4] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[5] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 1.260629979194317, 1.2605724391889166, 1.2605148991835162, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[6] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 1.260629979194317, 1.2605724391889166, 1.2605148991835162, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[7] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 1.260629979194317, 1.2605724391889166, 1.2605148991835162, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[8] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[9] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 4.516292111633966, 4.516234571628566 ]; matrix2[10] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 4.516292111633966, 4.516234571628566 ]; matrix2[11] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 4.516292111633966, 4.516234571628566 ]; matrix2[12] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[13] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[14] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 7.674529389168344, 7.674471849162944, 7.674414309157544, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[15] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 7.674529389168344, 7.674471849162944, 7.674414309157544, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[16] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 7.674529389168344, 7.674471849162944, 7.674414309157544, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[17] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[18] = [ 10.294134842548655, 10.294077302543254, 4.517270291725772, 4.517212751720372, 4.517155211714972, 4.517097671709571, 4.517040131704171, 4.5169825916987705, 4.51692505169337, 4.51686751168797, 4.516809971682569, 4.516752431677169, 4.516694891671769, 4.516637351666368, 4.516579811660968, 4.5165222716555675, 4.516464731650167, 4.516407191644767, 4.516349651639366, 10.293041582446047, 10.292984042440647 ]; matrix2[19] = [ 10.294134842548655, 10.294077302543254, 10.294019762537854, 10.293962222532453, 10.293904682527053, 10.293847142521653, 10.293789602516252, 10.293732062510852, 10.293674522505452, 10.293616982500051, 10.29355944249465, 10.29350190248925, 10.29344436248385, 10.29338682247845, 10.29332928247305, 10.293271742467649, 10.293214202462249, 10.293156662456848, 10.293099122451448, 10.293041582446047, 10.292984042440647 ]; matrix2[20] = [ 10.294134842548655, 10.294077302543254, 10.294019762537854, 10.293962222532453, 10.293904682527053, 10.293847142521653, 10.293789602516252, 10.293732062510852, 10.293674522505452, 10.293616982500051, 10.29355944249465, 10.29350190248925, 10.29344436248385, 10.29338682247845, 10.29332928247305, 10.293271742467649, 10.293214202462249, 10.293156662456848, 10.293099122451448, 10.293041582446047, 10.292984042440647 ]; shape = new CANNON.Heightfield(matrix2, { elementSize: elementSize }); heightBody = new CANNON.Body({mass: 0, shape: shape}); // Step 2 heightBody.position.y = -4.5; heightBody.position.x = -30; heightBody.position.z = 30; heightBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1,0,0),-Math.PI/2); console.log(heightBody.position); world.add(heightBody); } initPhysics(); Node.js for the server.js Chrome for the index.html WASD to move around.
  6. As my little project is done using Babylon, I thought this would be the most fitting and active section. If not, mods may move the topic as they please. I am unsure as to how it is possible to apply Server Reconciliation when using physics to drive the movement, as I am using Cannon.js. The examples I've seen on SR are based on movement, by first applying the confirmed position, followed by applying all the unconfirmed input, and thereby smoothing the movement of a local player in a multiplayer setup. I don't really see how this is possible, as by far as I know, Cannon won't be able to simulate ahead of time, and by using velocities/forces/impulses, there is bound to be time involved due to acceleration etc. 1) Is it possible, and has it been done using either Cannon.js, or other of the physics implementations? 2) Are there any other ways of dealing with the apparent jitter/lag? Prediction is good as Cannon is deterministic, but the updates still create a very laggy feel. A good example is this: http://www.gabrielgambetta.com/fpm_live.html Turning on prediction, you'll get results identical to what I have now, but applying unconfirmed input after the confirmed server snapshot simply makes the movement seem slow and unresponsive.
  7. I have am running a 3D multiplayer physics game where I currently send the input (4 keys, position: x,y,z and velocity x,y) to the clients (8 clients in total) 30 times a second. Is this considered to be a bit too much? Also, I assume changing (1) to (2) would decrease the size of data that is sent? (1) var keys = {l:true, r:true, u:true, d:true}; (2) var keys = {l:1, r:t1, u:1, d:1};
  8. Server to client update rate

    I am building a game that uses client-server architecture and have the following question regarding server to client updates. Currently the server is doing 60 ticks per second and is sending updates to the connected clients after every step, this means information about every entity that is in the world. Since my game is rather slow and rts based, a lot of the entities do not change state after every step and do not need to be updated. So to reduced server load I added an update queue. Only entities that require an update are added to this queue and it is executed after each step. Looking at this approach I wonder if there are any significant disadvantages to it. One I could think of is that maybe as the time goes by and the game gets more complicated all the entities will need to be updated on every step anyway and that might be just a waste of time to implement.
  9. Phaser features

    Hi. I'm actually new to HTML gaming, but I definitely see the potential. The thing is - until now, I've mostly used Java (libGDX framework) and, well, networking might be an issue if you want to (for example) use GWT. So, before I start digging into Phaser and JavaScript, I'd like to ask two questions: 1. Does this framework provide a networking API that would actually work on all browsers and isn't horribly complicated? Classic server-clients structure is fine for me. 2. Does it have a GUI API, preferably one that supports ninepatches? Sorry for wasting your time, take care. ; )
  10. We’re excited to announce the alpha release of AppWarpS2, our on-premise gaming server solution! We launched AppWarp cloud less than a year ago and have seen great adoption in the multiplayer developer community. One of the features often requested was the ability to host and extend the server side by the developers themselves. The release of AppWarpS2 fulfills those requirements. AppWarpS2 allows developers to tap in and extend the same server engine that powers our cloud product. Using our server side libraries and SDK, developers have the ability to build their own full-fledged Java game server. Popular cloud features of matchmaking, room properties, arbitration, connection resiliency etc. are all available out of the box. On the client side, developers will use the same cross-platform SDKs (iOS, Android, WP, Flash, Unity, Marmalade etc.) that they use when developing for the cloud. AppWarpS2 comes with great getting started guides and samples for different scenarios and client platforms. From simple chat server to MMO applications and casino style card game samples are included in the package. Conforming with ShepHertz developer-friendly strategy, AppWarpS2 will be free for developers up-to 500 CCUs. This is a high free threshold which spares developers of having to worry about license costs unless their games become wildly successful. Visit our AppWarpS2 page to register, log-in and download the setup. Contact us on support@shephertz.comfor any further queries.