├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock ├── docs ├── architecture.md ├── demo-2.png ├── demo.png ├── hhvmcraft.png └── hhvmcraft_logo.png ├── server.php ├── src └── HHVMCraft │ ├── API │ ├── BlockProvider.php │ ├── BlockRepository.php │ ├── Coordinates2D.php │ ├── Coordinates3D.php │ ├── CraftingRepository.php │ ├── ItemRepository.php │ ├── ItemStack.php │ └── Vector3.php │ └── Core │ ├── Entities │ ├── Entity.php │ ├── EntityManager.php │ ├── LivingEntity.php │ └── PlayerEntity.php │ ├── Helpers │ ├── Hex.php │ └── Logger.php │ ├── Networking │ ├── Client.php │ ├── Connection.php │ ├── Handlers │ │ ├── ChatHandler.php │ │ ├── DataHandler.php │ │ ├── LoginHandler.php │ │ └── PlayerHandler.php │ ├── MultiplayerServer.php │ ├── PacketHandler.php │ ├── PacketReader.php │ ├── Packets │ │ ├── AnimationPacket.php │ │ ├── AttachEntityPacket.php │ │ ├── BlockChangePacket.php │ │ ├── ChatMessagePacket.php │ │ ├── ChunkDataPacket.php │ │ ├── ChunkPreamblePacket.php │ │ ├── CloseWindowPacket.php │ │ ├── CollectItemPacket.php │ │ ├── DestroyEntityPacket.php │ │ ├── DisconnectPacket.php │ │ ├── EntityActionPacket.php │ │ ├── EntityEquipmentPacket.php │ │ ├── EntityMetadataPacket.php │ │ ├── EntityRelativeMovePacket.php │ │ ├── EntityStatusPacket.php │ │ ├── EntityTeleportPacket.php │ │ ├── EntityVelocityPacket.php │ │ ├── HandshakePacket.php │ │ ├── HandshakeResponsePacket.php │ │ ├── HoldingChangePacket.php │ │ ├── KeepAlivePacket.php │ │ ├── LoginRequestPacket.php │ │ ├── LoginResponsePacket.php │ │ ├── OpenWindowPacket.php │ │ ├── PickupSpawnPacket.php │ │ ├── PlayerBlockPlacementPacket.php │ │ ├── PlayerDiggingPacket.php │ │ ├── PlayerGroundedPacket.php │ │ ├── PlayerLookPacket.php │ │ ├── PlayerPositionAndLookPacket.php │ │ ├── PlayerPositionPacket.php │ │ ├── RespawnPacket.php │ │ ├── SetPlayerPositionPacket.php │ │ ├── SetSlotPacket.php │ │ ├── SoundEffectPacket.php │ │ ├── SpawnPlayerPacket.php │ │ ├── SpawnPositionPacket.php │ │ ├── TimeUpdatePacket.php │ │ ├── UpdateHealthPacket.php │ │ ├── UpdateProgressBarPacket.php │ │ ├── UpdateSignPacket.php │ │ ├── UseBedPacket.php │ │ ├── UseEntityPacket.php │ │ └── WindowItemsPacket.php │ └── StreamWrapper.php │ ├── Physics │ └── PhysicsEngine.php │ ├── TerrainGen │ └── FlatlandGenerator.php │ ├── Windows │ ├── ArmorWindowArea.php │ ├── CraftingWindowArea.php │ ├── InventoryWindow.php │ ├── Window.php │ └── WindowArea.php │ └── World │ ├── Chunk.php │ ├── NBTProvider.php │ ├── Region.php │ └── World.php └── tests └── Core ├── Networking └── ClientTest.php ├── TerrainGen └── FlatlandGeneratorTest.php └── World ├── ChunkTest.php └── WorldTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | 3 | vendor/ 4 | .idea/ 5 | logs/ 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | 6 | before_script: 7 | - composer install 8 | 9 | script: phpunit --bootstrap vendor/autoload.php tests/ 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2015 Andrew Vy 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, IN NO EVENT SHALL THE 20 | THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://github.com/andrewvy/HHVMCraft/raw/master/docs/hhvmcraft.png) 2 | 3 | Works on PHP 7.0.0 RC6 and HHVM 3.10.1! 4 | 5 | [![Build Status](https://travis-ci.org/andrewvy/HHVMCraft.svg?branch=master)](https://travis-ci.org/andrewvy/HHVMCraft) 6 | 7 | A Minecraft Beta 1.7.3 Server implemented in PHP, based off of 8 | [Truecraft's implementation](https://github.com/SirCmpwn/TrueCraft). Powered by [ReactPHP](http://reactphp.org/) (with some parts patched). 9 | 10 | ![demo](https://github.com/andrewvy/HHVMCraft/raw/master/docs/demo-2.png) 11 | 12 | The goal of this project is not to be a fully-functional server, 13 | but rather a proof-of-concept for PHP and HHVM. 14 | 15 | ### Installation? 16 | 17 | You need composer! 18 | 19 | `composer install` 20 | 21 | and 22 | 23 | `php server.php` or `hhvm server.php`! 24 | 25 | Make sure you're using the b1.7.3 client! Check the `Allow use of Old Beta Minecraft Versions`. 26 | 27 | ![](https://cloud.githubusercontent.com/assets/2051361/11055769/2b601e68-872f-11e5-81f3-da8c1a9e83ff.png) 28 | 29 | ### Why? 30 | 31 | This is a little coding exercise/project, it's not intended to be a serious 32 | application. :) 33 | 34 | As you can tell, this is not the most performant implementation. It's being 35 | developed in a way that's fast to code, and readable. 36 | 37 | I am also not a PHP programmer, or an advanced-level programmer, nor do I know much about socket programming! ;) 38 | 39 | So if you have any comments or if you have any crazy fun/interesting ideas for me to consider, please feel free to create an Github issue! 40 | 41 | ## Current Progress 42 | 43 | View [Things to make functional demo](https://github.com/andrewvy/HHVMCraft/issues/1)! 44 | 45 | ## Libraries used 46 | 47 | [Evenement](https://github.com/igorw/evenement) - Event Dispatching Library 48 | [ReactPHP](https://github.com/reactphp/react) - Low-level async event-driven library for PHP. 49 | [PHPUnit](https://phpunit.de) - PHP Testing Framework 50 | [Monolog](https://github.com/Seldaek/monolog) - Logging Framework 51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hhvmcraft", 3 | "description": "A Minecraft Beta 1.7.3 Server Implementation", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Andrew Vy", 8 | "email": "andrew@andrewvy.com" 9 | } 10 | ], 11 | "require": { 12 | "react/react": "0.4.*", 13 | "monolog/monolog": "~1.13" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "4.6.*" 17 | }, 18 | "autoload": { 19 | "psr-4": { 20 | "HHVMCraft\\": "src/HHVMCraft/" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "hash": "2394e67a060a443193302de696ce8d91", 8 | "content-hash": "96e776ec4d26d2e8180045553347cd83", 9 | "packages": [ 10 | { 11 | "name": "evenement/evenement", 12 | "version": "v2.0.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/igorw/evenement.git", 16 | "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/igorw/evenement/zipball/f6e843799fd4f4184d54d8fc7b5b3551c9fa803e", 21 | "reference": "f6e843799fd4f4184d54d8fc7b5b3551c9fa803e", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": ">=5.4.0" 26 | }, 27 | "type": "library", 28 | "extra": { 29 | "branch-alias": { 30 | "dev-master": "2.0-dev" 31 | } 32 | }, 33 | "autoload": { 34 | "psr-0": { 35 | "Evenement": "src" 36 | } 37 | }, 38 | "notification-url": "https://packagist.org/downloads/", 39 | "license": [ 40 | "MIT" 41 | ], 42 | "authors": [ 43 | { 44 | "name": "Igor Wiedler", 45 | "email": "igor@wiedler.ch", 46 | "homepage": "http://wiedler.ch/igor/" 47 | } 48 | ], 49 | "description": "Événement is a very simple event dispatching library for PHP", 50 | "keywords": [ 51 | "event-dispatcher", 52 | "event-emitter" 53 | ], 54 | "time": "2012-11-02 14:49:47" 55 | }, 56 | { 57 | "name": "guzzle/parser", 58 | "version": "v3.9.2", 59 | "target-dir": "Guzzle/Parser", 60 | "source": { 61 | "type": "git", 62 | "url": "https://github.com/Guzzle3/parser.git", 63 | "reference": "6874d171318a8e93eb6d224cf85e4678490b625c" 64 | }, 65 | "dist": { 66 | "type": "zip", 67 | "url": "https://api.github.com/repos/Guzzle3/parser/zipball/6874d171318a8e93eb6d224cf85e4678490b625c", 68 | "reference": "6874d171318a8e93eb6d224cf85e4678490b625c", 69 | "shasum": "" 70 | }, 71 | "require": { 72 | "php": ">=5.3.2" 73 | }, 74 | "type": "library", 75 | "extra": { 76 | "branch-alias": { 77 | "dev-master": "3.7-dev" 78 | } 79 | }, 80 | "autoload": { 81 | "psr-0": { 82 | "Guzzle\\Parser": "" 83 | } 84 | }, 85 | "notification-url": "https://packagist.org/downloads/", 86 | "license": [ 87 | "MIT" 88 | ], 89 | "description": "Interchangeable parsers used by Guzzle", 90 | "homepage": "http://guzzlephp.org/", 91 | "keywords": [ 92 | "URI Template", 93 | "cookie", 94 | "http", 95 | "message", 96 | "url" 97 | ], 98 | "abandoned": "guzzle/guzzle", 99 | "time": "2014-02-05 18:29:46" 100 | }, 101 | { 102 | "name": "monolog/monolog", 103 | "version": "1.17.2", 104 | "source": { 105 | "type": "git", 106 | "url": "https://github.com/Seldaek/monolog.git", 107 | "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" 108 | }, 109 | "dist": { 110 | "type": "zip", 111 | "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", 112 | "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", 113 | "shasum": "" 114 | }, 115 | "require": { 116 | "php": ">=5.3.0", 117 | "psr/log": "~1.0" 118 | }, 119 | "provide": { 120 | "psr/log-implementation": "1.0.0" 121 | }, 122 | "require-dev": { 123 | "aws/aws-sdk-php": "^2.4.9", 124 | "doctrine/couchdb": "~1.0@dev", 125 | "graylog2/gelf-php": "~1.0", 126 | "jakub-onderka/php-parallel-lint": "0.9", 127 | "php-console/php-console": "^3.1.3", 128 | "phpunit/phpunit": "~4.5", 129 | "phpunit/phpunit-mock-objects": "2.3.0", 130 | "raven/raven": "^0.13", 131 | "ruflin/elastica": ">=0.90 <3.0", 132 | "swiftmailer/swiftmailer": "~5.3", 133 | "videlalvaro/php-amqplib": "~2.4" 134 | }, 135 | "suggest": { 136 | "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", 137 | "doctrine/couchdb": "Allow sending log messages to a CouchDB server", 138 | "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", 139 | "ext-mongo": "Allow sending log messages to a MongoDB server", 140 | "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", 141 | "php-console/php-console": "Allow sending log messages to Google Chrome", 142 | "raven/raven": "Allow sending log messages to a Sentry server", 143 | "rollbar/rollbar": "Allow sending log messages to Rollbar", 144 | "ruflin/elastica": "Allow sending log messages to an Elastic Search server", 145 | "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" 146 | }, 147 | "type": "library", 148 | "extra": { 149 | "branch-alias": { 150 | "dev-master": "1.16.x-dev" 151 | } 152 | }, 153 | "autoload": { 154 | "psr-4": { 155 | "Monolog\\": "src/Monolog" 156 | } 157 | }, 158 | "notification-url": "https://packagist.org/downloads/", 159 | "license": [ 160 | "MIT" 161 | ], 162 | "authors": [ 163 | { 164 | "name": "Jordi Boggiano", 165 | "email": "j.boggiano@seld.be", 166 | "homepage": "http://seld.be" 167 | } 168 | ], 169 | "description": "Sends your logs to files, sockets, inboxes, databases and various web services", 170 | "homepage": "http://github.com/Seldaek/monolog", 171 | "keywords": [ 172 | "log", 173 | "logging", 174 | "psr-3" 175 | ], 176 | "time": "2015-10-14 12:51:02" 177 | }, 178 | { 179 | "name": "psr/log", 180 | "version": "1.0.0", 181 | "source": { 182 | "type": "git", 183 | "url": "https://github.com/php-fig/log.git", 184 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" 185 | }, 186 | "dist": { 187 | "type": "zip", 188 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", 189 | "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", 190 | "shasum": "" 191 | }, 192 | "type": "library", 193 | "autoload": { 194 | "psr-0": { 195 | "Psr\\Log\\": "" 196 | } 197 | }, 198 | "notification-url": "https://packagist.org/downloads/", 199 | "license": [ 200 | "MIT" 201 | ], 202 | "authors": [ 203 | { 204 | "name": "PHP-FIG", 205 | "homepage": "http://www.php-fig.org/" 206 | } 207 | ], 208 | "description": "Common interface for logging libraries", 209 | "keywords": [ 210 | "log", 211 | "psr", 212 | "psr-3" 213 | ], 214 | "time": "2012-12-21 11:40:51" 215 | }, 216 | { 217 | "name": "react/promise", 218 | "version": "v2.2.1", 219 | "source": { 220 | "type": "git", 221 | "url": "https://github.com/reactphp/promise.git", 222 | "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627" 223 | }, 224 | "dist": { 225 | "type": "zip", 226 | "url": "https://api.github.com/repos/reactphp/promise/zipball/3b6fca09c7d56321057fa8867c8dbe1abf648627", 227 | "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627", 228 | "shasum": "" 229 | }, 230 | "require": { 231 | "php": ">=5.4.0" 232 | }, 233 | "type": "library", 234 | "extra": { 235 | "branch-alias": { 236 | "dev-master": "2.0-dev" 237 | } 238 | }, 239 | "autoload": { 240 | "psr-4": { 241 | "React\\Promise\\": "src/" 242 | }, 243 | "files": [ 244 | "src/functions_include.php" 245 | ] 246 | }, 247 | "notification-url": "https://packagist.org/downloads/", 248 | "license": [ 249 | "MIT" 250 | ], 251 | "authors": [ 252 | { 253 | "name": "Jan Sorgalla", 254 | "email": "jsorgalla@gmail.com" 255 | } 256 | ], 257 | "description": "A lightweight implementation of CommonJS Promises/A for PHP", 258 | "time": "2015-07-03 13:48:55" 259 | }, 260 | { 261 | "name": "react/react", 262 | "version": "v0.4.1", 263 | "source": { 264 | "type": "git", 265 | "url": "https://github.com/reactphp/react.git", 266 | "reference": "17044db0ab6a9c145fb1ac40822c5c17d045dc36" 267 | }, 268 | "dist": { 269 | "type": "zip", 270 | "url": "https://api.github.com/repos/reactphp/react/zipball/17044db0ab6a9c145fb1ac40822c5c17d045dc36", 271 | "reference": "17044db0ab6a9c145fb1ac40822c5c17d045dc36", 272 | "shasum": "" 273 | }, 274 | "require": { 275 | "evenement/evenement": "~2.0", 276 | "guzzle/parser": "~3.0", 277 | "php": ">=5.4.0", 278 | "react/promise": "~2.0" 279 | }, 280 | "replace": { 281 | "react/cache": "self.version", 282 | "react/dns": "self.version", 283 | "react/event-loop": "self.version", 284 | "react/http": "self.version", 285 | "react/http-client": "self.version", 286 | "react/socket": "self.version", 287 | "react/socket-client": "self.version", 288 | "react/stream": "self.version" 289 | }, 290 | "require-dev": { 291 | "phpunit/phpunit": "3.7.*" 292 | }, 293 | "suggest": { 294 | "ext-event": "Allows for use of a more performant event-loop implementation.", 295 | "ext-libev": "Allows for use of a more performant event-loop implementation.", 296 | "ext-libevent": "Allows for use of a more performant event-loop implementation." 297 | }, 298 | "type": "library", 299 | "extra": { 300 | "branch-alias": { 301 | "dev-master": "0.5-dev" 302 | } 303 | }, 304 | "autoload": { 305 | "psr-4": { 306 | "React\\": "src" 307 | } 308 | }, 309 | "notification-url": "https://packagist.org/downloads/", 310 | "license": [ 311 | "MIT" 312 | ], 313 | "description": "Nuclear Reactor written in PHP.", 314 | "keywords": [ 315 | "event-loop", 316 | "reactor" 317 | ], 318 | "time": "2014-04-13 17:03:14" 319 | } 320 | ], 321 | "packages-dev": [ 322 | { 323 | "name": "doctrine/instantiator", 324 | "version": "1.0.5", 325 | "source": { 326 | "type": "git", 327 | "url": "https://github.com/doctrine/instantiator.git", 328 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" 329 | }, 330 | "dist": { 331 | "type": "zip", 332 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", 333 | "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", 334 | "shasum": "" 335 | }, 336 | "require": { 337 | "php": ">=5.3,<8.0-DEV" 338 | }, 339 | "require-dev": { 340 | "athletic/athletic": "~0.1.8", 341 | "ext-pdo": "*", 342 | "ext-phar": "*", 343 | "phpunit/phpunit": "~4.0", 344 | "squizlabs/php_codesniffer": "~2.0" 345 | }, 346 | "type": "library", 347 | "extra": { 348 | "branch-alias": { 349 | "dev-master": "1.0.x-dev" 350 | } 351 | }, 352 | "autoload": { 353 | "psr-4": { 354 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" 355 | } 356 | }, 357 | "notification-url": "https://packagist.org/downloads/", 358 | "license": [ 359 | "MIT" 360 | ], 361 | "authors": [ 362 | { 363 | "name": "Marco Pivetta", 364 | "email": "ocramius@gmail.com", 365 | "homepage": "http://ocramius.github.com/" 366 | } 367 | ], 368 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", 369 | "homepage": "https://github.com/doctrine/instantiator", 370 | "keywords": [ 371 | "constructor", 372 | "instantiate" 373 | ], 374 | "time": "2015-06-14 21:17:01" 375 | }, 376 | { 377 | "name": "phpdocumentor/reflection-docblock", 378 | "version": "2.0.4", 379 | "source": { 380 | "type": "git", 381 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", 382 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" 383 | }, 384 | "dist": { 385 | "type": "zip", 386 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", 387 | "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", 388 | "shasum": "" 389 | }, 390 | "require": { 391 | "php": ">=5.3.3" 392 | }, 393 | "require-dev": { 394 | "phpunit/phpunit": "~4.0" 395 | }, 396 | "suggest": { 397 | "dflydev/markdown": "~1.0", 398 | "erusev/parsedown": "~1.0" 399 | }, 400 | "type": "library", 401 | "extra": { 402 | "branch-alias": { 403 | "dev-master": "2.0.x-dev" 404 | } 405 | }, 406 | "autoload": { 407 | "psr-0": { 408 | "phpDocumentor": [ 409 | "src/" 410 | ] 411 | } 412 | }, 413 | "notification-url": "https://packagist.org/downloads/", 414 | "license": [ 415 | "MIT" 416 | ], 417 | "authors": [ 418 | { 419 | "name": "Mike van Riel", 420 | "email": "mike.vanriel@naenius.com" 421 | } 422 | ], 423 | "time": "2015-02-03 12:10:50" 424 | }, 425 | { 426 | "name": "phpspec/prophecy", 427 | "version": "v1.5.0", 428 | "source": { 429 | "type": "git", 430 | "url": "https://github.com/phpspec/prophecy.git", 431 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7" 432 | }, 433 | "dist": { 434 | "type": "zip", 435 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4745ded9307786b730d7a60df5cb5a6c43cf95f7", 436 | "reference": "4745ded9307786b730d7a60df5cb5a6c43cf95f7", 437 | "shasum": "" 438 | }, 439 | "require": { 440 | "doctrine/instantiator": "^1.0.2", 441 | "phpdocumentor/reflection-docblock": "~2.0", 442 | "sebastian/comparator": "~1.1" 443 | }, 444 | "require-dev": { 445 | "phpspec/phpspec": "~2.0" 446 | }, 447 | "type": "library", 448 | "extra": { 449 | "branch-alias": { 450 | "dev-master": "1.4.x-dev" 451 | } 452 | }, 453 | "autoload": { 454 | "psr-0": { 455 | "Prophecy\\": "src/" 456 | } 457 | }, 458 | "notification-url": "https://packagist.org/downloads/", 459 | "license": [ 460 | "MIT" 461 | ], 462 | "authors": [ 463 | { 464 | "name": "Konstantin Kudryashov", 465 | "email": "ever.zet@gmail.com", 466 | "homepage": "http://everzet.com" 467 | }, 468 | { 469 | "name": "Marcello Duarte", 470 | "email": "marcello.duarte@gmail.com" 471 | } 472 | ], 473 | "description": "Highly opinionated mocking framework for PHP 5.3+", 474 | "homepage": "https://github.com/phpspec/prophecy", 475 | "keywords": [ 476 | "Double", 477 | "Dummy", 478 | "fake", 479 | "mock", 480 | "spy", 481 | "stub" 482 | ], 483 | "time": "2015-08-13 10:07:40" 484 | }, 485 | { 486 | "name": "phpunit/php-code-coverage", 487 | "version": "2.2.4", 488 | "source": { 489 | "type": "git", 490 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git", 491 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979" 492 | }, 493 | "dist": { 494 | "type": "zip", 495 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979", 496 | "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979", 497 | "shasum": "" 498 | }, 499 | "require": { 500 | "php": ">=5.3.3", 501 | "phpunit/php-file-iterator": "~1.3", 502 | "phpunit/php-text-template": "~1.2", 503 | "phpunit/php-token-stream": "~1.3", 504 | "sebastian/environment": "^1.3.2", 505 | "sebastian/version": "~1.0" 506 | }, 507 | "require-dev": { 508 | "ext-xdebug": ">=2.1.4", 509 | "phpunit/phpunit": "~4" 510 | }, 511 | "suggest": { 512 | "ext-dom": "*", 513 | "ext-xdebug": ">=2.2.1", 514 | "ext-xmlwriter": "*" 515 | }, 516 | "type": "library", 517 | "extra": { 518 | "branch-alias": { 519 | "dev-master": "2.2.x-dev" 520 | } 521 | }, 522 | "autoload": { 523 | "classmap": [ 524 | "src/" 525 | ] 526 | }, 527 | "notification-url": "https://packagist.org/downloads/", 528 | "license": [ 529 | "BSD-3-Clause" 530 | ], 531 | "authors": [ 532 | { 533 | "name": "Sebastian Bergmann", 534 | "email": "sb@sebastian-bergmann.de", 535 | "role": "lead" 536 | } 537 | ], 538 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", 539 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage", 540 | "keywords": [ 541 | "coverage", 542 | "testing", 543 | "xunit" 544 | ], 545 | "time": "2015-10-06 15:47:00" 546 | }, 547 | { 548 | "name": "phpunit/php-file-iterator", 549 | "version": "1.4.1", 550 | "source": { 551 | "type": "git", 552 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git", 553 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" 554 | }, 555 | "dist": { 556 | "type": "zip", 557 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 558 | "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", 559 | "shasum": "" 560 | }, 561 | "require": { 562 | "php": ">=5.3.3" 563 | }, 564 | "type": "library", 565 | "extra": { 566 | "branch-alias": { 567 | "dev-master": "1.4.x-dev" 568 | } 569 | }, 570 | "autoload": { 571 | "classmap": [ 572 | "src/" 573 | ] 574 | }, 575 | "notification-url": "https://packagist.org/downloads/", 576 | "license": [ 577 | "BSD-3-Clause" 578 | ], 579 | "authors": [ 580 | { 581 | "name": "Sebastian Bergmann", 582 | "email": "sb@sebastian-bergmann.de", 583 | "role": "lead" 584 | } 585 | ], 586 | "description": "FilterIterator implementation that filters files based on a list of suffixes.", 587 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", 588 | "keywords": [ 589 | "filesystem", 590 | "iterator" 591 | ], 592 | "time": "2015-06-21 13:08:43" 593 | }, 594 | { 595 | "name": "phpunit/php-text-template", 596 | "version": "1.2.1", 597 | "source": { 598 | "type": "git", 599 | "url": "https://github.com/sebastianbergmann/php-text-template.git", 600 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" 601 | }, 602 | "dist": { 603 | "type": "zip", 604 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 605 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", 606 | "shasum": "" 607 | }, 608 | "require": { 609 | "php": ">=5.3.3" 610 | }, 611 | "type": "library", 612 | "autoload": { 613 | "classmap": [ 614 | "src/" 615 | ] 616 | }, 617 | "notification-url": "https://packagist.org/downloads/", 618 | "license": [ 619 | "BSD-3-Clause" 620 | ], 621 | "authors": [ 622 | { 623 | "name": "Sebastian Bergmann", 624 | "email": "sebastian@phpunit.de", 625 | "role": "lead" 626 | } 627 | ], 628 | "description": "Simple template engine.", 629 | "homepage": "https://github.com/sebastianbergmann/php-text-template/", 630 | "keywords": [ 631 | "template" 632 | ], 633 | "time": "2015-06-21 13:50:34" 634 | }, 635 | { 636 | "name": "phpunit/php-timer", 637 | "version": "1.0.7", 638 | "source": { 639 | "type": "git", 640 | "url": "https://github.com/sebastianbergmann/php-timer.git", 641 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" 642 | }, 643 | "dist": { 644 | "type": "zip", 645 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 646 | "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", 647 | "shasum": "" 648 | }, 649 | "require": { 650 | "php": ">=5.3.3" 651 | }, 652 | "type": "library", 653 | "autoload": { 654 | "classmap": [ 655 | "src/" 656 | ] 657 | }, 658 | "notification-url": "https://packagist.org/downloads/", 659 | "license": [ 660 | "BSD-3-Clause" 661 | ], 662 | "authors": [ 663 | { 664 | "name": "Sebastian Bergmann", 665 | "email": "sb@sebastian-bergmann.de", 666 | "role": "lead" 667 | } 668 | ], 669 | "description": "Utility class for timing", 670 | "homepage": "https://github.com/sebastianbergmann/php-timer/", 671 | "keywords": [ 672 | "timer" 673 | ], 674 | "time": "2015-06-21 08:01:12" 675 | }, 676 | { 677 | "name": "phpunit/php-token-stream", 678 | "version": "1.4.8", 679 | "source": { 680 | "type": "git", 681 | "url": "https://github.com/sebastianbergmann/php-token-stream.git", 682 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da" 683 | }, 684 | "dist": { 685 | "type": "zip", 686 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 687 | "reference": "3144ae21711fb6cac0b1ab4cbe63b75ce3d4e8da", 688 | "shasum": "" 689 | }, 690 | "require": { 691 | "ext-tokenizer": "*", 692 | "php": ">=5.3.3" 693 | }, 694 | "require-dev": { 695 | "phpunit/phpunit": "~4.2" 696 | }, 697 | "type": "library", 698 | "extra": { 699 | "branch-alias": { 700 | "dev-master": "1.4-dev" 701 | } 702 | }, 703 | "autoload": { 704 | "classmap": [ 705 | "src/" 706 | ] 707 | }, 708 | "notification-url": "https://packagist.org/downloads/", 709 | "license": [ 710 | "BSD-3-Clause" 711 | ], 712 | "authors": [ 713 | { 714 | "name": "Sebastian Bergmann", 715 | "email": "sebastian@phpunit.de" 716 | } 717 | ], 718 | "description": "Wrapper around PHP's tokenizer extension.", 719 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/", 720 | "keywords": [ 721 | "tokenizer" 722 | ], 723 | "time": "2015-09-15 10:49:45" 724 | }, 725 | { 726 | "name": "phpunit/phpunit", 727 | "version": "4.6.10", 728 | "source": { 729 | "type": "git", 730 | "url": "https://github.com/sebastianbergmann/phpunit.git", 731 | "reference": "7b5fe98b28302a8b25693b2298bca74463336975" 732 | }, 733 | "dist": { 734 | "type": "zip", 735 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/7b5fe98b28302a8b25693b2298bca74463336975", 736 | "reference": "7b5fe98b28302a8b25693b2298bca74463336975", 737 | "shasum": "" 738 | }, 739 | "require": { 740 | "ext-dom": "*", 741 | "ext-json": "*", 742 | "ext-pcre": "*", 743 | "ext-reflection": "*", 744 | "ext-spl": "*", 745 | "php": ">=5.3.3", 746 | "phpspec/prophecy": "~1.3,>=1.3.1", 747 | "phpunit/php-code-coverage": "~2.0,>=2.0.11", 748 | "phpunit/php-file-iterator": "~1.4", 749 | "phpunit/php-text-template": "~1.2", 750 | "phpunit/php-timer": "~1.0", 751 | "phpunit/phpunit-mock-objects": "~2.3", 752 | "sebastian/comparator": "~1.1", 753 | "sebastian/diff": "~1.2", 754 | "sebastian/environment": "~1.2", 755 | "sebastian/exporter": "~1.2", 756 | "sebastian/global-state": "~1.0", 757 | "sebastian/version": "~1.0", 758 | "symfony/yaml": "~2.1|~3.0" 759 | }, 760 | "suggest": { 761 | "phpunit/php-invoker": "~1.1" 762 | }, 763 | "bin": [ 764 | "phpunit" 765 | ], 766 | "type": "library", 767 | "extra": { 768 | "branch-alias": { 769 | "dev-master": "4.6.x-dev" 770 | } 771 | }, 772 | "autoload": { 773 | "classmap": [ 774 | "src/" 775 | ] 776 | }, 777 | "notification-url": "https://packagist.org/downloads/", 778 | "license": [ 779 | "BSD-3-Clause" 780 | ], 781 | "authors": [ 782 | { 783 | "name": "Sebastian Bergmann", 784 | "email": "sebastian@phpunit.de", 785 | "role": "lead" 786 | } 787 | ], 788 | "description": "The PHP Unit Testing framework.", 789 | "homepage": "https://phpunit.de/", 790 | "keywords": [ 791 | "phpunit", 792 | "testing", 793 | "xunit" 794 | ], 795 | "time": "2015-06-03 05:03:30" 796 | }, 797 | { 798 | "name": "phpunit/phpunit-mock-objects", 799 | "version": "2.3.8", 800 | "source": { 801 | "type": "git", 802 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", 803 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983" 804 | }, 805 | "dist": { 806 | "type": "zip", 807 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983", 808 | "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983", 809 | "shasum": "" 810 | }, 811 | "require": { 812 | "doctrine/instantiator": "^1.0.2", 813 | "php": ">=5.3.3", 814 | "phpunit/php-text-template": "~1.2", 815 | "sebastian/exporter": "~1.2" 816 | }, 817 | "require-dev": { 818 | "phpunit/phpunit": "~4.4" 819 | }, 820 | "suggest": { 821 | "ext-soap": "*" 822 | }, 823 | "type": "library", 824 | "extra": { 825 | "branch-alias": { 826 | "dev-master": "2.3.x-dev" 827 | } 828 | }, 829 | "autoload": { 830 | "classmap": [ 831 | "src/" 832 | ] 833 | }, 834 | "notification-url": "https://packagist.org/downloads/", 835 | "license": [ 836 | "BSD-3-Clause" 837 | ], 838 | "authors": [ 839 | { 840 | "name": "Sebastian Bergmann", 841 | "email": "sb@sebastian-bergmann.de", 842 | "role": "lead" 843 | } 844 | ], 845 | "description": "Mock Object library for PHPUnit", 846 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", 847 | "keywords": [ 848 | "mock", 849 | "xunit" 850 | ], 851 | "time": "2015-10-02 06:51:40" 852 | }, 853 | { 854 | "name": "sebastian/comparator", 855 | "version": "1.2.0", 856 | "source": { 857 | "type": "git", 858 | "url": "https://github.com/sebastianbergmann/comparator.git", 859 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22" 860 | }, 861 | "dist": { 862 | "type": "zip", 863 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/937efb279bd37a375bcadf584dec0726f84dbf22", 864 | "reference": "937efb279bd37a375bcadf584dec0726f84dbf22", 865 | "shasum": "" 866 | }, 867 | "require": { 868 | "php": ">=5.3.3", 869 | "sebastian/diff": "~1.2", 870 | "sebastian/exporter": "~1.2" 871 | }, 872 | "require-dev": { 873 | "phpunit/phpunit": "~4.4" 874 | }, 875 | "type": "library", 876 | "extra": { 877 | "branch-alias": { 878 | "dev-master": "1.2.x-dev" 879 | } 880 | }, 881 | "autoload": { 882 | "classmap": [ 883 | "src/" 884 | ] 885 | }, 886 | "notification-url": "https://packagist.org/downloads/", 887 | "license": [ 888 | "BSD-3-Clause" 889 | ], 890 | "authors": [ 891 | { 892 | "name": "Jeff Welch", 893 | "email": "whatthejeff@gmail.com" 894 | }, 895 | { 896 | "name": "Volker Dusch", 897 | "email": "github@wallbash.com" 898 | }, 899 | { 900 | "name": "Bernhard Schussek", 901 | "email": "bschussek@2bepublished.at" 902 | }, 903 | { 904 | "name": "Sebastian Bergmann", 905 | "email": "sebastian@phpunit.de" 906 | } 907 | ], 908 | "description": "Provides the functionality to compare PHP values for equality", 909 | "homepage": "http://www.github.com/sebastianbergmann/comparator", 910 | "keywords": [ 911 | "comparator", 912 | "compare", 913 | "equality" 914 | ], 915 | "time": "2015-07-26 15:48:44" 916 | }, 917 | { 918 | "name": "sebastian/diff", 919 | "version": "1.4.1", 920 | "source": { 921 | "type": "git", 922 | "url": "https://github.com/sebastianbergmann/diff.git", 923 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" 924 | }, 925 | "dist": { 926 | "type": "zip", 927 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", 928 | "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", 929 | "shasum": "" 930 | }, 931 | "require": { 932 | "php": ">=5.3.3" 933 | }, 934 | "require-dev": { 935 | "phpunit/phpunit": "~4.8" 936 | }, 937 | "type": "library", 938 | "extra": { 939 | "branch-alias": { 940 | "dev-master": "1.4-dev" 941 | } 942 | }, 943 | "autoload": { 944 | "classmap": [ 945 | "src/" 946 | ] 947 | }, 948 | "notification-url": "https://packagist.org/downloads/", 949 | "license": [ 950 | "BSD-3-Clause" 951 | ], 952 | "authors": [ 953 | { 954 | "name": "Kore Nordmann", 955 | "email": "mail@kore-nordmann.de" 956 | }, 957 | { 958 | "name": "Sebastian Bergmann", 959 | "email": "sebastian@phpunit.de" 960 | } 961 | ], 962 | "description": "Diff implementation", 963 | "homepage": "https://github.com/sebastianbergmann/diff", 964 | "keywords": [ 965 | "diff" 966 | ], 967 | "time": "2015-12-08 07:14:41" 968 | }, 969 | { 970 | "name": "sebastian/environment", 971 | "version": "1.3.3", 972 | "source": { 973 | "type": "git", 974 | "url": "https://github.com/sebastianbergmann/environment.git", 975 | "reference": "6e7133793a8e5a5714a551a8324337374be209df" 976 | }, 977 | "dist": { 978 | "type": "zip", 979 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", 980 | "reference": "6e7133793a8e5a5714a551a8324337374be209df", 981 | "shasum": "" 982 | }, 983 | "require": { 984 | "php": ">=5.3.3" 985 | }, 986 | "require-dev": { 987 | "phpunit/phpunit": "~4.4" 988 | }, 989 | "type": "library", 990 | "extra": { 991 | "branch-alias": { 992 | "dev-master": "1.3.x-dev" 993 | } 994 | }, 995 | "autoload": { 996 | "classmap": [ 997 | "src/" 998 | ] 999 | }, 1000 | "notification-url": "https://packagist.org/downloads/", 1001 | "license": [ 1002 | "BSD-3-Clause" 1003 | ], 1004 | "authors": [ 1005 | { 1006 | "name": "Sebastian Bergmann", 1007 | "email": "sebastian@phpunit.de" 1008 | } 1009 | ], 1010 | "description": "Provides functionality to handle HHVM/PHP environments", 1011 | "homepage": "http://www.github.com/sebastianbergmann/environment", 1012 | "keywords": [ 1013 | "Xdebug", 1014 | "environment", 1015 | "hhvm" 1016 | ], 1017 | "time": "2015-12-02 08:37:27" 1018 | }, 1019 | { 1020 | "name": "sebastian/exporter", 1021 | "version": "1.2.1", 1022 | "source": { 1023 | "type": "git", 1024 | "url": "https://github.com/sebastianbergmann/exporter.git", 1025 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e" 1026 | }, 1027 | "dist": { 1028 | "type": "zip", 1029 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", 1030 | "reference": "7ae5513327cb536431847bcc0c10edba2701064e", 1031 | "shasum": "" 1032 | }, 1033 | "require": { 1034 | "php": ">=5.3.3", 1035 | "sebastian/recursion-context": "~1.0" 1036 | }, 1037 | "require-dev": { 1038 | "phpunit/phpunit": "~4.4" 1039 | }, 1040 | "type": "library", 1041 | "extra": { 1042 | "branch-alias": { 1043 | "dev-master": "1.2.x-dev" 1044 | } 1045 | }, 1046 | "autoload": { 1047 | "classmap": [ 1048 | "src/" 1049 | ] 1050 | }, 1051 | "notification-url": "https://packagist.org/downloads/", 1052 | "license": [ 1053 | "BSD-3-Clause" 1054 | ], 1055 | "authors": [ 1056 | { 1057 | "name": "Jeff Welch", 1058 | "email": "whatthejeff@gmail.com" 1059 | }, 1060 | { 1061 | "name": "Volker Dusch", 1062 | "email": "github@wallbash.com" 1063 | }, 1064 | { 1065 | "name": "Bernhard Schussek", 1066 | "email": "bschussek@2bepublished.at" 1067 | }, 1068 | { 1069 | "name": "Sebastian Bergmann", 1070 | "email": "sebastian@phpunit.de" 1071 | }, 1072 | { 1073 | "name": "Adam Harvey", 1074 | "email": "aharvey@php.net" 1075 | } 1076 | ], 1077 | "description": "Provides the functionality to export PHP variables for visualization", 1078 | "homepage": "http://www.github.com/sebastianbergmann/exporter", 1079 | "keywords": [ 1080 | "export", 1081 | "exporter" 1082 | ], 1083 | "time": "2015-06-21 07:55:53" 1084 | }, 1085 | { 1086 | "name": "sebastian/global-state", 1087 | "version": "1.1.1", 1088 | "source": { 1089 | "type": "git", 1090 | "url": "https://github.com/sebastianbergmann/global-state.git", 1091 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" 1092 | }, 1093 | "dist": { 1094 | "type": "zip", 1095 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", 1096 | "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", 1097 | "shasum": "" 1098 | }, 1099 | "require": { 1100 | "php": ">=5.3.3" 1101 | }, 1102 | "require-dev": { 1103 | "phpunit/phpunit": "~4.2" 1104 | }, 1105 | "suggest": { 1106 | "ext-uopz": "*" 1107 | }, 1108 | "type": "library", 1109 | "extra": { 1110 | "branch-alias": { 1111 | "dev-master": "1.0-dev" 1112 | } 1113 | }, 1114 | "autoload": { 1115 | "classmap": [ 1116 | "src/" 1117 | ] 1118 | }, 1119 | "notification-url": "https://packagist.org/downloads/", 1120 | "license": [ 1121 | "BSD-3-Clause" 1122 | ], 1123 | "authors": [ 1124 | { 1125 | "name": "Sebastian Bergmann", 1126 | "email": "sebastian@phpunit.de" 1127 | } 1128 | ], 1129 | "description": "Snapshotting of global state", 1130 | "homepage": "http://www.github.com/sebastianbergmann/global-state", 1131 | "keywords": [ 1132 | "global state" 1133 | ], 1134 | "time": "2015-10-12 03:26:01" 1135 | }, 1136 | { 1137 | "name": "sebastian/recursion-context", 1138 | "version": "1.0.2", 1139 | "source": { 1140 | "type": "git", 1141 | "url": "https://github.com/sebastianbergmann/recursion-context.git", 1142 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791" 1143 | }, 1144 | "dist": { 1145 | "type": "zip", 1146 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", 1147 | "reference": "913401df809e99e4f47b27cdd781f4a258d58791", 1148 | "shasum": "" 1149 | }, 1150 | "require": { 1151 | "php": ">=5.3.3" 1152 | }, 1153 | "require-dev": { 1154 | "phpunit/phpunit": "~4.4" 1155 | }, 1156 | "type": "library", 1157 | "extra": { 1158 | "branch-alias": { 1159 | "dev-master": "1.0.x-dev" 1160 | } 1161 | }, 1162 | "autoload": { 1163 | "classmap": [ 1164 | "src/" 1165 | ] 1166 | }, 1167 | "notification-url": "https://packagist.org/downloads/", 1168 | "license": [ 1169 | "BSD-3-Clause" 1170 | ], 1171 | "authors": [ 1172 | { 1173 | "name": "Jeff Welch", 1174 | "email": "whatthejeff@gmail.com" 1175 | }, 1176 | { 1177 | "name": "Sebastian Bergmann", 1178 | "email": "sebastian@phpunit.de" 1179 | }, 1180 | { 1181 | "name": "Adam Harvey", 1182 | "email": "aharvey@php.net" 1183 | } 1184 | ], 1185 | "description": "Provides functionality to recursively process PHP variables", 1186 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context", 1187 | "time": "2015-11-11 19:50:13" 1188 | }, 1189 | { 1190 | "name": "sebastian/version", 1191 | "version": "1.0.6", 1192 | "source": { 1193 | "type": "git", 1194 | "url": "https://github.com/sebastianbergmann/version.git", 1195 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6" 1196 | }, 1197 | "dist": { 1198 | "type": "zip", 1199 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1200 | "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6", 1201 | "shasum": "" 1202 | }, 1203 | "type": "library", 1204 | "autoload": { 1205 | "classmap": [ 1206 | "src/" 1207 | ] 1208 | }, 1209 | "notification-url": "https://packagist.org/downloads/", 1210 | "license": [ 1211 | "BSD-3-Clause" 1212 | ], 1213 | "authors": [ 1214 | { 1215 | "name": "Sebastian Bergmann", 1216 | "email": "sebastian@phpunit.de", 1217 | "role": "lead" 1218 | } 1219 | ], 1220 | "description": "Library that helps with managing the version number of Git-hosted PHP projects", 1221 | "homepage": "https://github.com/sebastianbergmann/version", 1222 | "time": "2015-06-21 13:59:46" 1223 | }, 1224 | { 1225 | "name": "symfony/yaml", 1226 | "version": "v3.0.1", 1227 | "source": { 1228 | "type": "git", 1229 | "url": "https://github.com/symfony/yaml.git", 1230 | "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691" 1231 | }, 1232 | "dist": { 1233 | "type": "zip", 1234 | "url": "https://api.github.com/repos/symfony/yaml/zipball/3df409958a646dad2bc5046c3fb671ee24a1a691", 1235 | "reference": "3df409958a646dad2bc5046c3fb671ee24a1a691", 1236 | "shasum": "" 1237 | }, 1238 | "require": { 1239 | "php": ">=5.5.9" 1240 | }, 1241 | "type": "library", 1242 | "extra": { 1243 | "branch-alias": { 1244 | "dev-master": "3.0-dev" 1245 | } 1246 | }, 1247 | "autoload": { 1248 | "psr-4": { 1249 | "Symfony\\Component\\Yaml\\": "" 1250 | }, 1251 | "exclude-from-classmap": [ 1252 | "/Tests/" 1253 | ] 1254 | }, 1255 | "notification-url": "https://packagist.org/downloads/", 1256 | "license": [ 1257 | "MIT" 1258 | ], 1259 | "authors": [ 1260 | { 1261 | "name": "Fabien Potencier", 1262 | "email": "fabien@symfony.com" 1263 | }, 1264 | { 1265 | "name": "Symfony Community", 1266 | "homepage": "https://symfony.com/contributors" 1267 | } 1268 | ], 1269 | "description": "Symfony Yaml Component", 1270 | "homepage": "https://symfony.com", 1271 | "time": "2015-12-26 13:39:53" 1272 | } 1273 | ], 1274 | "aliases": [], 1275 | "minimum-stability": "stable", 1276 | "stability-flags": [], 1277 | "prefer-stable": false, 1278 | "prefer-lowest": false, 1279 | "platform": [], 1280 | "platform-dev": [] 1281 | } 1282 | -------------------------------------------------------------------------------- /docs/architecture.md: -------------------------------------------------------------------------------- 1 | # Project Setup 2 | 3 | `HHVMCraft.API` - Contains all the structs/objects 4 | `HHVMCraft.Core` - Contains core processing classes 5 | 6 | # HHVMCraft Server Architecture 7 | 8 | Main Server Thread: 9 | 10 | BOTTOM LAYER ---- ReactPHP EventLoop 11 | LAYER 1 ---- ReactPHP Socket Server 12 | LAYER 2 ---- MultiplayerServer 13 | 14 | CURRENT: 15 | 16 | MultiplayerServer handles both networking + game logic 17 | on the same eventloop. (Will be split later with pthreads) 18 | 19 | MultiplayerServer handles: 20 | - Incoming Connections 21 | - Sends pending Clientbound Packets 22 | - Runs game loop 23 | 24 | CLIENT CONNECTION PROTOCOL: 25 | 26 | Handshake packet with client username --> 27 | 28 | <-- Responds with handshake packet specifying authentication method. 29 | 30 | LoginRequest packet specifying protocol version and auth details --> 31 | 32 | <-- LoginResponse packet with details about the current world 33 | 34 | PLAYER ENTITY: 35 | 36 | <-- SpawnPosition packet with the client entity spawn position 37 | <-- SetPlayerPosition packet with the client entity current position 38 | 39 | WORLD TIME: 40 | 41 | <-- TimeUpdate packet with the world's current in-game time 42 | 43 | UPDATE CHUNKS: 44 | 45 | <-- ChunkPreamble packet with details of the incoming chunk data 46 | <-- ChunkData packet with actual chunk data 47 | 48 | Multithreaded scenarios: 49 | 50 | - Logging 51 | - Client Packet Receiver (Enqueues packets to be processed onto the main thread) 52 | - Client Packet Transmit (Dequeues packets to be emitted to the client) 53 | - World Chunk IO (Loads chunks from disk/memory) 54 | - Physics Engine (maybe?) 55 | 56 | CLIENT PACKET HANDLER 57 | --------------------- 58 | 59 | Each client has their own packet processing thread which handles 60 | incoming packets before enqueuing it onto the main process thread. 61 | 62 | CLIENT 1: CLIENT 2: CLIENT 3: 63 | --------------------------------------------------------- 64 | 65 | PACKET X PACKET Y PACKET Z 66 | . . . 67 | . . . 68 | . . . 69 | . . . 70 | DONE . DONE 71 | PACKET A . 72 | . . 73 | . . 74 | DONE DONE 75 | 76 | PACKET X + Z finished at the same time BUT Client 1 obtained the mutex first, and then Client 3 obtained it second. 77 | PACKET Y + A finished at the same time BUT Client 1 obtained the mutex first, and then Client 2 obtained it second. 78 | 79 | Server Packet Processing Queue (threadsafe LIFO stack): 80 | -------------------------------------- 81 | 82 | 1) PACKET X - Client 1 83 | 2) PACKET Z - Client 2 84 | 3) PACKET A - Client 1 85 | 4) PACKET Y - Client 3 86 | 87 | ENTITIY MANAGER 88 | ----------------- 89 | 90 | EM holds the UUID -> entity relationship 91 | Clients holds the array of UUIDs that the client "knows" about. 92 | 93 | 94 | When an entity updates the position, we need to check if the entity has moved out of range of any clients. 95 | Calculate range by checking the 3D coordinates of the Entity 96 | If an entity is out of range: 97 | Remove it from the array of known entities from that client 98 | And if the entity is a PlayerEntity, also remove the client from the moved entity's client 99 | -------------------------------------------------------------------------------- /docs/demo-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewvy/HHVMCraft/a035b0c238d756114baafcb5163a507e06082748/docs/demo-2.png -------------------------------------------------------------------------------- /docs/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewvy/HHVMCraft/a035b0c238d756114baafcb5163a507e06082748/docs/demo.png -------------------------------------------------------------------------------- /docs/hhvmcraft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewvy/HHVMCraft/a035b0c238d756114baafcb5163a507e06082748/docs/hhvmcraft.png -------------------------------------------------------------------------------- /docs/hhvmcraft_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewvy/HHVMCraft/a035b0c238d756114baafcb5163a507e06082748/docs/hhvmcraft_logo.png -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 7 | * // */ 8 | 9 | date_default_timezone_set('UTC'); 10 | 11 | require "vendor/autoload.php"; 12 | 13 | use HHVMCraft\Core\Networking\MultiplayerServer; 14 | error_reporting(E_ALL); 15 | 16 | $addr = "127.0.0.1"; 17 | 18 | $server = new MultiplayerServer($addr); 19 | $server->start(25565); 20 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/BlockProvider.php: -------------------------------------------------------------------------------- 1 | generateDropEntity($BlockDescriptor, $World, $Client->server); 22 | $World->setBlockId($BlockDescriptor->Coordinates, 0x00); 23 | } 24 | 25 | public function generateDropEntity($BlockDescriptor, $World, $Server) { 26 | $EntityManager = $Server->EntityManager; 27 | 28 | // For each item drop, do this 29 | // ItemEntity = new ItemEntity($Coordinates3D, $Item); 30 | // EntityManager->SpawnEntity(ItemEntity); 31 | } 32 | 33 | public function onItemUsed($Coordinates3D, $ItemStack, $BlockFace, $World) { 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/BlockRepository.php: -------------------------------------------------------------------------------- 1 | registerBlockProviders(); 10 | } 11 | 12 | public function registerBlockProviders() { 13 | // $this->registerBlockProvider(new GrassBlockProvider()); 14 | } 15 | 16 | public function getBlockProvider($id) { 17 | return $this->BlockProviders[$id]; 18 | } 19 | 20 | public function registerBlockProvider($provider) { 21 | $this->BlockProviders[$provider->id] = $provider; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/Coordinates2D.php: -------------------------------------------------------------------------------- 1 | x = $x; 17 | $this->z = $z; 18 | } 19 | 20 | public static function minimum($Coordinates2D1, $Coordinates2D2) { 21 | return new self( 22 | min($Coordinates2D1->x, $Coordinates2D2->x), 23 | min($Coordinates2D1->z, $Coordinates2D2->z) 24 | ); 25 | } 26 | 27 | public static function maximum($Coordinates2D1, $Coordinates2D2) { 28 | return new self( 29 | max($Coordinates2D1->x, $Coordinates2D2->x), 30 | max($Coordinates2D1->z, $Coordinates2D2->z) 31 | ); 32 | } 33 | 34 | public static function Zero() { 35 | return new self(0, 0); 36 | } 37 | 38 | public static function Forward() { 39 | return new self(0, 1); 40 | } 41 | 42 | public static function Backward() { 43 | return new self(0, -1); 44 | } 45 | 46 | public static function Left() { 47 | return new self(-1, 0); 48 | } 49 | 50 | public static function Right() { 51 | return new self(1, 0); 52 | } 53 | 54 | public function toString() { 55 | return sprintf('<%d, %d>', $this->x, $this->z); 56 | } 57 | 58 | public function distance() { 59 | return $this->distanceTo($this->Zero); 60 | } 61 | 62 | public function distanceTo($Coordinates2D) { 63 | return sqrt(pow($Coordinates2D->x - $this->x, 2) + pow($Coordinates2D->z - $this->z, 2)); 64 | } 65 | 66 | public function equals($Coordinates2D) { 67 | if ($this->x == $Coordinates2D->x && $this->z == $Coordinates2D->z) { 68 | return true; 69 | } else { 70 | return false; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/Coordinates3D.php: -------------------------------------------------------------------------------- 1 | x = $x; 31 | $this->y = $y; 32 | $this->z = $z; 33 | } 34 | 35 | public static function minimum($Coordinates3D1, $Coordinates3D2) { 36 | return new self( 37 | min($Coordinates3D1->x, $Coordinates3D2->x), 38 | min($Coordinates3D1->y, $Coordinates3D2->y), 39 | min($Coordinates3D1->z, $Coordinates3D2->z) 40 | ); 41 | } 42 | 43 | public static function maximum($Coordinates3D1, $Coordinates3D2) { 44 | return new self( 45 | max($Coordinates3D1->x, $Coordinates3D2->x), 46 | max($Coordinates3D1->y, $Coordinates3D2->y), 47 | max($Coordinates3D1->z, $Coordinates3D2->z) 48 | ); 49 | } 50 | 51 | public static function convert2Dto3D($Coordinates2D) { 52 | return new self($Coordinates2D->x, 0, $Coordinates2D->z); 53 | } 54 | 55 | public static function Vec3to3D($Vect3) { 56 | return new self($Vect3->x, $Vect3->y, $Vect3->z); 57 | } 58 | 59 | public static function Sizeto3D($Size) { 60 | return new self($Size->Width, $Size->Height, $Size->Depth); 61 | } 62 | 63 | public static function One() { 64 | return new self(1, 1, 1); 65 | } 66 | 67 | public static function Up() { 68 | return new self(0, 1, 0); 69 | } 70 | 71 | public static function Down() { 72 | return new self(0, -1, 0); 73 | } 74 | 75 | public static function Left() { 76 | return new self(-1, 0, 0); 77 | } 78 | 79 | public static function Right() { 80 | return new self(1, 0, 0); 81 | } 82 | 83 | public static function Backwards() { 84 | return new self(0, 0, -1); 85 | } 86 | 87 | public static function Forwards() { 88 | return new self(0, 0, 1); 89 | } 90 | 91 | public static function East() { 92 | return new self(1, 0, 0); 93 | } 94 | 95 | public static function West() { 96 | return new self(-1, 0, 0); 97 | } 98 | 99 | public static function North() { 100 | return new self(0, 0, -1); 101 | } 102 | 103 | public static function South() { 104 | return new self(0, 0, 1); 105 | } 106 | 107 | public static function OneX() { 108 | return new self(1, 0, 0); 109 | } 110 | 111 | public static function OneY() { 112 | return new self(0, 1, 0); 113 | } 114 | 115 | public static function OneZ() { 116 | return new self(0, 0, 1); 117 | } 118 | 119 | public function toString() { 120 | return sprintf('<%d, %d, %d>', $this->x, $this->y, $this->z); 121 | } 122 | 123 | public function clamp($val) { 124 | if (abs($this->x) > $val) { 125 | $this->x = $val * ($this->x ? -1 : 1); 126 | } 127 | if (abs($this->y) > $val) { 128 | $this->y = $val * ($this->y ? -1 : 1); 129 | } 130 | if (abs($this->x) > $val) { 131 | $this->z = $val * ($this->z ? -1 : 1); 132 | } 133 | } 134 | 135 | public function distance() { 136 | return $this->distanceTo($this->Zero()); 137 | } 138 | 139 | public function distanceTo($Coordinates3D) { 140 | return sqrt(pow($Coordinates3D->x - $this->x, 2) + pow($Coordinates3D->y - $this->y, 2) + pow($Coordinates3D->z - $this->z, 2)); 141 | } 142 | 143 | public static function Zero() { 144 | return new self(0, 0, 0); 145 | } 146 | 147 | public function equals($Coordinates3D) { 148 | if ($this->x == $Coordinates3D->x && $this->y == $Coordinates3D->y && $this->z == $Coordinates3D->z) { 149 | return true; 150 | } else { 151 | return false; 152 | } 153 | } 154 | 155 | public function equalsCoordinates($x, $y, $z) { 156 | if ($this->x == $x && $this->y == $y && $this->z == $z) { 157 | return true; 158 | } else { 159 | return false; 160 | } 161 | } 162 | 163 | public static function rounded($Coordinates3D) { 164 | return new self((int) $Coordinates3D->x, (int) $Coordinates3D->y, (int) $Coordinates3D->z); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/CraftingRepository.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | namespace HHVMCraft\API; 11 | 12 | class ItemStack { 13 | public $id; 14 | public $icount; 15 | public $metadata; 16 | public $nbt; 17 | 18 | public function __construct($id, $icount = 1, $metadata = 0, $nbt = null) { 19 | if ($icount == 0) { 20 | $this->id = -1; 21 | $this->metadata = 0; 22 | $this->nbt = null; 23 | } 24 | 25 | $this->id = $id; 26 | $this->icount = $icount; 27 | $this->metadata = $metadata; 28 | $this->nbt = $nbt; 29 | } 30 | 31 | public function fromStream($StreamWrapper) { 32 | $slot = self::emptyStack(); 33 | $slot->id = hexdec(bin2hex($StreamWrapper->readInt16())); 34 | 35 | if ($slot->isEmpty()) { 36 | return $slot; 37 | } 38 | 39 | $slot->icount = hexdec(bin2hex($StreamWrapper->readInt8())); 40 | $slot->metadata = hexdec(bin2hex($StreamWrapper->readInt16())); 41 | $l = hexdec(bin2hex($StreamWrapper->readInt16())); 42 | $buf = $StreamWrapper->readInt8Array($l); 43 | 44 | return $slot; 45 | } 46 | 47 | static function emptyStack() { 48 | return new self(-1); 49 | } 50 | 51 | public function isEmpty() { 52 | return ($this->id == -1); 53 | } 54 | 55 | public function toString() { 56 | return sprintf('', $this->id, $this->icount); 57 | } 58 | 59 | public function toStream($StreamWrapper) { 60 | // Handle NBT compressed data stream -> Int8 array 61 | 62 | $str = $StreamWrapper->writeInt16($this->id); 63 | if ($this->isEmpty()) { 64 | return $str; 65 | } 66 | 67 | $str = $str . 68 | $StreamWrapper->writeInt8($this->icount) . 69 | $StreamWrapper->writeInt16($this->metadata); 70 | 71 | if ($this->nbt == null) { 72 | $str = $str . $StreamWrapper->writeInt16(-1); 73 | 74 | return $str; 75 | } 76 | } 77 | 78 | public function duplicate() { 79 | return new self($this->id, $this->icount, $this->metadata, $this->nbt); 80 | } 81 | 82 | public function canMerge($ItemStack) { 83 | if ($this->isEmpty() && $ItemStack->isEmpty()) { 84 | return true; 85 | } else if ($this->id == $ItemStack->id && $this->metadata == $ItemStack->metadata && $this->nbt == $ItemStack->nbt) { 86 | return true; 87 | } else { 88 | return false; 89 | } 90 | } 91 | 92 | public function equals($ItemStack) { 93 | if ($this->id == $ItemStack->id && $this->icount == $ItemStack->icount && $this->metadata == $ItemStack->metadata && $this->nbt == $ItemStack->nbt) { 94 | return true; 95 | } else { 96 | return false; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/HHVMCraft/API/Vector3.php: -------------------------------------------------------------------------------- 1 | x = $x; 12 | $this->y = $y; 13 | $this->z = $z; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Entities/Entity.php: -------------------------------------------------------------------------------- 1 | uuid = uniqid("entity"); 25 | $this->spawnTime = new \DateTime(); 26 | $this->Velocity = new Vec3(0,0,0); 27 | $this->Position = new Coordinates3D(0,0,0); 28 | $this->OldPosition = new Coordinates3D(0,0,0); 29 | 30 | // This is the EventManager's event loop. 31 | // Used for propagating entity property updates to other clients. 32 | $this->Event = $Event; 33 | } 34 | 35 | public function setPosition($x=0, $y=0, $z=0) { 36 | $this->Position->x = $x; 37 | $this->Position->y = $y; 38 | $this->Position->z = $z; 39 | 40 | $this->propertyChanged("Position"); 41 | } 42 | 43 | public function setVelocity($x=0, $y=0, $z=0) { 44 | $this->Velocity->x = $x; 45 | $this->Velocity->y = $y; 46 | $this->Velocity->z = $z; 47 | 48 | $this->propertyChanged("Velocity"); 49 | } 50 | 51 | public function setYaw($Yaw) { 52 | $this->Yaw = $Yaw; 53 | 54 | $this->propertyChanged("Yaw"); 55 | } 56 | 57 | public function setPitch($Pitch) { 58 | $this->Pitch = $Pitch; 59 | 60 | $this ->propertyChanged("Pitch"); 61 | } 62 | 63 | public function setEntityFlags($value) { 64 | $this->EntityFlags = $value; 65 | 66 | $this->propertyChanged("Metadata"); 67 | } 68 | 69 | public function metadata() { 70 | // TODO (vy): Metadata Dictionary??? 71 | } 72 | 73 | public function update($EntityManager) { 74 | if ($this->Position->y < -50) { 75 | $EntityManager->despawnEntity($this); 76 | } 77 | } 78 | 79 | public function propertyChanged($propertyName) { 80 | if ($this->enablePropertyChange == true) { 81 | $this->Event->emit("PropertyChanged", array($this, $propertyName) ); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Entities/EntityManager.php: -------------------------------------------------------------------------------- 1 | Server = $Server; 25 | $this->PhysicsEngine = new PhysicsEngine($World, $Server->BlockRepository); 26 | $this->Event = new EventEmitter(); 27 | 28 | $this->Event->on("PropertyChanged", function ($sender, $propertyName) { 29 | $this->handlePropertyChanged($sender, $propertyName); 30 | }); 31 | } 32 | 33 | public function handlePropertyChanged($entity, $propertyName) { 34 | if (get_class($entity) == "PlayerEntity") { 35 | $this->handlePlayerPropertyChanged($propertyName, $entity); 36 | } 37 | 38 | switch ($propertyName) { 39 | case "Position": 40 | case "Yaw": 41 | case "Pitch": 42 | $this->propagateEntityPositionUpdates($entity); 43 | break; 44 | case "Metadata": 45 | $this->propagateEntityMetadataUpdates($entity); 46 | break; 47 | } 48 | } 49 | 50 | public function handlePlayerPropertyChanged($propertyName, $PlayerEntity) { 51 | switch ($propertyName) { 52 | case "Position": 53 | if ($playerEntity->Position->x >> 4 != $playerEntity->OldPosition->x >> 4 || 54 | $playerEntitiy->Position->z >> 4 != $playerEntity->OldPosition->z >> 4) { 55 | $this->Server->loop->nextTick($client->updateChunks); 56 | $this->updateClientEntities($entity->client); 57 | } 58 | break; 59 | } 60 | } 61 | 62 | public function updateClientEntities($client) { 63 | $entity = $client->Entity; 64 | 65 | // Remove entities from the client that have moved out of range of the client. 66 | for ($i = 0; $i < count($client->KnownEntities); $i++) { 67 | $knownEntity = $client->knownEntities[$i]; 68 | 69 | if ($knownEntity->Position->distanceTo($entity->Position) > $client->chunkRadius * Chunk::Depth) { 70 | $client->enqueuePacket(new DestroyEntityPacket($knownEntity->entityId)); 71 | unset($client->knownEntities[$i]); 72 | 73 | if (get_class($knownEntity) == "PlayerEntity") { 74 | $c = $knownEntity->Client; 75 | 76 | if (in_array($entity, $c->knownEntities)) { 77 | unset($c->knownEntities[$entity]); 78 | array_values($c->knownEntities); 79 | 80 | $c->enqueuePacket(new DestroyEntityPacket($entity->entityId)); 81 | } 82 | } 83 | } 84 | } 85 | 86 | // Reindex client entities array after we have removed some elements. 87 | array_values($client->knownEntities); 88 | 89 | // Now get entities that the client should know about 90 | $entitiesToSpawn = $this->getEntitiesInRange($entity, $client->chunkRadius); 91 | foreach ($entitiesToSpawn as $e) { 92 | if ($e != $entity && !in_array($e, $client->knownEntities)) { 93 | $this->sendEntityToClient($client, $e); 94 | 95 | // If it's a player, make sure that client knows about this entity. 96 | if (get_class($e == "PlayerEntity")) { 97 | $c = $e->Client; 98 | 99 | if (!in_array($entity, $c->knownEntities)) { 100 | $this->sendEntityToClient($c, $entity); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | public function sendEntityToClient($client, $entity) { 108 | array_push($client->knownEntities, $entity->uuid); 109 | 110 | $client->enqueuePacket($entity->spawnPacket); 111 | 112 | if (get_class($entity) == "PhysicsEntity") { 113 | $client->enqueuePacket(new EntityVelocityPacket( 114 | $entity->entityId, 115 | ($entity->Velocity->x * 320), 116 | ($entity->Velocity->z * 320))); 117 | } 118 | } 119 | 120 | public function propagateEntityPositionUpdates($sender) { 121 | for ($i = 0; $i < count($this->Server->Clients); $i++) { 122 | $client = $this->Server->Clients[$i]; 123 | 124 | if ($client->PlayerEntity == $sender) { 125 | continue; 126 | } 127 | 128 | if (in_array($sender, $client->knownEntities)) { 129 | $client->enqueuePacket(new EntityTeleportPacket( 130 | $sender->entityId, 131 | $sender->Position->x, 132 | $sender->Position->y, 133 | $sender->Position->z, 134 | ((($sender->Yaw % 360) / 360) * 256), 135 | ((($sender->Pitch % 360) / 360) * 256))); 136 | } 137 | } 138 | } 139 | 140 | public function propagateEntityMetadataUpdates($sender) { 141 | if ($sender->sendMetaDataToClients == false) { 142 | return; 143 | } 144 | 145 | for ($i = 0; $i < count($this->Server->Clients); $i++) { 146 | $client = $this->Server->Clients[$i]; 147 | 148 | if ($client->PlayerEntity == $sender) { 149 | continue; 150 | } 151 | 152 | if (in_array($sender, $client->knownEntities)) { 153 | $client->enqueuePacket(new EntityMetadataPacket($entity->entityId, $entity->metadata())); 154 | } 155 | } 156 | } 157 | 158 | public function despawnEntity($Entity) { 159 | array_push($this->pendingDespawns, $Entity); 160 | $Entity->despawned = true; 161 | } 162 | 163 | public function update() { 164 | // $this->PhysicsEngine->update(); 165 | 166 | foreach ($this->entities as $e) { 167 | if ($e->Despawned == false) { 168 | $e->update($this); 169 | } 170 | } 171 | 172 | $this->flushDespawns(); 173 | } 174 | 175 | public function flushDespawns() { 176 | while (count($this->pendingDespawns) != 0) { 177 | $entity = array_shift($this->pendingDespawns); 178 | 179 | if (get_class($entity) == "PhysicsEntity") { 180 | $this->PhysicsEngine->removeEntity($entity); 181 | } 182 | 183 | for ($i = 0; $i < count($this->Server->Clients); $i++) { 184 | $client = $this->Server->Clients[$i]; 185 | 186 | if (in_array($entity, $client->knownEntities) && $client->Disconnected == false) { 187 | $client->enqueuePacket(new DestroyEntityPacket($entity->entityId)); 188 | 189 | unset($client->knownEntities[$entity]); 190 | array_values($client->knownEntities); 191 | 192 | } 193 | } 194 | 195 | unset($this->entities[$entity]); 196 | } 197 | } 198 | 199 | public function addPlayerEntity($client) { 200 | $PlayerEntity = new PlayerEntity($client, $this->Event); 201 | array_push($this->entities, $PlayerEntity); 202 | return $PlayerEntity; 203 | } 204 | 205 | public function checkForBlockingEntities($Coordinates3D) { 206 | $roundedCoordinates = Coordinates3D::rounded($Coordinates3D); 207 | $result = false; 208 | 209 | 210 | // TODO(vy): Ensure we check for the entity height for the 3D bounding box. 211 | 212 | foreach ($this->entities as $entity) { 213 | $entityCoordinates = Coordinates3D::rounded($entity->Position); 214 | 215 | if ($roundedCoordinates->equalsCoordinates($entityCoordinates->x, $entityCoordinates->y, $entityCoordinates->z) || 216 | $roundedCoordinates->equalsCoordinates($entityCoordinates->x, $entityCoordinates->y + 1, $entityCoordinates->z)) { 217 | $result = true; 218 | break; 219 | } 220 | } 221 | 222 | return $result; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Entities/LivingEntity.php: -------------------------------------------------------------------------------- 1 | air = $value; 17 | 18 | $this->propertyChanged("Air"); 19 | } 20 | 21 | public function setHealth($value) { 22 | $this->health = $value; 23 | 24 | $this->propertyChanged("Health"); 25 | } 26 | 27 | public function setHeadYaw($value) { 28 | $this->headYaw = $value; 29 | 30 | $this->propertyChanged("HeadYaw"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Entities/PlayerEntity.php: -------------------------------------------------------------------------------- 1 | username = $Client->username; 20 | } 21 | 22 | public function spawnPacket() { 23 | return new SpawnPlayerPacket($this->entityId, 24 | $this->Position->x, 25 | $this->Position->y, 26 | $this->Position->z, 27 | ((($this->Yaw % 360) / 360) * 256), 28 | ((($this->Pitch % 360) / 360) * 256), 29 | 0); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Helpers/Hex.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | namespace HHVMCraft\Core\Helpers; 10 | 11 | class Hex { 12 | public static function dump($data, $newline = "\n") { 13 | static $from = ''; 14 | static $to = ''; 15 | 16 | static $width = 16; # number of bytes per line 17 | 18 | static $pad = '.'; # padding for non-visible characters 19 | 20 | if ($from === '') { 21 | for ($i = 0; $i <= 0xFF; $i++) { 22 | $from .= chr($i); 23 | $to .= ($i >= 0x20 && $i <= 0x7E) ? chr($i) : $pad; 24 | } 25 | } 26 | 27 | $hex = str_split(bin2hex($data), $width * 2); 28 | $chars = str_split(strtr($data, $from, $to), $width); 29 | 30 | $offset = 0; 31 | foreach ($hex as $i => $line) { 32 | echo sprintf('%6X', $offset) . ' : ' . implode(' ', str_split($line, 2)) . ' [' . $chars[$i] . ']' . $newline; 33 | $offset += $width; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Helpers/Logger.php: -------------------------------------------------------------------------------- 1 | > "; 10 | const ERROR_PREFIX = "[ERROR]"; 11 | const WARNING_PREFIX = "[WARNING]"; 12 | const LOG_PREFIX = "[LOG]"; 13 | 14 | public $options; 15 | public $PacketLog; 16 | 17 | public function __construct() { 18 | if (!file_exists("logs/")) { 19 | mkdir("logs/"); 20 | } 21 | 22 | $this->PacketLog = new MLogger('PacketLogger'); 23 | $this->PacketLog->pushHandler(new StreamHandler('logs/packet_log'), MLogger::INFO); 24 | $this->ServerLog = new MLogger('ServerLogger'); 25 | # $this->OutLog = new MLogger('OutLogger'); 26 | # $this->OutLog->pushHandler(new StreamHandler('logs/out_log'), MLogger::INFO); 27 | } 28 | 29 | public function throwLog($msg) { 30 | $response = $this::PREFIX . $msg . PHP_EOL; 31 | $this->ServerLog->addInfo($response); 32 | # $this->OutLog->addInfo($response); 33 | } 34 | 35 | public function throwWarning($msg) { 36 | $response = $this::PREFIX . $msg . PHP_EOL; 37 | $this->ServerLog->addWarning($response); 38 | } 39 | 40 | public function throwError($msg) { 41 | $response = $this::PREFIX . $msg . PHP_EOL; 42 | $this->ServerLog->addError($response); 43 | } 44 | 45 | public function logPacket($packet) { 46 | $this->PacketLog->addInfo($packet); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Client.php: -------------------------------------------------------------------------------- 1 | uuid = uniqid("client"); 38 | $this->connection = $connection; 39 | $this->streamWrapper = new StreamWrapper($connection); 40 | $this->Server = $server; 41 | $this->World = $server->World; 42 | $this->Inventory = new InventoryWindow($server->CraftingRepository); 43 | $this->setItem(0x01, 0x40, 0x00, 3, 0); 44 | $this->setupPacketListener(); 45 | $this->pktCount = 0; 46 | } 47 | 48 | public function setupPacketListener() { 49 | $this->connection->on('data', function ($data) { 50 | $this->pktCount++; 51 | $this->streamWrapper->data($data); 52 | 53 | while ($this->pktCount > 0) { 54 | $this->Server->handlePacket($this); 55 | $this->pktCount--; 56 | } 57 | }); 58 | } 59 | 60 | public function updateChunks() { 61 | for ($i = 0; $i < 2; $i++) { 62 | for ($j = 0; $j < 2; $j++) { 63 | $Coordinates2D = new Coordinates2D($i, $j); 64 | 65 | $chunk = $this->World->generateChunk($Coordinates2D); 66 | $preamble = new ChunkPreamblePacket($Coordinates2D->x, $Coordinates2D->z); 67 | $data = $this->createChunkPacket($chunk); 68 | $this->enqueuePacket($preamble); 69 | $this->enqueuePacket($data); 70 | } 71 | } 72 | } 73 | 74 | public function createChunkPacket($chunk) { 75 | $x = $chunk->x; 76 | $z = $chunk->z; 77 | 78 | $blockdata = $chunk->deserialize(); 79 | $compress = gzcompress($blockdata); 80 | 81 | return new ChunkDataPacket( 82 | $x, 83 | 0, 84 | $z, 85 | $chunk::Width, 86 | $chunk::Height, 87 | $chunk::Depth, 88 | $compress); 89 | } 90 | 91 | public function enqueuePacket($packet) { 92 | $this->Server->writePacket($packet, $this); 93 | } 94 | 95 | public function loadChunk($Coordinates2D) { 96 | $chunk = $this->World->generateChunk($Coordinates2D); 97 | $this->enqueuePacket(new ChunkPreamblePacket($chunk->x, $chunk->z)); 98 | $this->enqueuePacket($this->createChunkPacket($chunk)); 99 | 100 | $serialized = $chunk->x . ":" . $chunk->z; 101 | 102 | $this->loadedChunks[$serialized] = true; 103 | } 104 | 105 | public function unloadChunk($Coordinates2D) { 106 | $this->enqueuePacket(new ChunkPreamablePacket($Coordinates2D->x, $Coordiantes2D->z, false)); 107 | $serialized = $chunk->x . ":" . $chunk->z; 108 | unset($this->loadedChunks[$serialized]); 109 | $this->loadedChunks = array_values($array); 110 | } 111 | 112 | public function disconnect() { 113 | $this->streamWrapper->close(); 114 | $this->loadedChunks = []; 115 | $this->connection->handleClose(); 116 | } 117 | 118 | public function disconnectWithReason($reason) { 119 | $this->loadedChunks = []; 120 | $this->enqueuePacket(new DisconnectPacket($reason)); 121 | $this->connection->handleClose(); 122 | } 123 | 124 | public function sendMessage($message="") { 125 | $this->enqueuePacket(new ChatMessagePacket( 126 | $message 127 | )); 128 | } 129 | 130 | public function setItem($id=0x00, $amount=0x40, $metadata=0x00, $window=3, $slot=0) { 131 | $this->Inventory->WindowAreas[$window]->Items[$slot] = new ItemStack($id, $amount, $metadata); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Connection.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | namespace React\Socket; 12 | 13 | use React\Stream\Stream; 14 | 15 | class Connection extends Stream implements ConnectionInterface { 16 | public function handleData($stream) { 17 | $data = stream_socket_recvfrom($stream, $this->bufferSize); 18 | if ('' !== $data && false !== $data) { 19 | $this->emit('data', array($data, $this)); 20 | } 21 | 22 | if ('' === $data || false === $data || !is_resource($stream) || feof($stream)) { 23 | $this->end(); 24 | } 25 | } 26 | 27 | public function handleClose() { 28 | if (is_resource($this->stream)) { 29 | // http://chat.stackoverflow.com/transcript/message/7727858#7727858 30 | stream_socket_shutdown($this->stream, STREAM_SHUT_RDWR); 31 | stream_set_blocking($this->stream, false); 32 | fclose($this->stream); 33 | } 34 | } 35 | 36 | public function getRemoteAddress() { 37 | return $this->parseAddress(stream_socket_get_name($this->stream, true)); 38 | } 39 | 40 | private function parseAddress($address) { 41 | return trim(substr($address, 0, strrpos($address, ':')), '[]'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Handlers/ChatHandler.php: -------------------------------------------------------------------------------- 1 | message[0] == "/") { 13 | self::handleCommand($Packet->message, $Client, $Server); 14 | } else { 15 | $message = "<" . $Client->username . "> " . $Packet->message; 16 | $Server->sendMessage($message); 17 | } 18 | } 19 | 20 | public static function handleCommand($message="", $Client, $Server) { 21 | $args = explode(" ", $message); 22 | $args_count = count($args); 23 | 24 | switch ($args[0]) { 25 | case "/buffer": 26 | $count = "Buffer is: ".count($Client->streamWrapper->streamBuffer); 27 | $Client->sendMessage($count); 28 | break; 29 | case "/ping": 30 | $Client->sendMessage("Pong!"); 31 | break; 32 | case "/kill": 33 | $Client->enqueuePacket(new UpdateHealthPacket()); 34 | break; 35 | case "/sethealth": 36 | if (!is_numeric($args[1])) { 37 | return $Client->sendMessage("Number needed!"); 38 | } 39 | 40 | $Client->enqueuePacket(new UpdateHealthPacket($args[1])); 41 | break; 42 | case "/getpos": 43 | $x = $Client->PlayerEntity->Position->x; 44 | $y = $Client->PlayerEntity->Position->y; 45 | $z = $Client->PlayerEntity->Position->z; 46 | $coords = new Coordinates3D($x, $y, $z); 47 | 48 | $Client->sendMessage($coords->toString()); 49 | break; 50 | case "/give": 51 | // give a stack of the given item id to the client 52 | // /give 5 <- gives 64 of item_id: 5 53 | // /give 5 32 <- gives 32 of item_id: 5 54 | 55 | if (!is_numeric($args[1])) { 56 | return $Client->sendMessage("/give [id] [count]. Numerical Item ID needed!"); 57 | } 58 | 59 | if ($args_count == 3 && !is_numeric($args[2])) { 60 | return $Client->sendMessage("/give [id] [count]. Numerical Item count needed!"); 61 | } else if ($args_count == 3) { 62 | $item_count = (int) $args[2]; 63 | } else { 64 | $item_count = 0x40; 65 | } 66 | 67 | $item_id = (int) $args[1]; 68 | 69 | if ($item_id > 0x00) { 70 | $Client->setItem($item_id, $item_count); 71 | $Client->enqueuePacket(new WindowItemsPacket(0, $Client->Inventory->getSlots())); 72 | return $Client->sendMessage("Successfully gave a stack of " . $args[1]); 73 | } else { 74 | return $Client->sendMessage("Item ID given was not a valid item id."); 75 | } 76 | 77 | break; 78 | case "/heart": 79 | return $Client->sendMessage("<3"); 80 | break; 81 | case "/rename": 82 | if ($args_count != 2) { return; } 83 | 84 | $Server->sendMessage($Client->username . " has changed their name to: " . $args[1]); 85 | 86 | $Client->username = $args[1]; 87 | $Client->PlayerEntity->username = $args[1]; 88 | break; 89 | default: 90 | $Client->sendMessage("Command not recognized!"); 91 | } 92 | } 93 | 94 | # TODO (vy): Port commands into their own functions here 95 | } 96 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Handlers/DataHandler.php: -------------------------------------------------------------------------------- 1 | Logger->throwLog("Client has disconnected for reason: " . $Packet->reason); 15 | 16 | $Server->handleDisconnect($Client); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Handlers/LoginHandler.php: -------------------------------------------------------------------------------- 1 | username = $packet->username; 18 | 19 | // Sends the string "-" to indicate that no account authentication should take place. 20 | $client->enqueuePacket(new HandshakeResponsePacket("-")); 21 | } 22 | 23 | public static function HandleLoginRequest($packet, $client, $server) { 24 | // Make sure the client has the right protocol version before allowing them to connect. 25 | if ($packet->protocolVersion == 14) { 26 | 27 | // Respond with details about the world. 28 | $client->enqueuePacket(new LoginResponsePacket(0, 0, 0)); 29 | 30 | // Add player entity to entitymanager, subscribe client to entities. 31 | $client->PlayerEntity = $server->EntityManager->addPlayerEntity($client); 32 | 33 | // Handle client inventory.. (WindowItemPacket) 34 | $client->enqueuePacket(new WindowItemsPacket(0, $client->Inventory->getSlots())); 35 | 36 | // Set the player entity position to the world's spawnpoint 37 | $client->PlayerEntity->Position = $client->World->ChunkProvider->spawnpoint; 38 | 39 | // Send packaet that sets the player's spawnpoint to that location. 40 | $client->enqueuePacket(new SpawnPositionPacket( 41 | $client->PlayerEntity->Position->x, 42 | $client->PlayerEntity->Position->y, 43 | $client->PlayerEntity->Position->z) 44 | ); 45 | 46 | // send packet that actually sets the player's current position to that position. 47 | $client->enqueuePacket(new SetPlayerPositionPacket( 48 | $client->PlayerEntity->Position->x + 5, 49 | $client->PlayerEntity->Position->y + PlayerEntity::Height - 5, 50 | $client->PlayerEntity->Position->y, 51 | $client->PlayerEntity->Position->z+15, 52 | 0, 53 | 0, 54 | 0) 55 | ); 56 | 57 | // Send the world time to the client. 58 | $client->enqueuePacket(new TimeUpdatePacket( 59 | $server->World->getTime()) 60 | ); 61 | 62 | // Begin sending chunk data. 63 | $client->updateChunks(); 64 | 65 | $server->Logger->throwLog("Added new client!"); 66 | $server->sendMessage($client->username . " has joined the server!"); 67 | 68 | } else { 69 | // The client's version is not the same as this server implementation. 70 | // So, we should disconnect that client with a 'Wrong Version' message. 71 | $server->Logger->throwWarning("Wrong client version!"); 72 | $server->handleDisconnect($client, true, "Wrong client version!"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Handlers/PlayerHandler.php: -------------------------------------------------------------------------------- 1 | PlayerEntity->Position->x = $packet->x; 19 | $client->PlayerEntity->Position->y = $packet->y; 20 | $client->PlayerEntity->Position->z = $packet->z; 21 | } 22 | 23 | public static function HandleLook($Packet, $Client, $Server) { 24 | $Client->PlayerEntity->Position->pitch = $Packet->pitch; 25 | $Client->PlayerEntity->Position->yaw = $Packet->yaw; 26 | } 27 | 28 | public static function HandlePositionAndLook($Packet, $Client, $Server) { 29 | $Client->PlayerEntity->Position->x = $Packet->x; 30 | $Client->PlayerEntity->Position->y = $Packet->y; 31 | $Client->PlayerEntity->Position->z = $Packet->z; 32 | $Client->PlayerEntity->Position->pitch = $Packet->pitch; 33 | $Client->PlayerEntity->Position->yaw = $Packet->yaw; 34 | } 35 | 36 | public static function HandleRespawn($Packet, $Client, $Server) { 37 | $spawnpoint = $Client->World->ChunkProvider->spawnpoint; 38 | 39 | $Client->enqueuePacket(new SetPlayerPositionPacket( 40 | $spawnpoint->x, 41 | $spawnpoint->y, 42 | $spawnpoint->y + PlayerEntity::Height, 43 | $spawnpoint->z, 44 | 0, 45 | 0, 46 | true) 47 | ); 48 | 49 | $Client->enqueuePacket(new RespawnPacket()); 50 | } 51 | 52 | public static function HandleBlockPlacement($Packet, $Client, $Server) { 53 | // DIRECTION 54 | // 0 1 2 3 4 5 55 | // -Y +Y -Z +Z -X +X 56 | 57 | $direction = $Packet->direction; 58 | $x = $Packet->x; 59 | $y = $Packet->y; 60 | $z = $Packet->z; 61 | 62 | switch ($direction) { 63 | case 0: 64 | $y--; 65 | break; 66 | case 1: 67 | $y++; 68 | break; 69 | case 2: 70 | $z--; 71 | break; 72 | case 3: 73 | $z++; 74 | break; 75 | case 4: 76 | $x--; 77 | break; 78 | case 5: 79 | $x++; 80 | break; 81 | default: 82 | return 0; 83 | } 84 | 85 | $Coordinates3D = new Coordinates3D($x, $y, $z); 86 | 87 | if ($Packet->blockid == 0xFFFF) { 88 | return $Server->sendMessage("Use item not implemented yet!"); 89 | } 90 | 91 | if (!$Server->EntityManager->checkForBlockingEntities($Coordinates3D)) { 92 | $Server->World->setBlockID($Coordinates3D, $Packet->blockid); 93 | $broadcastPacket = new BlockChangePacket($x, $y, $z, $Packet->blockid, 0x00); 94 | $Server->broadcastPacket($broadcastPacket); 95 | } 96 | } 97 | 98 | public static function HandleDigging($Packet, $Client, $Server) { 99 | $status = $Packet->status; 100 | $x = $Packet->x; 101 | $y = $Packet->y; 102 | $z = $Packet->z; 103 | 104 | $coords = new Coordinates3D($x, $y, $z); 105 | 106 | $face = $Packet->face; 107 | 108 | switch ($status) { 109 | case 0: 110 | return 0; 111 | break; 112 | case 2: 113 | $slot_index = $Client->Inventory->findEmptySpace(); 114 | 115 | // Translate block coordinates to chunk coordinates 116 | // Fetch the chunk that contains that block coords 117 | // Get the block id from the chunk 118 | // Find empty space where we can increment or add w/ the block id 119 | // Update the player inventory with the block id 120 | 121 | if ($slot_index > -1) { 122 | } 123 | 124 | $Server->World->setBlockID($coords, 0x00); 125 | $broadcastPacket = new BlockChangePacket($x, $y, $z, 0x00, 0x00); 126 | $Server->broadcastPacket($broadcastPacket); 127 | break; 128 | case 4: 129 | return 0; 130 | break; 131 | default: 132 | return 0; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/MultiplayerServer.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | namespace HHVMCraft\Core\Networking; 13 | 14 | use Evenement\EventEmitter; 15 | 16 | use HHVMCraft\API\BlockRepository; 17 | use HHVMCraft\API\CraftingRepository; 18 | use HHVMCraft\Core\Entities\EntityManager; 19 | use HHVMCraft\Core\Helpers\Logger; 20 | use HHVMCraft\Core\Networking\Handlers; 21 | use HHVMCraft\Core\Networking\PackerReader; 22 | use HHVMCraft\Core\Networking\Packets\ChatMessagePacket; 23 | use HHVMCraft\Core\Networking\Packets\KeepAlivePacket; 24 | use HHVMCraft\Core\World\World; 25 | use React\Socket\Server; 26 | 27 | class MultiplayerServer extends EventEmitter { 28 | public $address; 29 | public $Clients = []; 30 | 31 | public $PacketHandler; 32 | public $PacketReader; 33 | public $EntityManager; 34 | public $World; 35 | 36 | public $loop; 37 | public $socket; 38 | 39 | public $tickRate = 0.05; 40 | 41 | public function __construct($address) { 42 | $this->address = $address; 43 | $this->loop = \React\EventLoop\Factory::create(); 44 | $this->socket = new Server($this->loop); 45 | 46 | $this->PacketReader = new PacketReader(); 47 | $this->PacketReader->registerPackets(); 48 | 49 | $this->BlockRepository = new BlockRepository(); 50 | $this->CraftingRepository = new CraftingRepository(); 51 | 52 | $this->PacketHandler = new PacketHandler($this); 53 | $this->World = new World("Flatland", $this->BlockRepository); 54 | 55 | $this->EntityManager = new EntityManager($this, $this->World); 56 | 57 | $this->Logger = new Logger(); 58 | } 59 | 60 | public function start($port) { 61 | $this->socket->on('connection', function ($connection) { 62 | $this->Logger->throwLog("New Connection"); 63 | $this->acceptClient($connection); 64 | }); 65 | 66 | $this->socket->listen($port, $this->address); 67 | 68 | $this->loop->addPeriodicTimer($this->tickRate, function () { 69 | $this->EntityManager->update(); 70 | }); 71 | 72 | $this->loop->addPeriodicTimer(1, function () { 73 | $this->emitKeepAlive(); 74 | $this->World->updateTime(); 75 | }); 76 | 77 | $this->Logger->throwLog("Listening on address: " . $this->address . ":" . $port); 78 | $this->loop->run(); 79 | } 80 | 81 | public function acceptClient($connection) { 82 | $client = new Client($connection, $this); 83 | $this->Clients[$client->uuid] = $client; 84 | } 85 | 86 | public function handlePacket($client) { 87 | $self = $this; 88 | $this->loop->nextTick(function() use ($self, $client) { 89 | $packet = $self->PacketReader->readPacket($client); 90 | 91 | if ($packet) { 92 | $self->loop->nextTick(function() use ($self, $packet, $client) { 93 | $self->PacketHandler->handlePacket($packet, $client, $self); 94 | }); 95 | } 96 | }); 97 | } 98 | 99 | public function writePacket($packet, $client) { 100 | $self = $this; 101 | 102 | $this->loop->nextTick(function() use ($self, $packet, $client) { 103 | $self->PacketReader->writePacket($packet, $client); 104 | }); 105 | } 106 | 107 | public function broadcastPacket($packet) { 108 | foreach ($this->Clients as $Client) { 109 | $Client->enqueuePacket($packet); 110 | } 111 | } 112 | 113 | public function handleDisconnect($Client, $ServerOriginated = false, $reason="") { 114 | if ($ServerOriginated) { 115 | $Client->disconnectWithReason($reason); 116 | } else { 117 | $Client->disconnect(); 118 | } 119 | 120 | $Client->connection->handleClose(); 121 | $Client->connection->close(); 122 | 123 | unset($this->Clients[$Client->uuid]); 124 | 125 | $this->sendMessage($Client->username." has disconnected from the server."); 126 | } 127 | 128 | public function emitKeepAlive() { 129 | foreach ($this->Clients as $Client) { 130 | $Client->enqueuePacket(new KeepAlivePacket()); 131 | } 132 | } 133 | 134 | public function sendMessage($message="") { 135 | $this->Logger->throwLog($message); 136 | 137 | foreach ($this->Clients as $Client) { 138 | $Client->enqueuePacket(new ChatMessagePacket( 139 | $message 140 | )); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/PacketHandler.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking; 9 | 10 | use HHVMCraft\Core\Networking\Handlers; 11 | use HHVMCraft\Core\Networking\Packets; 12 | 13 | class PacketHandler { 14 | public $server; 15 | public $LoginHandler; 16 | public $Handlers; 17 | 18 | public function __construct($server) { 19 | $this->server = $server; 20 | $this->Handlers = new \SplFixedArray(256); 21 | 22 | $this->registerHandlers(); 23 | 24 | $dataHandler = new Handlers\DataHandler(); 25 | $chatHandler = new Handlers\ChatHandler(); 26 | $loginHandler = new Handlers\LoginHandler(); 27 | $playerHandler = new Handlers\PlayerHandler(); 28 | 29 | } 30 | 31 | public function registerHandlers() { 32 | $this->Handlers[Packets\KeepAlivePacket::id] = function($packet, $client, $server) { Handlers\DataHandler::HandleKeepAlive($packet, $client, $server); }; 33 | $this->Handlers[Packets\DisconnectPacket::id] = function($packet, $client, $server) { Handlers\DataHandler::HandleDisconnect($packet, $client, $server); }; 34 | $this->Handlers[Packets\ChatMessagePacket::id] = function($packet, $client, $server) { Handlers\ChatHandler::HandleChatMessage($packet, $client, $server); }; 35 | 36 | $this->Handlers[Packets\HandshakePacket::id] = function($packet, $client, $server) { Handlers\LoginHandler::HandleHandshake($packet, $client, $server); }; 37 | $this->Handlers[Packets\LoginRequestPacket::id] = function($packet, $client, $server) { Handlers\LoginHandler::HandleLoginRequest($packet, $client, $server); }; 38 | 39 | $this->Handlers[Packets\PlayerGroundedPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandleGrounded($packet, $client, $server); }; 40 | $this->Handlers[Packets\PlayerPositionPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandlePosition($packet, $client, $server); }; 41 | $this->Handlers[Packets\PlayerLookPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandleLook($packet, $client, $server); }; 42 | $this->Handlers[Packets\PlayerPositionAndLookPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandlePositionAndLook($packet, $client, $server); }; 43 | $this->Handlers[Packets\RespawnPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandleRespawn($packet, $client, $server); }; 44 | $this->Handlers[Packets\PlayerBlockPlacementPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandleBlockPlacement($packet, $client, $server); }; 45 | $this->Handlers[Packets\PlayerDiggingPacket::id] = function($packet, $client, $server) { Handlers\PlayerHandler::HandleDigging($packet, $client, $server); }; 46 | } 47 | 48 | public function handlePacket($packet, $client, $server) { 49 | $func = $this->Handlers[$packet::id]; 50 | 51 | 52 | if ($func) { 53 | // Through some fun hackery, the correct handler function 54 | // is called by figuring out the handler by packet ID. 55 | // This allows us to have a base class Handler around generic action 56 | // while specificing a specific function to handle the packet. 57 | $func($packet, $client, $server); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/PacketReader.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | namespace HHVMCraft\Core\Networking; 12 | 13 | use HHVMCraft\Core\Networking\Packets; 14 | 15 | class PacketReader { 16 | public $protocol_version; 17 | public $ServerboundPackets = []; 18 | public $ClientboundPackets = []; 19 | 20 | public function __construct($protocol_version = 14) { 21 | $this->protocol_version = $protocol_version; 22 | $this->ServerboundPackets = new \SplFixedArray(256); 23 | $this->ClientboundPackets = new \SplFixedArray(256); 24 | } 25 | 26 | public function registerPackets() { 27 | // Register new packet type. type: packet, serverbound: bool, clientbound: bool. 28 | $this->registerPacketType('Packets\KeepAlivePacket', true, false); 29 | $this->registerPacketType('Packets\LoginRequestPacket', true, false); 30 | $this->registerPacketType('Packets\LoginResponsePacket', false, true); 31 | $this->registerPacketType('Packets\HandshakePacket', true, false); 32 | $this->registerPacketType('Packets\HandshakeResponsePacket', false, true); 33 | $this->registerPacketType('Packets\ChatMessagePacket', true, true); 34 | $this->registerPacketType('Packets\TimeUpdatePacket', false, true); 35 | $this->registerPacketType('Packets\EntityEquipmentPacket', false, true); 36 | $this->registerPacketType('Packets\SpawnPositionPacket', false, true); 37 | $this->registerPacketType('Packets\UseEntityPacket', true, false); 38 | $this->registerPacketType('Packets\UpdateHealthPacket', false, true); 39 | $this->registerPacketType('Packets\RespawnPacket'); 40 | $this->registerPacketType('Packets\PlayerGroundedPacket', true, false); 41 | $this->registerPacketType('Packets\PlayerPositionPacket', true, false); 42 | $this->registerPacketType('Packets\PlayerLookPacket', true, false); 43 | $this->registerPacketType('Packets\PlayerPositionAndLookPacket', true, false); 44 | $this->registerPacketType('Packets\SetPlayerPositionPacket', false, true); 45 | $this->registerPacketType('Packets\HoldingChangePacket', true, false); 46 | $this->registerPacketType('Packets\EntityActionPacket', true, false); 47 | $this->registerPacketType('Packets\PlayerDiggingPacket', true, false); 48 | $this->registerPacketType('Packets\PlayerBlockPlacementPacket', true, false); 49 | $this->registerPacketType('Packets\UseBedPacket', false, true); 50 | $this->registerPacketType('Packets\AnimationPacket'); 51 | $this->registerPacketType('Packets\SpawnPlayerPacket', false, true); 52 | $this->registerPacketType('Packets\PickupSpawnPacket', false, true); 53 | $this->registerPacketType('Packets\CollectItemPacket', false, true); 54 | // $this->registerPacketType(Packets\SpawnGenericEntityPacket, false, true); 55 | // $this->registerPacketType(Packets\SpawnMobPacket, false, true); 56 | // $this->registerPacketType(Packets\SpawnPaintingPacket, false, true); 57 | 58 | $this->registerPacketType('Packets\EntityVelocityPacket', false, true); 59 | // $this->registerPacketType(Packets\DestroyEntityPacket, false, true); 60 | // $this->registerPacketType(Packets\UselessEntityPacket, false, true); 61 | $this->registerPacketType('Packets\EntityRelativeMovePacket', false, true); 62 | // $this->registerPacketType(Packets\EntityLookPacket, false, true); 63 | // $this->registerPacketType(Packets\EntityLookAndRelativeMovePacket, false, true); 64 | $this->registerPacketType('Packets\EntityTeleportPacket', false, true); 65 | 66 | $this->registerPacketType('Packets\EntityStatusPacket', false, true); 67 | $this->registerPacketType('Packets\AttachEntityPacket', false, true); 68 | $this->registerPacketType('Packets\EntityMetadataPacket', false, true); 69 | 70 | $this->registerPacketType('Packets\ChunkPreamblePacket', false, true); 71 | $this->registerPacketType('Packets\ChunkDataPacket', false, true); 72 | // $this->registerPacketType(Packets\BulkBlockChangePacket, false, true); 73 | $this->registerPacketType('Packets\BlockChangePacket', false, true); 74 | // $this->registerPacketType(Packets\BlockActionPacket, false, true); 75 | 76 | // $this->registerPacketType(Packets\ExplosionPacket, false, true); 77 | $this->registerPacketType('Packets\SoundEffectPacket', false, true); 78 | 79 | // $this->registerPacketType(Packets\EnvironmentStatePacket, false, true); 80 | // $this->registerPacketType(Packets\LightningPacket, false, true); 81 | 82 | $this->registerPacketType('Packets\OpenWindowPacket', false, true); 83 | $this->registerPacketType('Packets\CloseWindowPacket'); 84 | // $this->registerPacketType(Packets\ClickWindowPacket, true, false); 85 | $this->registerPacketType('Packets\SetSlotPacket', false, true); 86 | $this->registerPacketType('Packets\WindowItemsPacket', false, true); 87 | $this->registerPacketType('Packets\UpdateProgressBarPacket', false, true); 88 | // $this->registerPacketType(Packets\TransactionStatusPacket, false, true); 89 | 90 | $this->registerPacketType('Packets\UpdateSignPacket', false, true); 91 | // $this->registerPacketType(Packets\MapDataPacket, false, true); 92 | 93 | // $this->registerPacketType(Packets\UpdateStatisticPacket, false, true); 94 | 95 | $this->registerPacketType('Packets\DisconnectPacket', true, true); 96 | 97 | } 98 | 99 | public function registerPacketType($type, $serverbound = true, $clientbound = true) { 100 | if ($serverbound) { 101 | $this->ServerboundPackets[constant('HHVMCraft\Core\Networking\\' . $type . '::id')] = $type; 102 | } 103 | if ($clientbound) { 104 | $this->ClientboundPackets[constant('HHVMCraft\Core\Networking\\' . $type . '::id')] = $type; 105 | } 106 | } 107 | 108 | public function readPacket($client, $serverbound = true) { 109 | $id = $client->streamWrapper->readInt8(); 110 | 111 | if ($serverbound && isset($this->ServerboundPackets[$id])) { 112 | $type = $this->ServerboundPackets[$id]; 113 | } else if (isset($this->ClientboundPackets[$id])) { 114 | $type = $this->ClientboundPackets[$id]; 115 | } else if ($id == -1) { 116 | $type = $this->ServerboundPackets[0xFF]; 117 | } 118 | 119 | if ($type == null) { 120 | $client->Server->Logger->throwError("Unrecognized Packet ID: " . $id); 121 | 122 | return; 123 | } 124 | 125 | $construct = "HHVMCraft\\Core\\Networking\\" . $type; 126 | 127 | $packet = new $construct(); 128 | $packet->readPacket($client->streamWrapper); 129 | 130 | if ($type == "Packets\\PlayerGroundedPacket") { 131 | return; 132 | } 133 | 134 | return $packet; 135 | } 136 | 137 | public function writePacket($packet, $client) { 138 | if ($packet->writePacket($client->streamWrapper) == false) { 139 | $client->enqueuePacket($packet); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/AnimationPacket.php: -------------------------------------------------------------------------------- 1 | eid = $StreamWrapper->readInt(); 15 | $this->animate = $StreamWrapper->readInt8(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/AttachEntityPacket.php: -------------------------------------------------------------------------------- 1 | entity_id = $entity_id; 14 | $this->vehicle_id = $vehicle_id; 15 | } 16 | 17 | public function writePacket(StreamWrapper $StreamWrapper) { 18 | $p = $StreamWrapper->writeInt8(self::id) . 19 | $StreamWrapper->writeInt($this->entity_id) . 20 | $StreamWrapper->writeInt($this->vehicle_id); 21 | 22 | return $StreamWrapper->writePacket($p); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/BlockChangePacket.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking\Packets; 9 | use HHVMCraft\Core\Helpers\Hex; 10 | use HHVMCraft\Core\Networking\StreamWrapper; 11 | 12 | class BlockChangePacket { 13 | const id = 0x35; 14 | public $x; 15 | public $y; 16 | public $z; 17 | public $blockId; 18 | public $blockMetadata; 19 | 20 | public function __construct($x, $y, $z, $blockId, $blockMetadata) { 21 | $this->x = $x; 22 | $this->y = $y; 23 | $this->z = $z; 24 | $this->blockId = $blockId; 25 | $this->blockMetadata = $blockMetadata; 26 | } 27 | 28 | public function writePacket(StreamWrapper $StreamWrapper) { 29 | $str = $StreamWrapper->writeInt8(self::id) . 30 | $StreamWrapper->writeInt($this->x) . 31 | $StreamWrapper->writeInt8($this->y) . 32 | $StreamWrapper->writeInt($this->z) . 33 | $StreamWrapper->writeInt8($this->blockId) . 34 | $StreamWrapper->writeInt8($this->blockMetadata); 35 | 36 | return $StreamWrapper->writePacket($str); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/ChatMessagePacket.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking\Packets; 9 | 10 | use HHVMCraft\Core\Networking\StreamWrapper; 11 | 12 | class ChatMessagePacket { 13 | const id = 0x03; 14 | public $message; 15 | 16 | public function __construct($message="") { 17 | $this->message = $message; 18 | } 19 | 20 | public function writePacket(StreamWrapper $StreamWrapper) { 21 | $str = $StreamWrapper->writeInt8(self::id) . 22 | $StreamWrapper->writeInt16(strlen($this->message)) . 23 | $StreamWrapper->writeString16($this->message); 24 | 25 | return $StreamWrapper->writePacket($str); 26 | } 27 | 28 | public function readPacket(StreamWrapper $StreamWrapper) { 29 | $this->message = $StreamWrapper->readString16(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/ChunkDataPacket.php: -------------------------------------------------------------------------------- 1 | x = $x; 23 | $this->y = $y; 24 | $this->z = $z; 25 | $this->Width = $Width - 1; 26 | $this->Height = $Height - 1; 27 | $this->Depth = $Depth - 1; 28 | $this->BlockData = $BlockData; 29 | } 30 | 31 | public function writePacket(StreamWrapper $StreamWrapper) { 32 | $str = $StreamWrapper->writeInt8(self::id) . 33 | $StreamWrapper->writeInt($this->x) . 34 | $StreamWrapper->writeInt16($this->y) . 35 | $StreamWrapper->writeInt($this->z) . 36 | $StreamWrapper->writeInt8(15) . 37 | $StreamWrapper->writeInt8(127) . 38 | $StreamWrapper->writeInt8(15) . 39 | $StreamWrapper->writeInt(strlen($this->BlockData)) . 40 | $this->BlockData; 41 | 42 | return $StreamWrapper->writePacket($str); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/ChunkPreamblePacket.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking\Packets; 9 | use HHVMCraft\Core\Helpers\Hex; 10 | use HHVMCraft\Core\Networking\StreamWrapper; 11 | 12 | class ChunkPreamblePacket { 13 | const id = 0x32; 14 | public $x; 15 | public $z; 16 | public $load; 17 | 18 | public function __construct($x, $z, $load = true) { 19 | $this->x = $x; 20 | $this->z = $z; 21 | $this->load = $load; 22 | } 23 | 24 | public function writePacket(StreamWrapper $StreamWrapper) { 25 | $str = $StreamWrapper->writeInt8(self::id) . 26 | $StreamWrapper->writeInt($this->x) . 27 | $StreamWrapper->writeInt($this->z) . 28 | $StreamWrapper->writeInt8($this->load); 29 | 30 | return $StreamWrapper->writePacket($str); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/CloseWindowPacket.php: -------------------------------------------------------------------------------- 1 | windowId = $StreamWrapper->readInt8(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/CollectItemPacket.php: -------------------------------------------------------------------------------- 1 | collected_eid = $collected_eid; 15 | $this->collector_eid = $collector_eid; 16 | } 17 | 18 | public function readPacket(StreamWrapper $StreamWrapper) { 19 | $this->collected_eid = $StreamWrapper->readInt(); 20 | $this->collector_eid = $StreamWrapper->readInt(); 21 | } 22 | 23 | public function writePacket(StreamWrapper $StreamWrapper) { 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/DestroyEntityPacket.php: -------------------------------------------------------------------------------- 1 | entityId = $entityId; 13 | } 14 | 15 | public function writePacket(StreamWrapper $StreamWrapper) { 16 | $str = $StreamWrapper->writeInt8(self::id) . 17 | $StreamWrapper->writeInt($this->entityId); 18 | 19 | return $StreamWrapper->writePacket($str); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/DisconnectPacket.php: -------------------------------------------------------------------------------- 1 | reason = $reason; 13 | } 14 | 15 | public function writePacket(StreamWrapper $StreamWrapper) { 16 | $str = $StreamWrapper->writeInt8(self::id) . 17 | $StreamWrapper->writeInt16(strlen($this->reason)) . 18 | $StreamWrapper->writeString16($this->reason); 19 | 20 | return $StreamWrapper->writePacket($str); 21 | } 22 | 23 | public function readPacket(StreamWrapper $StreamWrapper) { 24 | $this->reason = $StreamWrapper->readString16(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityActionPacket.php: -------------------------------------------------------------------------------- 1 | eid = $StreamWrapper->readInt(); 15 | $this->action = $StreamWrapper->readInt8(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityEquipmentPacket.php: -------------------------------------------------------------------------------- 1 | eid = $eid; 17 | $this->slot = $slot; 18 | $this->itemid = $itemid; 19 | $this->damage = $damage; 20 | } 21 | 22 | public function writePacket(StreamWrapper $StreamWrapper) { 23 | $str = $StreamWrapper->writeInt8(self::id) . 24 | $StreamWrapper->writeInt($this->eid) . 25 | $StreamWrapper->writeInt16($this->slot) . 26 | $StreamWrapper->writeInt16($this->itemid) . 27 | $StreamWrapper->writeInt16($this->damage); 28 | 29 | return $StreamWrapper->writePacket($str); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityMetadataPacket.php: -------------------------------------------------------------------------------- 1 | eid = $StreamWrapper->readInt(); 15 | 16 | // TODO (vy): Implement metadata parsing.. 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityRelativeMovePacket.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking\Packets; 9 | 10 | use HHVMCraft\Core\Networking\StreamWrapper; 11 | 12 | class EntityRelativeMovePacket { 13 | const id = 0x1F; 14 | 15 | public $eid; // Entity ID 16 | public $dX; // X axis relative movement as absolute int 17 | public $dY; // Y axis relative movement as absolute int 18 | public $dZ; // Z axis relative movement as absolute int 19 | 20 | public function __construct($eid, $dX, $dY, $dZ) { 21 | $this->eid = $eid; 22 | $this->dX = $dX; 23 | $this->dY = $dY; 24 | $this->dZ = $dZ; 25 | } 26 | 27 | public function writePacket(StreamWrapper $StreamWrapper) { 28 | $str = $StreamWrapper->writeInt8(self::id) . 29 | $StreamWrapper->writeInt($this->eid) . 30 | $StreamWrapper->writeInt8($this->dX) . 31 | $StreamWrapper->writeInt8($this->dY) . 32 | $StreamWrapper->writeInt8($this->dZ); 33 | 34 | return $StreamWrapper->writePacket($str); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityStatusPacket.php: -------------------------------------------------------------------------------- 1 | entity_id = $entity_id; 14 | $this->entity_status = $entity_status; 15 | } 16 | 17 | public function writePacket(StreamWrapper $StreamWrapper) { 18 | $p = $StreamWrapper->writeInt8(self::id) . 19 | $StreamWrapper->writeInt($this->entity_id) . 20 | $StreamWrapper->writeInt8($this->entity_status); 21 | 22 | return $StreamWrapper->writePacket($p); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityTeleportPacket.php: -------------------------------------------------------------------------------- 1 | entityId = $entityId; 18 | $this->x = $x; 19 | $this->y = $y; 20 | $this->z = $z; 21 | $this->yaw = $yaw; 22 | $this->pitch = $pitch; 23 | } 24 | 25 | public function writePacket(StreamWrapper $StreamWrapper) { 26 | $str = $StreamWrapper->writeInt8(self::id) . 27 | $StreamWrapper->writeInt($this->x) . 28 | $StreamWrapper->writeInt($this->y) . 29 | $StreamWrapper->writeInt($this->z) . 30 | $StreamWrapper->writeInt8($this->yaw) . 31 | $StreamWrapper->writeInt8($this->pitch); 32 | 33 | return $StreamWrapper->writePacket($str); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/EntityVelocityPacket.php: -------------------------------------------------------------------------------- 1 | entityId = $entityId; 16 | $this->xVel = $xVel; 17 | $this->yVel = $yVel; 18 | $this->zVel = $zVel; 19 | } 20 | 21 | public function writePacket(StreamWrapper $StreamWrapper) { 22 | $str = $StreamWrapper->writeInt8(self::id) . 23 | $StreamWrapper->writeInt($this->entityId) . 24 | $StreamWrapper->writeInt16($this->xVel) . 25 | $StreamWrapper->writeInt16($this->yVel) . 26 | $StreamWrapper->writeInt16($this->zVel); 27 | 28 | return $StreamWrapper->writePacket($str); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/HandshakePacket.php: -------------------------------------------------------------------------------- 1 | username = $username; 13 | } 14 | 15 | public function readPacket(StreamWrapper $StreamWrapper) { 16 | $this->username = $StreamWrapper->readString16(); 17 | } 18 | 19 | public function writePacket(StreamWrapper $stream) { 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/HandshakeResponsePacket.php: -------------------------------------------------------------------------------- 1 | connectionHash = $connectionHash; 13 | } 14 | 15 | public function readPacket(StreamWrapper $StreamWrapper) { 16 | } 17 | 18 | public function writePacket(StreamWrapper $StreamWrapper) { 19 | $str = $StreamWrapper->writeInt8(self::id) . 20 | $StreamWrapper->writeInt16(strlen($this->connectionHash)) . 21 | $StreamWrapper->writeString16($this->connectionHash); 22 | 23 | return $StreamWrapper->writePacket($str); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/HoldingChangePacket.php: -------------------------------------------------------------------------------- 1 | slotid = $StreamWrapper->readInt16(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/KeepAlivePacket.php: -------------------------------------------------------------------------------- 1 | writeInt8(self::id); 12 | return $StreamWrapper->writePacket($str); 13 | } 14 | 15 | public function readPacket(StreamWrapper $StreamWrapper) { 16 | $StreamWrapper->readInt8(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/LoginRequestPacket.php: -------------------------------------------------------------------------------- 1 | protocolVersion = $StreamWrapper->readInt(); 14 | $this->username = $StreamWrapper->readString16(); 15 | 16 | // These bytes are not used.. 17 | 18 | $StreamWrapper->readLong(); 19 | $StreamWrapper->readInt8(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/LoginResponsePacket.php: -------------------------------------------------------------------------------- 1 | EntityID = $entityID; 15 | $this->Seed = $seed; 16 | $this->Dimension = $dimension; 17 | } 18 | 19 | public function writePacket(StreamWrapper $StreamWrapper) { 20 | $p = $StreamWrapper->writeInt8(self::id) . 21 | $StreamWrapper->writeInt($this->EntityID) . 22 | $StreamWrapper->writeInt16(0) . 23 | $StreamWrapper->writeLong($this->Seed) . 24 | $StreamWrapper->writeInt8($this->Dimension); 25 | 26 | return $StreamWrapper->writePacket($p); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/OpenWindowPacket.php: -------------------------------------------------------------------------------- 1 | windowId = $windowId; 16 | $this->inventoryType = $inventoryType; 17 | $this->windowTitle = $windowTitle; 18 | $this->numberOfSlots = $numberOfSlots; 19 | } 20 | 21 | public function writePacket(StreamWrapper $StreamWrapper) { 22 | $str = $StreamWrapper->writeInt8(self::id) . 23 | $StreamWrapper->writeInt8($this->windowId) . 24 | $StreamWrapper->writeInt8($this->inventoryType) . 25 | $StreamWrapper->writeString16($this->windowTitle) . 26 | $StreamWrapper->writeInt8($this->numberOfSlots); 27 | 28 | return $StreamWrapper->writePacket($str); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PickupSpawnPacket.php: -------------------------------------------------------------------------------- 1 | eid = $StreamWrapper->readInt(); 23 | $this->item = $StreamWrapper->readInt16(); 24 | $this->itemcount = $StreamWrapper->readInt8(); 25 | $this->damage = $StreamWrapper->readInt16(); 26 | $this->x = $StreamWrapper->readInt(); 27 | $this->y = $StreamWrapper->readInt(); 28 | $this->z = $StreamWrapper->readInt(); 29 | $this->rotation = $StreamWrapper->readInt8(); 30 | $this->pitch = $StreamWrapper->readInt8(); 31 | $this->roll = $StreamWrapper->readInt8(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerBlockPlacementPacket.php: -------------------------------------------------------------------------------- 1 | x = $StreamWrapper->readInt(); 20 | $this->y = $StreamWrapper->readInt8(); 21 | $this->z = $StreamWrapper->readInt(); 22 | $this->direction = $StreamWrapper->readInt8(); 23 | $this->blockid = $StreamWrapper->readInt16(); 24 | 25 | if ($this->blockid >= 0x00) { 26 | $this->amount = $StreamWrapper->readInt8(); 27 | $this->damage = $StreamWrapper->readInt16(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerDiggingPacket.php: -------------------------------------------------------------------------------- 1 | status = $StreamWrapper->readInt8(); 23 | $this->x = $StreamWrapper->readInt(); 24 | $this->y = $StreamWrapper->readInt8(); 25 | $this->z = $StreamWrapper->readInt(); 26 | $this->face = $StreamWrapper->readInt8(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerGroundedPacket.php: -------------------------------------------------------------------------------- 1 | onGround = $StreamWrapper->readInt8(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerLookPacket.php: -------------------------------------------------------------------------------- 1 | yaw = $StreamWrapper->readInt(); 16 | $this->pitch = $StreamWrapper->readInt(); 17 | $this->onGround = $StreamWrapper->readBool(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerPositionAndLookPacket.php: -------------------------------------------------------------------------------- 1 | x = $StreamWrapper->readDouble(); 20 | $this->y = $StreamWrapper->readDouble(); 21 | $this->stance = $StreamWrapper->readDouble(); 22 | $this->z = $StreamWrapper->readDouble(); 23 | $this->yaw = $StreamWrapper->readInt(); 24 | $this->pitch = $StreamWrapper->readInt(); 25 | $this->onGround = $StreamWrapper->readBool(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/PlayerPositionPacket.php: -------------------------------------------------------------------------------- 1 | x = $StreamWrapper->readDouble(); 18 | $this->y = $StreamWrapper->readDouble(); 19 | $this->stance = $StreamWrapper->readDouble(); 20 | $this->z = $StreamWrapper->readDouble(); 21 | $this->onGround = $StreamWrapper->readBool(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/RespawnPacket.php: -------------------------------------------------------------------------------- 1 | world = $world; 13 | } 14 | 15 | public function readPacket(StreamWrapper $StreamWrapper) { 16 | $this->world = $StreamWrapper->readInt8(); 17 | } 18 | 19 | public function writePacket(StreamWrapper $StreamWrapper) { 20 | $str = $StreamWrapper->writeInt8(self::id) . 21 | $StreamWrapper->writeInt8($this->world); 22 | 23 | return $StreamWrapper->writePacket($str); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/SetPlayerPositionPacket.php: -------------------------------------------------------------------------------- 1 | x = $x; 19 | $this->y = $y; 20 | $this->z = $z; 21 | $this->stance = $stance; 22 | $this->yaw = $yaw; 23 | $this->pitch = $pitch; 24 | $this->onGround = $onGround; 25 | } 26 | 27 | public function writePacket(StreamWrapper $StreamWrapper) { 28 | $str = $StreamWrapper->writeInt8(self::id) . 29 | $StreamWrapper->writeDouble($this->x) . 30 | $StreamWrapper->writeDouble($this->stance) . 31 | $StreamWrapper->writeDouble($this->y) . 32 | $StreamWrapper->writeDouble($this->z) . 33 | $StreamWrapper->writeInt($this->yaw) . 34 | $StreamWrapper->writeInt($this->pitch) . 35 | $StreamWrapper->writeInt8($this->onGround); 36 | 37 | return $StreamWrapper->writePacket($str); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/SetSlotPacket.php: -------------------------------------------------------------------------------- 1 | window_id = $window_id; 17 | $this->slot = $slot; 18 | $this->item_id = $item_id; 19 | $this->item_count = $item_count; 20 | $this->item_uses = $item_uses; 21 | } 22 | 23 | public function writePacket(StreamWrapper $StreamWrapper) { 24 | $str = $StreamWrapper->writeInt8(self::id) . 25 | $StreamWrapper->writeInt8($this->window_id) . 26 | $StreamWrapper->writeInt16($this->slot) . 27 | $StreamWrapper->writeInt16($this->item_id) . 28 | $StreamWrapper->writeInt8($this->item_count) . 29 | $StreamWrapper->writeInt16($this->item_uses); 30 | 31 | return $StreamWrapper->writePacket($str); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/SoundEffectPacket.php: -------------------------------------------------------------------------------- 1 | effectId = $effectId; 17 | $this->x = $x; 18 | $this->y = $y; 19 | $this->z = $z; 20 | $this->soundData = $soundData; 21 | } 22 | 23 | public function writePacket(StreamWrapper $StreamWrapper) { 24 | $str = $StreamWrapper->writeInt8(self::id) . 25 | $StreamWrapper->writeInt($this->effectId) . 26 | $StreamWrapper->writeInt($this->x) . 27 | $StreamWrapper->writeInt8($this->y) . 28 | $StreamWrapper->writeInt($this->z) . 29 | $StreamWrapper->writeInt($this->soundData); 30 | 31 | return $StreamWrapper->writePacket($str); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/SpawnPlayerPacket.php: -------------------------------------------------------------------------------- 1 | entityId = $entityId; 20 | $this->playerName = $playerName; 21 | $this->x = $x; 22 | $this->y = $y; 23 | $this->z = $z; 24 | $this->yaw = $yaw; 25 | $this->pitch = $pitch; 26 | $this->currentItem = $currentItem; 27 | } 28 | 29 | public function writePacket(StreamWrapper $StreamWrapper) { 30 | $str = $StreamWrapper->writeInt8(self::id) . 31 | $StreamWrapper->writeInt($this->entityId) . 32 | $StreamWrapper->writeInt16(strlen($this->playerName)) . 33 | $StreamWrapper->writeString16($this->playerName) . 34 | $StreamWrapper->writeInt($this->x) . 35 | $StreamWrapper->writeInt($this->y) . 36 | $StreamWrapper->writeInt($this->z) . 37 | $StreamWrapper->writeInt8($this->yaw) . 38 | $StreamWrapper->writeInt8($this->pitch) . 39 | $StreamWrapper->writeInt16($this->currentItem); 40 | 41 | return $StreamWrapper->writePacket($str); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/SpawnPositionPacket.php: -------------------------------------------------------------------------------- 1 | x = $x; 15 | $this->y = $y; 16 | $this->z = $z; 17 | } 18 | 19 | public function writePacket(StreamWrapper $StreamWrapper) { 20 | $str = $StreamWrapper->writeInt8(self::id) . 21 | $StreamWrapper->writeInt($this->x) . 22 | $StreamWrapper->writeInt($this->y) . 23 | $StreamWrapper->writeInt($this->z); 24 | 25 | return $StreamWrapper->writePacket($str); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/TimeUpdatePacket.php: -------------------------------------------------------------------------------- 1 | time = $time; 13 | } 14 | 15 | public function writePacket(StreamWrapper $StreamWrapper) { 16 | $str = $StreamWrapper->writeInt8(self::id) . 17 | $StreamWrapper->writeLong($this->time); 18 | 19 | return $StreamWrapper->writePacket($str); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/UpdateHealthPacket.php: -------------------------------------------------------------------------------- 1 | health = $health; 13 | } 14 | 15 | public function writePacket(StreamWrapper $StreamWrapper) { 16 | $p = $StreamWrapper->writeInt8(self::id) . 17 | $StreamWrapper->writeInt16($this->health); 18 | 19 | return $StreamWrapper->writePacket($p); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/UpdateProgressBarPacket.php: -------------------------------------------------------------------------------- 1 | window_id = $window_id; 15 | $this->progress_bar = $progress_bar; 16 | $this->progress_value = $progress_value; 17 | } 18 | 19 | public function writePacket(StreamWrapper $StreamWrapper) { 20 | $p = $StreamWrapper->writeInt8(self::id) . 21 | $StreamWrapper->writeInt8($this->window_id) . 22 | $StreamWrapper->writeInt16($this->progress_bar) . 23 | $StreamWrapper->writeInt16($this->progress_value); 24 | 25 | return $StreamWrapper->writePacket($p); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/UpdateSignPacket.php: -------------------------------------------------------------------------------- 1 | x = $x; 19 | $this->y = $y; 20 | $this->z = $z; 21 | $this->text1 = $text1; 22 | $this->text2 = $text2; 23 | $this->text3 = $text3; 24 | $this->text4 = $text4; 25 | } 26 | 27 | public function writePacket(StreamWrapper $StreamWrapper) { 28 | $str = $StreamWrapper->writeInt8(self::id) . 29 | $StreamWrapper->writeInt($this->x) . 30 | $StreamWrapper->writeInt16($this->y) . 31 | $StreamWrapper->writeInt($this->z) . 32 | $StreamWrapper->writeInt16($this->text1) . 33 | $StreamWrapper->writeString16($this->text1) . 34 | $StreamWrapper->writeInt16($this->text2) . 35 | $StreamWrapper->writeString16($this->text2) . 36 | $StreamWrapper->writeInt16($this->text3) . 37 | $StreamWrapper->writeString16($this->text3) . 38 | $StreamWrapper->writeInt16($this->text4) . 39 | $StreamWrapper->writeString16($this->text4); 40 | 41 | return $StreamWrapper->writePacket($str); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/UseBedPacket.php: -------------------------------------------------------------------------------- 1 | eid = $StreamWrapper->readInt(); 18 | $this->in_bed = $StreamWrapper->readInt8(); 19 | $this->x = $StreamWrapper->readInt(); 20 | $this->y = $StreamWrapper->readInt8(); 21 | $this->z = $StreamWrapper->readInt(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/UseEntityPacket.php: -------------------------------------------------------------------------------- 1 | user = $StreamWrapper->readInt(); 16 | $this->target = $StreamWrapper->readInt(); 17 | $this->leftclick = $StreamWrapper->readInt8(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/Packets/WindowItemsPacket.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | namespace HHVMCraft\Core\Networking\Packets; 9 | 10 | use HHVMCraft\Core\Networking\StreamWrapper; 11 | 12 | class WindowItemsPacket { 13 | const id = 0x68; 14 | public $windowId; 15 | public $items; 16 | 17 | public function __construct($windowId, $items) { 18 | $this->windowId = $windowId; 19 | $this->items = $items; 20 | } 21 | 22 | public function writePacket(StreamWrapper $StreamWrapper) { 23 | $str = $StreamWrapper->writeInt8(self::id) . 24 | $StreamWrapper->writeInt8($this->windowId) . 25 | $StreamWrapper->writeInt16(count($this->items)); 26 | 27 | for ($i = 0; $i < count($this->items); $i++) { 28 | $str = $str . $StreamWrapper->writeInt16($this->items[$i]->id); 29 | 30 | if (!$this->items[$i]->isEmpty()) { 31 | $str = $str . $StreamWrapper->writeInt8($this->items[$i]->icount) . 32 | $StreamWrapper->writeInt16($this->items[$i]->metadata); 33 | } 34 | } 35 | 36 | return $StreamWrapper->writePacket($str); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Networking/StreamWrapper.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | 11 | namespace HHVMCraft\Core\Networking; 12 | 13 | use HHVMCraft\Core\Helpers\Hex; 14 | 15 | // http://stackoverflow.questions/16039751/php-pack-format-for-signed-32-int-big-endian 16 | define('BIG_ENDIAN', pack('L', 1) === pack('N', 1)); 17 | 18 | class StreamWrapper { 19 | public $stream; 20 | public $streamBuffer; 21 | 22 | public function __construct($stream) { 23 | $this->stream = $stream; 24 | $this->streamBuffer = []; 25 | } 26 | 27 | public function data($data) { 28 | $arr = array_reverse(str_split(bin2hex($data), 2)); 29 | 30 | $this->streamBuffer = array_merge($this->streamBuffer, $arr); 31 | } 32 | 33 | public function close() { 34 | $this->streamBuffer = []; 35 | } 36 | 37 | public function read($len) { 38 | $s = ""; 39 | for ($i = 0; $i < $len; $i++) { 40 | $s = $s.hex2bin(array_pop($this->streamBuffer)); 41 | } 42 | 43 | return $s; 44 | } 45 | 46 | public function readInt8() { 47 | return unpack("c", $this->read(1))[1]; 48 | } 49 | 50 | public function writeInt8($data) { 51 | return pack("c", $data); 52 | } 53 | 54 | public function readBool() { 55 | return (bool) $this->readInt8(); 56 | } 57 | 58 | public function writeBool($data) { 59 | if ($data == true) { 60 | $this->writeInt8(0x01); 61 | } else { 62 | $this->writeInt8(0x00); 63 | } 64 | } 65 | 66 | public function readInt16() { 67 | return unpack("n", $this->read(2))[1]; 68 | } 69 | 70 | public function writeInt16($data) { 71 | return pack("n*", $data); 72 | } 73 | 74 | public function readInt() { 75 | return unpack("N", $this->read(4))[1]; 76 | } 77 | 78 | public function writeInt($data) { 79 | if (BIG_ENDIAN) { 80 | return pack('l', $data); 81 | } 82 | 83 | return strrev(pack("l*", $data)); 84 | } 85 | 86 | public function readLong() { 87 | return unpack("q", $this->read(8))[1]; 88 | } 89 | 90 | public function writeLong($data) { 91 | return pack("q*", $data); 92 | } 93 | 94 | public function readString16() { 95 | $l = $this->readInt16(); 96 | $str = ""; 97 | 98 | for ($i = 0; $i < $l; $i++) { 99 | $str = $str . chr($this->readInt16()); 100 | } 101 | 102 | if (strlen($str) > 0) { 103 | return $str; 104 | } else { 105 | // No string found? 106 | } 107 | } 108 | 109 | public function writeString16($str) { 110 | $str = iconv("UTF-8", "UTF-16BE", $str); 111 | 112 | return $str; 113 | } 114 | 115 | public function readDouble() { 116 | return unpack("d", strrev($this->read(8)))[1]; 117 | } 118 | 119 | public function writeDouble($data) { 120 | if (BIG_ENDIAN) { 121 | return pack("d*", $data); 122 | } 123 | 124 | return strrev(pack("d", $data)); 125 | } 126 | 127 | public function writePacket($data) { 128 | $res = $this->stream->write($data); 129 | if ($res != false) { 130 | return true; 131 | } else { 132 | return false; 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Physics/PhysicsEngine.php: -------------------------------------------------------------------------------- 1 | World = $World; 12 | $this->BlockRepository = $BlockRepository; 13 | } 14 | 15 | // TODO (vy): Methods for manipulating PhysicsEngine tracked entities 16 | 17 | public function addEntity($Entity) { 18 | } 19 | 20 | public function removeEntity($Entity) { 21 | } 22 | 23 | public function update() { 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/TerrainGen/FlatlandGenerator.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | namespace HHVMCraft\Core\TerrainGen; 11 | 12 | use HHVMCraft\API\Coordinates3D; 13 | use HHVMCraft\Core\World\Chunk; 14 | 15 | class FlatlandGenerator { 16 | const LevelType = "FLAT"; 17 | public $spawnpoint; 18 | public $layers = []; 19 | 20 | public function __construct() { 21 | $this->spawnpoint = new Coordinates3D(0, 10, 0); 22 | } 23 | 24 | public function generateChunk($Coordinates2DPos) { 25 | $newC = new Chunk($Coordinates2DPos); 26 | 27 | $y = 0; 28 | // Flatland, dirt from layer 1 - 8, grass on layer 9. 29 | while ($y < 10) { 30 | for ($x = 0; $x < 16; $x++) { 31 | for ($z = 0; $z < 16; $z++) { 32 | if ($y < 9) { 33 | $newC->setBlockID(new Coordinates3D($x, $y, $z), 0x03); 34 | } else { 35 | $newC->setBlockID(new Coordinates3D($x, $y, $z), 0x02); 36 | } 37 | } 38 | } 39 | 40 | $y++; 41 | } 42 | 43 | return $newC; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Windows/ArmorWindowArea.php: -------------------------------------------------------------------------------- 1 | length; $i++) { 19 | if ($this->isValid($slot, $i)) { 20 | if (empty($this->Items[$i])) { 21 | $this->Items[$i] = $slot; 22 | $from->Items[$i] = ItemStack::emptyStack(); 23 | 24 | return $i; 25 | } 26 | } 27 | } 28 | 29 | return -1; 30 | } 31 | 32 | public function isValid($slot, $index) { 33 | if ($slot->isEmpty()) { 34 | return true; 35 | } else { 36 | return parent::isValid($slot, $index); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Windows/CraftingWindowArea.php: -------------------------------------------------------------------------------- 1 | CraftingRepository = $CraftingRepository; 15 | $this->Event->on("WindowChange", function ($index, $value) { 16 | $this->handleWindowChange($index, $value); 17 | }); 18 | } 19 | 20 | public function handleWindowChange($index, $value) { 21 | $current = $CraftingRepository->getRecipe($this->Bench()); 22 | 23 | if ($index == self::craftingOutput) { 24 | if (empty($value) && $current != null) { 25 | $this->removeItemFromOutput($current); 26 | 27 | $current = $CraftingRepository->getRecipe($this->Bench()); 28 | } 29 | } 30 | 31 | if ($current == null) { 32 | $this->Items[self::craftingOutput] = ItemStack::emptyStack(); 33 | } else { 34 | $this->Items[self::craftingOutput] = $current->output(); 35 | } 36 | } 37 | 38 | public function Bench() { 39 | $result = new WindowArea(1, $this->width * $this->height, $this->width, $this->height); 40 | for ($i = 1; $i < $this->Items->length; $i++) { 41 | $result->Items[$i - 1] = $this->Items[$i]; 42 | } 43 | 44 | return $result; 45 | } 46 | 47 | public function removeItemFromOutput($Recipe) { 48 | $x = 0; 49 | $y = 0; 50 | 51 | for ($x = 0; $x < $this->width; $x++) { 52 | $found = false; 53 | for ($y = 0; $y < $this->height; $y++) { 54 | if ($CraftingRepository->testRecipe($this->Bench(), $Recipe, $x, $y)) { 55 | $found = true; 56 | break; 57 | } 58 | } 59 | 60 | if ($found == true) { 61 | break; 62 | } 63 | } 64 | 65 | for ($_x = 0; $_x < $Recipe->Pattern->getLength(1); $_x++) { 66 | for ($_y = 0; $_y < $Recipe->Pattern->getLength(0); $_y++) { 67 | $item = $this->Items[($y + $_y) * $this->width + ($x + $_x) + 1]; 68 | $item->icount -= $Recipe->Pattern->icount; 69 | $this->Items[($y + $_y) * $this->width + ($x + $_x) + 1] = $item; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Windows/InventoryWindow.php: -------------------------------------------------------------------------------- 1 | WindowAreas = [ 20 | new CraftingWindowArea($CraftingRepository, self::craftingOutputIndex), 21 | new ArmorWindowArea(self::armorIndex), 22 | new WindowArea(self::mainIndex, 27, 9, 3), 23 | new WindowArea(self::hotbarIndex, 9, 9, 1) 24 | ]; 25 | } 26 | 27 | public function getLinkedArea($index, $slot) { 28 | if ($index == 0 || $index == 1 || $index == 3) { 29 | return $this->WindowAreas[2]; 30 | } 31 | else { 32 | return $this->WindowAreas[3]; 33 | } 34 | } 35 | 36 | // TODO (vy) 37 | public function pickUpStack() { 38 | 39 | } 40 | 41 | // TODO (vy) 42 | public function copyToInventory() { 43 | 44 | } 45 | 46 | public function findEmptySpace() { 47 | // Go through window area and find an empty slot 48 | $slot_index = -1; 49 | 50 | $window_areas = [ 51 | $this->WindowAreas[3], 52 | $this->WindowAreas[2] 53 | ]; 54 | 55 | foreach ($window_areas as $Area) { 56 | for ($i = 0; $i < $Area->length; $i++) { 57 | if ($slot_index > -1) { 58 | break; 59 | } 60 | 61 | $index = $Area->startIndex + $i; 62 | $item = $Area->Items[$i]; 63 | 64 | if ($item->isEmpty()) { 65 | $slot_index = $index; 66 | break; 67 | } else if ($item->icount >= 64) { 68 | continue; 69 | } 70 | } 71 | } 72 | 73 | return $slot_index; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Windows/Window.php: -------------------------------------------------------------------------------- 1 | Event = new EventEmitter(); 16 | } 17 | 18 | public function moveToAlternateArea($index) { 19 | $fromIndex = $this->getAreaIndex($index); 20 | $from = $this->getArea($index); 21 | $slot = $this->from[$index]; 22 | 23 | if ($slot == null) { 24 | return; 25 | } 26 | 27 | $to = $this->getLinkedArea($fromIndex, $slot); 28 | $destination = $to->moveOrMergeItem($index, $slot, $from); 29 | $this->windowChange($destination + $to->startIndex, $slot); 30 | } 31 | 32 | public function getAreaIndex($index) { 33 | for ($i = 0; $i < count($this->WindowAreas); $i++) { 34 | $Area = $this->WindowAreas[$i]; 35 | if ($index >= $Area->startIndex && $index < $Area->startIndex + $Area->length) { 36 | return $i; 37 | } 38 | } 39 | } 40 | 41 | // Get the window area responsible for this index, and modify index accordingly. 42 | 43 | public function getArea(&$index) { 44 | foreach ($WindowAreas as $Area) { 45 | if ($Area->startIndex <= $index && $Area->startIndex + $Area->length > $index) { 46 | $index = $index - $Area->startIndex; 47 | return $Area; 48 | } 49 | } 50 | } 51 | 52 | // Gets window area index from index 53 | 54 | public function windowChange() { 55 | $this->Event->emit("WindowChange", (func_get_args())); 56 | } 57 | 58 | public function length() { 59 | $this->windowAreaLength(); 60 | } 61 | 62 | public function windowAreaLength() { 63 | $l = 0; 64 | foreach ($this->WindowAreas as $Area) { 65 | $l = $l + $Area->length; 66 | } 67 | 68 | return $l; 69 | } 70 | 71 | public function isEmpty() { 72 | $hasStuff = false; 73 | 74 | foreach ($WindowAreas as $Area) { 75 | foreach ($Area->Items as $Item) { 76 | if (!empty($Item)) { 77 | $hasStuff = true; 78 | } 79 | } 80 | } 81 | 82 | return !$hasStuff; 83 | } 84 | 85 | public function getSlots() { 86 | $l = $this->windowAreaLength(); 87 | $slots = array_fill(0, $l, 0); 88 | 89 | for ($i = 0; $i < $l; $i++) { 90 | $slots[$i] = ItemStack::emptyStack(); 91 | } 92 | 93 | foreach ($this->WindowAreas as $Area) { 94 | for ($i = 0; $i < $Area->length; $i++) { 95 | $index = $Area->startIndex + $i; 96 | $slots[$index] = $Area->Items[$i]; 97 | } 98 | } 99 | 100 | return $slots; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/Windows/WindowArea.php: -------------------------------------------------------------------------------- 1 | Event = new EventEmitter(); 18 | $this->startIndex = $startIndex; 19 | $this->length = $length; 20 | $this->width = $width; 21 | $this->height = $height; 22 | 23 | $this->setItems($length); 24 | } 25 | 26 | public function setItems($length) { 27 | $this->Items = array_fill(0, $length, 0); 28 | 29 | for ($i = 0; $i < $length; $i++) { 30 | $this->Items[$i] = ItemStack::emptyStack(); 31 | } 32 | } 33 | 34 | public function moveOrMergeItem($index, $item, $from) { 35 | $emptyIndex = -1; 36 | 37 | // TODO (vy): Should grab the item's const max stack size. 38 | $maxStack = 64; 39 | 40 | for ($i = 0; $i < $this->length; $i++) { 41 | if ($this->Items[$i]->isEmpty() && $emptyIndex == -1) { 42 | $emptyIndex == $i; 43 | } else if ($this->Items[$i]->id == $item->id && 44 | $this->Items[$i]->metadata == $item->metadata && 45 | $this->Items[$i]->icount < $maxStack 46 | ) { 47 | $emptyIndex = -1; 48 | 49 | if ($from != null) { 50 | $from->Items[$index] = ItemStack::emptyStack(); 51 | } 52 | 53 | // If mergine two stacks becomes more than max stack, we create one with max size 54 | // and create one with the remainder. 55 | 56 | if ($this->Items[$i]->icount + $item->icount > $maxStack) { 57 | $item = new ItemStack($item->id, 58 | ($this->icount - ($maxStack - $this->Items[$i]->icount)), 59 | $this->metadata, 60 | $this->nbt); 61 | 62 | $this->Items[$i] = new ItemStack($item->id, $maxStack); 63 | } 64 | 65 | $this->Items[$i] = new ItemStack($item->id, ($this->Items[$i]->icount + $item->icount), $item->metadata); 66 | 67 | return $i; 68 | } 69 | } 70 | } 71 | 72 | public function copyTo($Area) { 73 | for ($i = 0; $i < $Area->length; $i++) { 74 | $Area->Items[$i] = $this->Items[$i]; 75 | } 76 | } 77 | 78 | public function windowChange() { 79 | $this->Event->emit("WindowChange", (func_get_args())); 80 | } 81 | 82 | public function isValid($slot, $index) { 83 | // TODO (vy): Actually check if the slot is valid? 84 | return true; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/World/Chunk.php: -------------------------------------------------------------------------------- 1 | x = $Coordinates2D->x * self::Width; 28 | $this->z = $Coordinates2D->z * self::Depth; 29 | 30 | $this->Blocks = array_fill(0, self::Size, 0x00); 31 | $this->Metadata = array_fill(0, self::Size, 0x00); 32 | $this->BlockLight = array_fill(0, self::Size, 0x00); 33 | $this->HeightMap = array_fill(0, self::Size, 0x00); 34 | $this->SkyLight = array_fill(0, self::Size, 0xFF); 35 | } 36 | 37 | public function Coordinates() { 38 | return new Coordinates2D($this->x, $this->z); 39 | } 40 | 41 | public function setCoordinates($Coordinates2D) { 42 | $this->x = $Coordinates2D->x; 43 | $this->z = $Coordinates2D->z; 44 | } 45 | 46 | public function getMetadata($Coordinates3D) { 47 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 48 | $index = $Coordinates3D->y + ($Coordinates3D->z * self::Height) + ($Coordinates3D->x * self::Height * self::Width); 49 | 50 | return $this->Metadata[$index]; 51 | } 52 | 53 | public function getSkyLight($Coordinates3D) { 54 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 55 | $index = $Coordinates3D->y + ($Coordinates3D->z * self::Height) + ($Coordinates3D->x * self::Height * self::Width); 56 | 57 | return $this->SkyLight[$index]; 58 | } 59 | 60 | public function getBlockLight($Coordinates3D) { 61 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 62 | $index = $Coordinates3D->y + ($Coordinates3D->z * self::Height) + ($Coordinates3D->x * self::Height * self::Width); 63 | 64 | return $this->getBlockLight[$index]; 65 | } 66 | 67 | public function setBlockID($Coordinates3D, $val) { 68 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 69 | $this->isModified = true; 70 | $index = $Coordinates3D->y + ($Coordinates3D->z * self::Height) + ($Coordinates3D->x * self::Height * self::Width); 71 | $this->Blocks[$index] = $val; 72 | 73 | $oldHeight = $this->getHeight($Coordinates3D->x, $Coordinates3D->z); 74 | if ($val == 0x00) { 75 | if ($oldHeight < $Coordinates3D->y) { 76 | while ($Coordinates3D->y > 0) { 77 | $Coordinates3D->y--; 78 | if ($this->getBlockID($Coordinates3D) != 0x00) { 79 | $this->setHeight($Coordinates3D->x, $Coordinates3D->z, $Coordinates3D->y); 80 | } 81 | } 82 | } 83 | } else if ($oldHeight < $Coordinates3D->y) { 84 | $this->setHeight($Coordinates3D->x, $Coordinates3D->z, $Coordinates3D->y); 85 | } 86 | } 87 | 88 | 89 | public function getHeight($x, $z) { 90 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 91 | 92 | return $this->HeightMap[$z * self::Depth + $x]; 93 | } 94 | 95 | public function getBlockID($Coordinates3D) { 96 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 97 | $index = $Coordinates3D->y + ($Coordinates3D->z * self::Height) + ($Coordinates3D->x * self::Height * self::Width); 98 | 99 | return $this->Blocks[$index]; 100 | } 101 | 102 | public function setHeight($x, $z, $val) { 103 | $this->lastAccessed = new \DateTime(null, new \DateTimeZone('Pacific/Nauru')); 104 | $this->isModified = true; 105 | $this->HeightMap[$z * self::Depth + $x] = $val; 106 | } 107 | 108 | public function toNbt() { 109 | } 110 | 111 | public function fromNbt($NbtFile) { 112 | } 113 | 114 | public function nbtSerialize($TagName) { 115 | } 116 | 117 | public function nbtDeserialize($val) { 118 | } 119 | 120 | public function deserialize() { 121 | $deserialized = ""; 122 | 123 | # TODO (vy): the zlib turns these hex into ascii form of hex 124 | # so you will probably have to do decbin or some crazy stuff 125 | 126 | try { 127 | for ($i = 0; $i < self::Size; $i++) { 128 | $deserialized .= chr($this->Blocks[$i]); 129 | } 130 | for ($i = 0; $i < self::Size; $i++) { 131 | $deserialized .= chr(0x00); 132 | } 133 | for ($i = 0; $i < self::Size; $i++) { 134 | $deserialized .= chr(0xFF); 135 | } 136 | for ($i = 0; $i < self::Size; $i++) { 137 | $deserialized .= chr(0xFF); 138 | } 139 | } catch (Exception $e) { 140 | echo 'Caught exception: ', $e->getMessage(), "\n"; 141 | } 142 | 143 | return $deserialized; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/World/NBTProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * @version 1.0 8 | * 9 | * Dependencies: 10 | * PHP 4.3+ (5.3+ recommended) 11 | * GMP Extension 12 | */ 13 | 14 | namespace HHVMCraft\Core\World; 15 | 16 | class NBT { 17 | const TAG_END = 0; 18 | const TAG_BYTE = 1; 19 | const TAG_SHORT = 2; 20 | const TAG_INT = 3; 21 | const TAG_LONG = 4; 22 | const TAG_FLOAT = 5; 23 | const TAG_DOUBLE = 6; 24 | const TAG_BYTE_ARRAY = 7; 25 | const TAG_STRING = 8; 26 | const TAG_LIST = 9; 27 | const TAG_COMPOUND = 10; 28 | public $root = array(); 29 | public $verbose = false; 30 | 31 | public function loadFile($filename, $wrapper = "compress.zlib://") { 32 | if (is_string($wrapper) && is_file($filename)) { 33 | if ($this->verbose) trigger_error("Loading file \"{$filename}\" with stream wrapper \"{$wrapper}\".", 34 | E_USER_NOTICE); 35 | $fp = fopen("{$wrapper}{$filename}", "rb"); 36 | } 37 | elseif (is_null($wrapper) && is_resource($filename)) { 38 | if ($this->verbose) trigger_error("Loading file from existing resource.", E_USER_NOTICE); 39 | $fp = $filename; 40 | } 41 | else { 42 | trigger_error("First parameter must be a filename or a resource.", E_USER_WARNING); 43 | 44 | return false; 45 | } 46 | if ($this->verbose) trigger_error("Traversing first tag in file.", E_USER_NOTICE); 47 | $this->traverseTag($fp, $this->root); 48 | if ($this->verbose) trigger_error("Encountered end tag for first tag; finished.", E_USER_NOTICE); 49 | 50 | return end($this->root); 51 | } 52 | 53 | public function traverseTag($fp, &$tree) { 54 | if (feof($fp)) { 55 | if ($this->verbose) trigger_error("Reached end of file/resource.", E_USER_NOTICE); 56 | 57 | return false; 58 | } 59 | $tagType = $this->readType($fp, self::TAG_BYTE); // Read type byte. 60 | if ($tagType == self::TAG_END) { 61 | return false; 62 | } 63 | else { 64 | if ($this->verbose) $position = ftell($fp); 65 | $tagName = $this->readType($fp, self::TAG_STRING); 66 | if ($this->verbose) trigger_error("Reading tag \"{$tagName}\" at offset {$position}.", E_USER_NOTICE); 67 | $tagData = $this->readType($fp, $tagType); 68 | $tree[] = array("type" => $tagType, "name" => $tagName, "value" => $tagData); 69 | 70 | return true; 71 | } 72 | } 73 | 74 | public function readType($fp, $tagType) { 75 | switch ($tagType) { 76 | case self::TAG_BYTE: // Signed byte (8 bit) 77 | list(, $unpacked) = unpack("c", fread($fp, 1)); 78 | 79 | return $unpacked; 80 | case self::TAG_SHORT: // Signed short (16 bit, big endian) 81 | list(, $unpacked) = unpack("n", fread($fp, 2)); 82 | if ($unpacked >= pow(2, 15)) $unpacked -= pow(2, 16); // Convert unsigned short to signed short. 83 | return $unpacked; 84 | case self::TAG_INT: // Signed integer (32 bit, big endian) 85 | list(, $unpacked) = unpack("N", fread($fp, 4)); 86 | if ($unpacked >= pow(2, 31)) $unpacked -= pow(2, 32); // Convert unsigned int to signed int 87 | return $unpacked; 88 | case self::TAG_LONG: // Signed long (64 bit, big endian) 89 | extension_loaded("gmp") or trigger_error( 90 | "This file contains a 64-bit number and execution cannot continue. " . 91 | "Please install the GMP extension for 64-bit number handling.", E_USER_ERROR 92 | ); 93 | list(, $firstHalf) = unpack("N", fread($fp, 4)); 94 | list(, $secondHalf) = unpack("N", fread($fp, 4)); 95 | $value = gmp_add($secondHalf, gmp_mul($firstHalf, "4294967296")); 96 | if (gmp_cmp($value, gmp_pow(2, 63)) >= 0) $value = gmp_sub($value, gmp_pow(2, 64)); 97 | 98 | return gmp_strval($value); 99 | case self::TAG_FLOAT: // Floating point value (32 bit, big endian, IEEE 754-2008) 100 | list(, $value) = (pack('d', 1) == "\77\360\0\0\0\0\0\0") ? unpack('f', fread($fp, 4)) : unpack('f', 101 | strrev(fread($fp, 4))); 102 | 103 | return $value; 104 | case self::TAG_DOUBLE: // Double value (64 bit, big endian, IEEE 754-2008) 105 | list(, $value) = (pack('d', 1) == "\77\360\0\0\0\0\0\0") ? unpack('d', fread($fp, 8)) : unpack('d', 106 | strrev(fread($fp, 8))); 107 | 108 | return $value; 109 | case self::TAG_BYTE_ARRAY: // Byte array 110 | $arrayLength = $this->readType($fp, self::TAG_INT); 111 | $array = array(); 112 | for ($i = 0; $i < $arrayLength; $i++) $array[] = $this->readType($fp, self::TAG_BYTE); 113 | 114 | return $array; 115 | case self::TAG_STRING: // String 116 | if (!$stringLength = $this->readType($fp, self::TAG_SHORT)) return ""; 117 | $string = utf8_decode(fread($fp, 118 | $stringLength)); // Read in number of bytes specified by string length, and decode from utf8. 119 | return $string; 120 | case self::TAG_LIST: // List 121 | $tagID = $this->readType($fp, self::TAG_BYTE); 122 | $listLength = $this->readType($fp, self::TAG_INT); 123 | if ($this->verbose) trigger_error("Reading in list of {$listLength} tags of type {$tagID}.", E_USER_NOTICE); 124 | $list = array("type" => $tagID, "value" => array()); 125 | for ($i = 0; $i < $listLength; $i++) { 126 | if (feof($fp)) break; 127 | $list["value"][] = $this->readType($fp, $tagID); 128 | } 129 | 130 | return $list; 131 | case self::TAG_COMPOUND: // Compound 132 | $tree = array(); 133 | while ($this->traverseTag($fp, $tree)) ; 134 | 135 | return $tree; 136 | } 137 | } 138 | 139 | public function writeFile($filename, $wrapper = "compress.zlib://") { 140 | if (is_string($wrapper)) { 141 | if ($this->verbose) trigger_error("Writing file \"{$filename}\" with stream wrapper \"{$wrapper}\".", 142 | E_USER_NOTICE); 143 | $fp = fopen("{$wrapper}{$filename}", "wb"); 144 | } 145 | elseif (is_null($wrapper) && is_resource($fp)) { 146 | if ($this->verbose) trigger_error("Writing file to existing resource.", E_USER_NOTICE); 147 | $fp = $filename; 148 | } 149 | else { 150 | trigger_error("First parameter must be a filename or a resource.", E_USER_WARNING); 151 | 152 | return false; 153 | } 154 | if ($this->verbose) trigger_error("Writing " . count($this->root) . " root tag(s) to file/resource.", 155 | E_USER_NOTICE); 156 | foreach ($this->root as $rootNum => $rootTag) if (!$this->writeTag($fp, $rootTag) 157 | ) trigger_error("Failed to write root tag #{$rootNum} to file/resource.", E_USER_WARNING); 158 | 159 | return true; 160 | } 161 | 162 | public function writeTag($fp, $tag) { 163 | if ($this->verbose) { 164 | $position = ftell($fp); 165 | trigger_error("Writing tag \"{$tag["name"]}\" of type {$tag["type"]} at offset {$position}.", E_USER_NOTICE); 166 | } 167 | 168 | return $this->writeType($fp, self::TAG_BYTE, $tag["type"]) && $this->writeType($fp, self::TAG_STRING, 169 | $tag["name"]) && $this->writeType($fp, $tag["type"], $tag["value"]); 170 | } 171 | 172 | public function writeType($fp, $tagType, $value) { 173 | switch ($tagType) { 174 | case self::TAG_BYTE: // Signed byte (8 bit) 175 | return is_int(fwrite($fp, pack("c", $value))); 176 | case self::TAG_SHORT: // Signed short (16 bit, big endian) 177 | if ($value < 0) $value += pow(2, 16); // Convert signed short to unsigned short 178 | return is_int(fwrite($fp, pack("n", $value))); 179 | case self::TAG_INT: // Signed integer (32 bit, big endian) 180 | if ($value < 0) $value += pow(2, 32); // Convert signed int to unsigned int 181 | return is_int(fwrite($fp, pack("N", $value))); 182 | case self::TAG_LONG: // Signed long (64 bit, big endian) 183 | extension_loaded("gmp") or trigger_error( 184 | "This file contains a 64-bit number and execution cannot continue. " . 185 | "Please install the GMP extension for 64-bit number handling.", E_USER_ERROR 186 | ); 187 | $secondHalf = gmp_mod($value, 2147483647); 188 | $firstHalf = gmp_sub($value, $secondHalf); 189 | 190 | return is_int(fwrite($fp, pack("N", gmp_intval($firstHalf)))) && is_int(fwrite($fp, 191 | pack("N", gmp_intval($secondHalf)))); 192 | case self::TAG_FLOAT: // Floating point value (32 bit, big endian, IEEE 754-2008) 193 | return is_int(fwrite($fp, 194 | (pack('d', 1) == "\77\360\0\0\0\0\0\0") ? pack('f', $value) : strrev(pack('f', $value)))); 195 | case self::TAG_DOUBLE: // Double value (64 bit, big endian, IEEE 754-2008) 196 | return is_int(fwrite($fp, 197 | (pack('d', 1) == "\77\360\0\0\0\0\0\0") ? pack('d', $value) : strrev(pack('d', $value)))); 198 | case self::TAG_BYTE_ARRAY: // Byte array 199 | return $this->writeType($fp, self::TAG_INT, count($value)) && is_int(fwrite($fp, 200 | call_user_func_array("pack", array_merge(array("c" . count($value)), $value)))); 201 | case self::TAG_STRING: // String 202 | $value = utf8_encode($value); 203 | 204 | return $this->writeType($fp, self::TAG_SHORT, strlen($value)) && is_int(fwrite($fp, $value)); 205 | case self::TAG_LIST: // List 206 | if ($this->verbose) trigger_error("Writing list of " . count($value["value"]) . " tags of type {$value["type"]}.", 207 | E_USER_NOTICE); 208 | if (!($this->writeType($fp, self::TAG_BYTE, $value["type"]) && $this->writeType($fp, self::TAG_INT, 209 | count($value["value"]))) 210 | ) return false; 211 | foreach ($value["value"] as $listItem) if (!$this->writeType($fp, $value["type"], $listItem)) return false; 212 | 213 | return true; 214 | case self::TAG_COMPOUND: // Compound 215 | foreach ($value as $listItem) if (!$this->writeTag($fp, $listItem)) return false; 216 | if (!is_int(fwrite($fp, "\0"))) return false; 217 | 218 | return true; 219 | } 220 | } 221 | 222 | public function purge() { 223 | if ($this->verbose) trigger_error("Purging all loaded data", E_USER_ERROR); 224 | $this->root = array(); 225 | } 226 | } 227 | 228 | ?> 229 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/World/Region.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | namespace HHVMCraft\Core\World; 11 | 12 | 13 | use HHVMCraft\API\Coordinates2D; 14 | 15 | class Region { 16 | const Width = 32; 17 | const Depth = 32; 18 | 19 | public $x; 20 | public $z; 21 | public $World; 22 | public $Chunks = []; 23 | 24 | public function __construct($Coordinates2D, $World) { 25 | $this->x = $Coordinates2D->x; 26 | $this->z = $Coordinates2D->z; 27 | $this->World =& $World; 28 | } 29 | 30 | public function getChunk($Coordinates2D) { 31 | // Get chunk from memory/disk, or generate a chunk if not loaded. 32 | } 33 | 34 | public function generateChunk($Coordinates2D) { 35 | // Generates chunk from chunk provider 36 | $Global2DCoordinates = new Coordinates2D(($this->x * $this::Width) + $Coordinates2D->x, 37 | ($this->z * $this::Depth) + $Coordinates2D->z); 38 | $Chunk = $this->World->generateChunk($Global2DCoordinates); 39 | $Chunk->isModified = true; 40 | $Chunk->setCoordinates($Global2DCoordinates); 41 | $Chunks[$Global2DCoordinates] = $Chunk; 42 | } 43 | 44 | public function setChunk($Coordinates2D) { 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/HHVMCraft/Core/World/World.php: -------------------------------------------------------------------------------- 1 | worldname = $worldname; 20 | $this->BlockProvider = $BlockProvider; 21 | $this->ChunkProvider = new FlatlandGenerator(); 22 | } 23 | 24 | public function getTime() { 25 | // World Time in Minecraft 26 | // 20 server ticks per second 27 | // 24000 ticks per day = 20 minutes a day 28 | // 0 is sunrise, 6000 is noon, 12000 is sunset, 18000 is midnight 29 | return $this->WorldTime; 30 | } 31 | 32 | public function updateTime() { 33 | // Every second, increase worldtime by 20 ticks. 34 | // if ($this->WorldTime == 24000) { 35 | // $this->WorldTime = 0; 36 | // } else { 37 | // $this->WorldTime += 20; 38 | // } 39 | } 40 | 41 | public function getChunk($Coordinates2D) { 42 | if (array_key_exists($Coordinates2D->toString(), $this->Chunks)) { 43 | return $this->Chunks[$Coordinates2D->toString()]; 44 | } else { 45 | return null; 46 | } 47 | } 48 | 49 | public function setChunk($Coordinates2D, $Chunk) { 50 | $this->Chunks[$Coordinates2D->toString()] = $Chunk; 51 | 52 | return $Chunk; 53 | } 54 | 55 | // For quick purposes, let's just generate a 0,0 chunk. 56 | public function getFakeChunk() { 57 | $Coordinates2D = new Coordinates2D(0, 0); 58 | 59 | return $this->ChunkProvider->generateChunk($Coordinates2D); 60 | } 61 | 62 | public function generateChunk($Coordinates2D) { 63 | $chunk = $this->getChunk($Coordinates2D); 64 | 65 | if ($chunk) { 66 | return $chunk; 67 | } else { 68 | $new_chunk = $this->ChunkProvider->generateChunk($Coordinates2D); 69 | return $this->setChunk($Coordinates2D, $new_chunk); 70 | } 71 | } 72 | 73 | public function setBlockId($Coordinates3D, $id) { 74 | $chunkX = $Coordinates3D->x >> 4; 75 | $chunkZ = $Coordinates3D->z >> 4; 76 | 77 | $normalizedBlockCoordinates = new Coordinates3D($Coordinates3D->x % 16, $Coordinates3D->y % 128, $Coordinates3D->z % 16); 78 | $coordinates = new Coordinates2D($chunkX, $chunkZ); 79 | 80 | $chunk = $this->generateChunk($coordinates); 81 | $chunk->setBlockID($normalizedBlockCoordinates, $id); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /tests/Core/Networking/ClientTest.php: -------------------------------------------------------------------------------- 1 | generateChunk($chunk1pos); 14 | 15 | $c = new Coordinates3D(0,20,0); 16 | $blockid = $chunk->getBlockID($c); 17 | $this->assertEquals($blockid, 0x00, "Expects block to be air at y-level 20"); 18 | 19 | $c = new Coordinates3D(0,0,0); 20 | $blockid = $chunk->getBlockID($c); 21 | $this->assertEquals($blockid, 0x03, "Expects block to be dirt at y-level 0"); 22 | 23 | 24 | $c = new Coordinates3D(0,9,0); 25 | $blockid = $chunk->getBlockID($c); 26 | $this->assertEquals($blockid, 0x02, "Expects block to be grass at y-level 9"); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /tests/Core/World/ChunkTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($chunk->getBlockID($blockCoordinates), 0x00, "Expects block to be air in the chunk"); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /tests/Core/World/WorldTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($world->worldname, "testworld", "Expects world name to be testworld"); 18 | 19 | $chunk = $world->generateChunk($chunkCoordinates); 20 | 21 | $this->assertEquals($chunk->getBlockID($blockCoordinates), 0x00, "Expects block to be air in the chunk"); 22 | } 23 | } 24 | --------------------------------------------------------------------------------