Subscribe to PHP Freaks RSS

Game Development with React and PHP: How Compatible Are They?

syndicated from www.sitepoint.com on September 13, 2017

"I'd like to make a multiplayer, economy-based game. Something like Stardew Valley, but with none of the befriending aspects and a player-based economy."

I started thinking about this the moment I decided to try and build a game using PHP and React. The trouble is, I knew nothing about the dynamics of multiplayer games, or how to think about and implement player-based economies.

Stardew valley

I wasn't even sure I knew enough about React to justify using it. I mean, the initial interface --- where I focus heavily on the server and economic aspects of the game --- is perfectly suited for React. But what about when I start to make the farming /interaction aspects? I love the idea of building an isometric interface around the economic system.

I once watched a talk by dead_lugosi, where she described building a medieval game in PHP. Margaret inspired me, and that talk was one of the things that led to me writing a book about JS game development. I became determined to write about my experience. Perhaps others could learn from my mistakes in this case, too.


The code for this part can be found at: github.com/assertchris-tutorials/sitepoint-making-games/tree/part-1. I've tested it with PHP 7.1 and in a recent version of Google Chrome.


Setting Up the Back End

The first thing I searched for was guidance on building multiplayer economies. I found an excellent Stack Overflow thread in which folks explained various things to think about. I got about halfway through it before realizing I may have been starting from the wrong place.

"First things first: I need a PHP server. I'm going to have a bunch of React clients, so I want something capable of high-concurrency (perhaps even WebSockets). And it needs to be persistent: things must happen even when players aren't around."

I went to work setting up an async PHP server --- to handle high concurrency and support WebSockets. I added my recent work with PHP preprocessors to make things cleaner, and made the first couple of endpoints.

From config.pre:

$host = new Aerys\Host();
$host->expose("*", 8080);

$host->use($router = Aerys\router()); $host->use($root = Aerys\root(.."/public"));

$web = process .."/routes/web.pre"; $web($router);

$api = process .."/routes/api.pre"; $api($router);

I decided to use Aerys for the HTTP and WebSocket portions of the application. This code looked very different from the Aerys docs, but that's because I had a good idea about what I needed.

The usual process for running an Aerys app was to use a command like this:

vendor/bin/aerys -d -c config.php

That's a lot of code to keep repeating, and it didn't handle the fact that I wanted to use PHP preprocessing. I created a loader file.

From loader.php:

return Pre\processAndRequire(__DIR__ . "/config.pre");

I then installed my dependencies. This is from composer.json:

"require": {
  "amphp/aerys": "dev-amp_v2",
  "amphp/parallel": "dev-master",
  "league/container": "^2.2",
  "league/plates": "^3.3",
  "pre/short-closures": "^0.4.0"
},
"require-dev": {
  "phpunit/phpunit": "^6.0"
},

I wanted to use amphp/parallel, to move blocking code out of the async server, but it wouldn't install with a stable tag of amphp/aerys. That's why I went with the dev-amp_v2 branch.

I thought it would be a good idea to include some sort of template engine and service locator. I opted for PHP League versions of each. Finally I added pre/short-closures, both to handle the custom syntax in config.pre and the short closures I planned on using after…

Then I set about creating routes files. From routes/web.pre:

use Aerys\Router;
use App\Action\HomeAction;

return (Router $router) => { $router->route( "GET", "/", new HomeAction ); };

And, from routes/api.pre:

use Aerys\Router;
use App\Action\Api\HomeAction;

return (Router $router) => { $router->route( "GET", "/api", new HomeAction ); };

Though simple routes, these helped me to test the code in config.pre. I decided to make these routes files return closures, so I could pass them a typed $router, to which they could add their own routes. Finally, I created two (similar) actions.

From app/Actions/HomeAction.pre:

namespace App\Action;

use Aerys\Request; use Aerys\Response;

class HomeAction { public function __invoke(Request $request, Response $response) { $response->end("hello world"); } }

One final touch was to add shortcut scripts, to launch dev and prod versions of the Aerys server.

From composer.json:

"scripts": {
  "dev": "vendor/bin/aerys -d -c loader.php",
  "prod": "vendor/bin/aerys -c loader.php"
},
"config": {
  "process-timeout": 0
},

With all of this done, I could spin up a new server, and visit http://127.0.0.1:8080 just by typing:

composer dev

Continue reading %Game Development with React and PHP: How Compatible Are They?%