Skip to content

oppenheimj/openplanet-public

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Openplanet

Openplanet is a containerized network-accessible procedurally generated 3D world. cover

Introduction

The purpose of this project was to learn how to build a multiplayer procedurally generated 3D world. The result is a constallation of microservices tied together using docker-compose that together generate such a browser-based, network-accessible world. The project is currently deployed at https://www.openplanet.com but may also be run locally in development mode, as described below. This readme includes setup instructions, an overview of the architecture, and things to experiment with in the codebase.

Quick start

  1. Install Docker Desktop.
  2. Clone the code.
    $ git clone git@github.com:oppenheimj/openplanet-public.git
    
  3. In websockets/ and webpage/ folders, copy the .env_example file and rename .env.
  4. Build and launch using docker-compose.
    $ docker-compose build
    $ docker-compose up
    
  5. Browse to http://localhost:8080.

Architecture

Each of the four microservices are fully contained in the four top-level folders, nginx/, skins/, webpage/, and websockets/. What follows is a brief description of each microservice and then a description of overall code flow.

architecutre

Nginx

Nginx is responsible for routing incoming traffic to the proper microservice. The production environment uses HTTPS and WSS, enabled by a TLS certificate from Let's Encrypt. There are separate nginx.{dev, prod}.conf files and secure protocols aren't used when running the code locally, for ease of development. The nginx.prod.conf file is excluded from version control for security.

Webpage

This is the first microservice hit by the client's browser. This is a simple Node.js Express API with one endpoint, which returns the bundle of html and javascript. It is bundled using webpack. Terrain is efficiently loaded using multiple Web Workers.

Skins

The skin server is another simple Express API used for fetching mesh data. It contains a subdirectory called /skins, which is where various .obj files live.

Websockets

This is the primary game server and has two responsibilities. First it maintains websocket connections with clients and handles receiving and forwarding look and position vector updates to other clients. Second, handles terrain generation and forwarding of terrain chunk data to clients. Terrain is generated procedurally using two dimensional fractal brownian motion, by many threads.

Code flow

  1. The user points their browser to http://localhost:8080.
  2. Nginx forwards the request to the webpage microservice, which return a bundle of html and javacript to the browser.
  3. The client begins executing the browser code, which makes requests to the skins server for various skins and to the websockets server to establish a connection.

As the player moves around in the world, their position and look vector updates are sent to the websockets server, which forwards those updates to other clients. Terrain chunks are generated by the websockets server and sent to the client as needed. The server maintains a cache of all terrain chunks that have ever been generated and keeps track of which clients have received which terrain chunks. The client also caches terrain chunks it has received, but only renders those chunks in the immediate area. So a terrain chunk is only generated once, and only sent to a particular client once.

Things to experiment with

Play with the procedural noise function

  1. Parameters for the FBM can be modified in /websockets/server/chunk.go
  2. Constants DIM, RES, and NRENDER must agree between /websockets/server/terrain.go and webpage/src/TerrainManager.js. This is tech debt.

Modify the shaders

Shaders are located in webpage/src/shaders.js.

Add more skins

  1. Add .obj file to skins/skins/
  2. Add new endpoint to skins/src/api.js
  3. Add skin inside file webpage/src/main.js and function loadSkins().
  4. Create new file inside webpage/src/{SkinName}.js at model it after one of the others, like Cow.js.
  5. Modify the otherPlayer.setGeometry() call inside webpage/src/main.js to use the new skin.

Examples

What follows are examples of neat parameter combinations to modify the terrain.

fbm2D(xPos/4096, zPos/4096, 0.95)

cover

fbm2D(xPos/16384, zPos/16384, 0.98)

cover