Search the Community
Showing results for tags 'networking'.
-
Hi everyone! I'm working on the fast paced multiplayer browser pvp game with following gamedev stack. Angular Website -> global place for the game, responsible for: user account state, game UI, user info etc. (REST and WebSocket connections) Client Game Engine -> based on ECS and PIXI rendering with custom gameloop, client-side- predition and all that stuff. (WebRTC Geckos.io connections.) Nest.js -> backend for angular website, chat, matchmaking, creating services with Server Game Engines when players are ready to play. (REST api and WebSocket connections) Authoritative Server Game Engine -> shared a lot with client, based on ECS etc (WebRTC Geckos.io connections.) MongoDB -> DB for everything. Any ideas / guides on how to scale things up? I read about load balancers using reverse-proxy on nginx and pm2, node.js task partitioning and offloading also microservices with redis. But still dont know if i'm understaning this correctly. For example: 2 players want to play with each other -> new instance of ecs world will be created with all physics etc, this instance has a server game loop with examplary 50 ticks per second, taking from 10% to 100% cpu usage depending on how many entities are currently in game world. If i understand correctly when server game loop is running its blocking the node.js event loop so no other task can be done. So if another 2 players would want to play i would need to create new game server instance in the best case scenario on the second core in the worst on the new physical server. To make this work I would need to have a load balancer and some kind of management over creating and removing new server game engine instances. Maybe game engine should be microservice and Nest.js backend would be the main service which would delegate users to game engine instances? It's very odd looking to me that on 8 core CPU only 16 players could play in the same time. Is there any diffrent way i should go with this to enable more CCU playing? Maybe serverless? Any advice on how to scale server side gameloops which are resource demanding on node would be appreciated :). Sorry for my english and overall knowledge (new in networking and backend world!)
-
Los Angeles, CA— Lucid Sight, the independent game studio behind MLB Champions, CSC with Star Trek, Herocade, Polyrunner, and several other games spanning VR, AR, and NFTs is announcing that it has raised $2.58 million to launch a cloud-hosted multiplayer service called Colyuseus Arena. Colyseus Arena is a fully managed solution that handles server management, infrastructure, and scaling so that game makers can focus on game design and development. The funding round was led by Galaxy Interactive. Other investors included VamosVentures, Goal Venture Partners, Gaingels, as well as existing investors. The new round brings total raised to $15.4 Million. Earlier this year Lucid Sight acquired Colyseus, the most popular open-source Node.js multiplayer framework. The Colyseus open-source framework has had over 300,000 downloads since it was created six years ago and averages 10,000 downloads per month. Lucid Sight created Colyseus Arena because the number one request from Colyseus game developers was a cloud-hosted solution for Colyseus. Colyseus Arena reduces or eliminates the need for dedicated networking and DevOps engineers in multiplayer games. “Networking and DevOps engineers have always created a $500k to $1MM annual drag on every multiplayer game Lucid Sight has ever created. Our goal with Arena is to bring that cost way down, so more styles of casual, hypercasual, and NFT games can afford rich multiplayer experiences. Colyseus will always be open-source and we made the lowest tier of Arena FREE because we wanted no barrier for developers to dive in and GET TO FUN FASTER.” said Lucid Sight CEO Randy Saaf. With Colyseus Arena, game developers can set up, manage and update their servers with a few clicks from an intuitive administration dashboard. Features include: Get started for FREE with an Indie/Dev plan. Easily manage your servers and add server-side logic from an intuitive dashboard. Optimized server configuration and infrastructure setup. Global availability with 7 data centers and regional access points. Worry-free scaling with tier 4 and up plans so when your game peaks there will be no interruptions. Enterprise-grade load balancing & custom matchmaking. 24/7 monitoring & DDoS Protection DevOps as a service for non-Colyseus users Host your custom Linux Servers. Host Unity / Unreal headless server. Get fully customized solutions for projects of all sizes. The simple setup allows you to GET TO FUN FASTER™. Lucid Sight began accepting Colyseus Arena Early Access sign-ups in Feb 2021 and has since amassed hundreds of customers with games supporting millions of Daily Active Users. Customers of note include Lightfox Games (Knight’s Edge), PM Studios (Squish), Tobspr (SchoolBreak.io), Kirka.io, and massively popular Indian streaming and short-form UGC video platform MX Media & Entertainment. “We are excited to use Colyseus Arena as the managed multiplayer server solution for Knight’s Edge. We see Lucid Sight as a valuable partner as we begin the journey of growing our game and supporting our players'' said Lightfox CEO Ryan Hanft-Murphy. Colyseus Arena can be used by all types of game developers but it proves uniquely powerful for multiplayer games that require a flexible server hosting solution. By utilizing a modern containers-centric technology stack, developers have instant scalability with a pay-for-usage billing model. "One of the fastest growing verticals we see for Colyseus Arena is our hosting of web, quick-play, instant-play games. These types of games typically run within another application or standalone on a mobile accessible website. For these games a flexible hosting plan that has the ability to scale from 1 CPU to 1000s CPUs and then back down again in a short period of time is essential and a service Arena provides. Combine this with Colyseus's small resource footprint and simple JavaScript programing language and you quickly see why Colyseus Arena provides the BEST solution for these instant play style multiplayer games." said Lucid Sight CTO Fazri Zubair. Lucid Sight has also seen Colyseus and Colyses Arena usage by non-game developers. Projects such as virtual office and event platform Teamflow, innovative school enrichment program Synthesis, and an immersive VR sports experience created by VRGlass are all examples of how Colyseus is being used by a wide variety of non-gaming developers. Lucid Sight is also using its experience as an innovator in the NFT space to provide Colyseus Arena to a number of NFT and metaverse projects. Interested developers can begin making multiplayer games and experiences by joining Arena Free to start.
-
- lucidsight
- indiedev
-
(and 4 more)
Tagged with:
-
Please bear with me, I am completely new to game networking. Any help and improvise in this question will be appreciated!!! I have been playing around with BJS (babylon.js) for a while now, wanting to make a real-time multiplayer game. I have been searching around the web about game networking, and read this: https://github.com/gafferongames/gafferongames/blob/master/content/post/what_every_programmer_needs_to_know_about_game_networking.md It gave me a clear idea of how should I go ahead. But in all the forums, I hear people saying game networking is very hard, not to waste time on it rn, and stuff. So I thought to give it a try. I used node.js for server with socket.io to communicate to the client back and forth. What I did was, when I receive the server's update about the location, I would compare that to the location of the client (which was predicted, or more like comprehended on the client side) to check whether the difference was under 0.1 (or any number). If it was not, I would redirect the player back to location sent by the server. (there is still some minor jitter in the gameplay) After doing the above, I felt it like a piece of cake. I felt on top of the world! But, I was still trying to figure out what people really meant by "hard". So after bit of more research, I found out nengi.js, a game networking engine for node.js. I noticed people comparing socket.io and nengi.js. Aren't they two different things? socket.io is used for bidirectional communication between client and server, and nengi.js is a game networking engine! This has created a huge confusion in my head. Could anyone please help me with this? Also, please clarify whether the process I did above for client prediction is correct or not? If you need anymore details/info, please let me know! Thanks a lot for reading through! Thanks in advance!
-
I have multiple servers in multiple locations. I need to get the frequency at which the websocket messages are received. When I connect to a low-latency server, the frequency is normal (50 - 60 ms). But on high latency servers, the frequency sometimes is 0. I asked a similar question not to long ago, but the answer there was that the socket is buffering messages. I find this unlikely since it only happens on high latency servers. Here is the code responsible for handling the websocket: startTime = Date.now(); ws.onmessage = function (evt) { prevData = recivedData; var receivedMsg = evt.data; recivedData = JSON.parse(receivedMsg); const endTime = Date.now(); ms = endTime - startTime; startTime = endTime; if(msAvg == null){ msAvg = ms; } msAvg = Math.round(((msAvg * 5) + ms) / 6); id = recivedData.id; } ms is the frequency in milliseconds. How can I get to the bottom of this issue?
-
Im receiving websocket messages on my webpage, and i'm trying to calculate the frequency at which they are received. I do this like so: let startTime = new Date(); ws.onmessage = function (evt) { prevData = recivedData; var receivedMsg = evt.data; recivedData = JSON.parse(receivedMsg); const endTime = new Date(); const timeDif = endTime - startTime; startTime = endTime; console.log(timeDif) } But it seems timeDif sometimes is 0, and I find this unlikely. People in other questions of mine have sad that its due to the websocket buffering incoming messages. How do I prevent this?
- 11 replies
-
- networking
- websocket
-
(and 1 more)
Tagged with:
-
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.
- 4 replies
-
- networking
- websockets
-
(and 1 more)
Tagged with:
-
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.
-
- networking
- phaser
-
(and 5 more)
Tagged with:
-
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: [email protected]
- 3 replies
-
- ai
- pathfinding
- (and 10 more)
-
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.
- 4 replies
-
- networking
- socket.io
-
(and 2 more)
Tagged with:
-
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.
-
- cannon.js
- server reconciliation
-
(and 2 more)
Tagged with:
-
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 replies
-
- physics
- networking
-
(and 3 more)
Tagged with:
-
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.
- 1 reply
-
- networking
- client-server
-
(and 3 more)
Tagged with:
-
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. ; )
- 1 reply
-
- phaser
- networking
-
(and 2 more)
Tagged with: