Compare commits

..

133 Commits

Author SHA1 Message Date
c23081e713 PEAR compile fix 2013-01-12 18:44:49 +01:00
2ba2116498 Alpha_1.0.6!! 2013-01-12 18:10:58 +01:00
07ac028558 Fixed a mini memory leak 2013-01-12 18:06:31 +01:00
7e4692aaf1 Correct block drop 2013-01-12 17:43:48 +01:00
baec22208f Updated event names 2013-01-12 17:35:32 +01:00
62a7461bbd Fixed #53 correct door hinge placing for double doors 2013-01-12 17:30:26 +01:00
4df7aed15d Fixed some events 2013-01-12 17:15:59 +01:00
d733eb6b4a Added more Events and Handlers 2013-01-12 17:01:20 +01:00
e4d71949e3 Fixed #39 placing blocks inside an entity 2013-01-12 16:47:12 +01:00
4bda101ab8 Added Entity speed, removed falling damage 2013-01-12 16:03:42 +01:00
b74c089ce7 Fixed #24 event duplication
Found 2 PHP bugs with this Commit
2013-01-12 14:22:57 +01:00
02950474af Improved Chat API 2013-01-12 12:25:27 +01:00
b398b9daa2 Deleted documentation 2013-01-12 11:01:34 +01:00
b2731261e5 Merge pull request #51 from sekjun9878/master
Added chat() function in ChatAPI. Also PlayerAPI Documentation
2013-01-12 02:00:22 -08:00
03c8c91b46 Changed info 2013-01-12 10:07:17 +01:00
13f4f3285f Re-fixed #45 and other bugs 2013-01-12 10:05:27 +01:00
8bc115a2ab Remove XML dependency 2013-01-12 00:40:54 +01:00
2665dffdd8 Show External IP 2013-01-11 15:08:02 +01:00
ab2b519c2e Entity despawning memory leak fixed #45 2013-01-11 15:06:24 +01:00
6ab08020c3 Online players broadcast when >= 2 players. fixes #48 2013-01-11 14:19:02 +01:00
107d71f291 Updated TEST_MD5 2013-01-11 14:16:12 +01:00
09c2c1b2ee Changed chat to send. Documentation included 2013-01-11 21:34:21 +09:00
f3e9d4df31 New Documentation for PlayerAPI - Alpha 2013-01-11 11:56:34 +09:00
d86bf37def Added and Fixed chat function in chat API. Tested 2013-01-11 11:25:42 +09:00
5753db627c Added chat API to ChatAPI.php 2013-01-11 11:09:57 +09:00
6102efc809 Fixed Memory Leak 2013-01-11 00:20:13 +01:00
8d3ad0c5ec Changing code parts [Hey! Don't look here!] 2013-01-10 23:59:03 +01:00
eebbc263b0 TickLoop thread 2013-01-10 17:10:15 +01:00
d5d319fb7c Fixed function name 2013-01-09 23:21:37 +01:00
03db7c8738 Compile script without Root access 2013-01-09 20:29:42 +01:00
2801f18753 Fixed entity despawning 2013-01-09 18:05:25 +01:00
b9b0039cd7 More events 2013-01-09 18:05:12 +01:00
cd3aaa1b50 New compile script 2013-01-09 14:44:33 +01:00
902cee1107 Debug event 2013-01-09 14:44:25 +01:00
ab5481a392 UPnP class error fix 2013-01-08 20:55:39 +01:00
f11bc82b86 removed PocketMinecraftServer stdClass 2013-01-08 16:38:35 +01:00
0cd8479026 Fixed #38 Debian install script 2013-01-08 16:38:20 +01:00
90d9099b3c Added Iron Doors 2013-01-07 16:02:23 +01:00
47e80151e6 Removed info 2013-01-07 11:06:39 +01:00
19aa4bd527 Allow console parameters 2013-01-07 01:58:15 +01:00
a6ddba8d97 Fixed "forwading" typo 2013-01-07 00:48:06 +01:00
3a110bf8b6 Error dump 2013-01-06 22:51:17 +01:00
18bab3b045 Fixed placing blocks on TNT 2013-01-06 21:25:09 +01:00
89c0702a47 Added optional Windows UPnP port forwading 2013-01-06 21:19:39 +01:00
21594d699d Fake explosions! 2013-01-06 17:31:28 +01:00
b1b16bc366 Alpha 1.0.5 2013-01-06 14:05:57 +01:00
aa1a39adfc Fixed TABS 2013-01-06 13:38:28 +01:00
b3b38605b5 Multi-update system and version numeration 2013-01-06 13:31:30 +01:00
7030f9118d Fixes #31 Server crash in health regeneration 2013-01-06 02:28:58 +01:00
b150d4e001 Fixes #30 Server crash when a player dies 2013-01-06 01:53:13 +01:00
125f1c11b4 Added Furnace 2013-01-06 01:16:49 +01:00
82f2a0f2f8 Fixed creative crash when opening chests 2013-01-06 00:58:46 +01:00
700314d75a Starting to support Chest windows 2013-01-05 22:13:10 +01:00
54e6bd0ee9 Fixed death in Creative mode 2013-01-05 20:35:21 +01:00
fd1186061f Updated entity despawning 2013-01-05 19:39:12 +01:00
f03f376535 Fall damage 2013-01-05 19:37:01 +01:00
586f49994a Fixed entity despawn 2013-01-05 18:33:39 +01:00
c1aec49ad1 Changes in Entity events 2013-01-05 18:30:29 +01:00
683c05f206 Replace player spaces & handle more data packets 2013-01-05 17:13:12 +01:00
9ebe68294b Item Drop 2013-01-05 15:17:57 +01:00
c2a45212dd Fixed item spawning 2013-01-05 15:01:21 +01:00
344449fa12 Removed Raknet version & added Current MC version 2013-01-05 13:39:37 +01:00
732ee755e8 Updated to Alpha_1.0.4 2013-01-05 11:37:57 +01:00
f0393f9b93 /give command 2013-01-05 11:34:08 +01:00
f23c2f894d Merge pull request #27 from C0deH4cker/patch-1
Fixed typo [HUGE]
2013-01-05 01:32:41 -08:00
85dfb651bd Merge pull request #29 from C0deH4cker/patch-2
Added pause at the end
2013-01-05 01:31:09 -08:00
b5e6130214 Added pause at the end
Added pause so users can see any errors that occur instead of the command prompt window just closing.
2013-01-05 02:01:05 -05:00
957f24c1f3 Fixed typo 2013-01-04 23:08:39 -05:00
518afeae48 Item pickup 2013-01-05 03:16:11 +01:00
57282225d2 Fixed Sapling, flower placing 2013-01-05 02:40:29 +01:00
369f268b90 Improved compression 2013-01-04 21:47:36 +01:00
ab0abfab1c Alpha_1.0.3 2013-01-04 19:42:26 +01:00
a64f0c8bbe Entities despawning, half item pickup 2013-01-04 19:36:51 +01:00
8d9f9ed9ed Fixed item drop megaduplication 2013-01-04 19:02:54 +01:00
38eed99789 Fixed issues and sockets 2013-01-04 16:04:06 +01:00
284daff67f Removed quiet param 2013-01-04 15:53:52 +01:00
a1e6184c74 Updated ;) with removed one 2013-01-04 15:49:20 +01:00
a495e8f436 Added install scripts for linux 2013-01-04 15:46:29 +01:00
3e9afbdf54 Added world compression 2013-01-04 12:51:33 +01:00
3ba8662c2c NormalGenerator class 2013-01-04 11:29:08 +01:00
52b4220ade Fix for block updates 2013-01-04 01:32:06 +01:00
b9eca491e8 Updated triggers to handle or dhandle API method 2013-01-04 01:20:29 +01:00
d7b1ae4b47 Vectors 2013-01-03 23:39:57 +01:00
9b09ff4042 Added 0x03 unknown packet 2013-01-03 12:20:28 +01:00
f9c71f1800 Fixed unknown data folders 2013-01-03 12:07:08 +01:00
d71167f66c Added generator name to level.dat 2013-01-03 00:57:32 +01:00
4b641bba39 Changed base broadcasts frequency 2013-01-02 22:48:50 +01:00
00379bb2d1 Added command alias 2013-01-02 22:48:37 +01:00
b96070a53a Added PHP >= 5.5 notice 2013-01-02 19:44:52 +01:00
db1b7ec4f2 Line ending cleanup 2013-01-02 19:41:23 +01:00
aafd36859f Solve segfault killing the process 2013-01-02 19:10:05 +01:00
0bea6e8051 Detect Mac OS 2013-01-02 19:09:28 +01:00
96bac5aaec Added player hand distance check 2013-01-02 18:25:39 +01:00
a501020198 Fix for .DS_Store folders in Mac 2013-01-02 17:33:24 +01:00
19436d4953 Removed protocol Changing 2013-01-02 01:09:54 +01:00
6c8e283599 Function names 2012-12-30 20:49:04 +01:00
2b5c21d909 Updated deleted header 2012-12-30 19:55:52 +01:00
2370ca6fd4 Autoloading non-included extensions 2012-12-29 19:54:05 +01:00
04c3229835 Removed GC Collector 2012-12-29 19:12:38 +01:00
9cd5a0c030 Line endings ... 2012-12-29 19:10:55 +01:00
302c865f4e Use Console Thread for Async I/O 2012-12-29 19:10:04 +01:00
b3cc13d4f2 Async class 2012-12-29 18:06:10 +01:00
c922b073b5 Moved ToDo list to the Wiki 2012-12-29 16:21:03 +01:00
f1956464b0 Final merging changes 2012-12-29 16:14:59 +01:00
aa7d6b12b5 BlockIterator 2012-12-29 16:13:59 +01:00
9ea1a05536 More different trees, check grow space 2012-12-29 16:13:58 +01:00
29a5041ef1 Fix for placing blocks where no block exists 2012-12-29 16:13:57 +01:00
000ba5de52 Fix 2012-12-29 16:13:56 +01:00
637d1c313b Changed version to next release 2012-12-29 16:13:52 +01:00
3e392820f1 Readded pthreads dependency 2012-12-29 16:13:03 +01:00
3ab149cf58 Fix for not-placing action blocks 2012-12-29 13:58:33 +01:00
473f5ec01e Updated fopen error 2012-12-29 12:37:47 +01:00
cacf14df81 Nasty line endings :S 2012-12-29 03:27:40 +01:00
e20251eaf4 Fixed Linux bug 2012-12-29 03:23:02 +01:00
000a0dedd9 Alpha 1.0.2
Changed structure
2012-12-29 02:52:12 +01:00
5df48f3b7c Merge remote-tracking branch 'origin/threading' 2012-12-29 02:31:48 +01:00
4c34cdaa61 Removed pthreads dependency for merge 2012-12-29 02:31:19 +01:00
c959e14718 Cancel unallowed break/place events 2012-12-29 02:30:20 +01:00
470352cd31 Bedrock *again* 2012-12-29 02:27:51 +01:00
ca54acd09b Removed example folder (added those examples to the wiki) 2012-12-29 02:19:54 +01:00
8a0c5e4655 Scheduled update for entities 2012-12-28 16:33:16 +01:00
b5c191530a Updated folders 2012-12-28 15:27:49 +01:00
8abc36acd4 Changed files 2012-12-28 15:12:46 +01:00
0b683c6b0e Updated directory structure 2012-12-28 15:09:35 +01:00
0c498e96a3 Removed bedrock 2012-12-28 14:44:56 +01:00
2034dbfb15 Removed bedrock from unbreakable blocks 2012-12-28 14:43:29 +01:00
3409f873be Updated README.md 2012-12-28 04:59:45 +01:00
aefa34187b Updated README.md 2012-12-28 02:09:43 +01:00
2d9b701847 pthreads dependency 2012-12-28 01:02:22 +01:00
78c2a06623 Updated objectives 2012-12-28 00:23:41 +01:00
4d3ef98f1f Updated TODO list 2012-12-28 00:08:20 +01:00
ca12a5c2ce Changed message 2012-12-27 23:57:14 +01:00
ada2662627 Fixed placing Saplings in farmland 2012-12-27 17:35:40 +01:00
64 changed files with 2725 additions and 1203 deletions

5
.gitattributes vendored
View File

@ -1,5 +1,10 @@
# Auto detect text files and perform LF normalization # Auto detect text files and perform LF normalization
* text=auto * text=auto
*.php text eol=lf
*.sh text eol=lf
*.txt text eol=lf
*.properties text eol=lf
*.bat text eol=crlf
# Custom for Visual Studio # Custom for Visual Studio
*.cs diff=csharp *.cs diff=csharp

8
.gitignore vendored
View File

@ -1,11 +1,11 @@
data/players/* players/*
data/maps/* worlds/*
data/plugins/* plugins/*
logs/*
*.log *.log
server.properties server.properties
white-list.txt white-list.txt
banned-ips.txt banned-ips.txt
console.in
################# #################
## Eclipse ## Eclipse

View File

@ -25,17 +25,12 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
//Protocol Version: 4 require_once(dirname(__FILE__)."/src/common/dependencies.php");
require_once("classes/PocketMinecraftServer.class.php");
require_once("API/ServerAPI.php");
$server = new ServerAPI();
$server->run();//$server->start();
//$server->join();
define("MC_KEEP_ALIVE", 0x00); kill(getmypid()); //Fix for segfault
define("MC_CLIENT_CONNECT", 0x09);
define("MC_SERVER_HANDSHAKE", 0x10);
define("MC_CLIENT_HANDSHAKE", 0x13);
define("MC_CLIENT_DISCONNECT", 0x15);
define("MC_LOGIN", 0x86);
define("MC_LOGIN_STATUS", 0x87);
define("MC_READY", 0x88);
define("MC_CHAT", 0x89);
define("MC_SET_TIME", 0x8a);
define("MC_START_GAME", 0x8b);

View File

@ -1,65 +1,59 @@
This program is free software: you can redistribute it and/or modify ```
it under the terms of the GNU Lesser General Public License as published by This program is free software: you can redistribute it and/or modify
the Free Software Foundation, either version 3 of the License, or it under the terms of the GNU Lesser General Public License as published by
(at your option) any later version. the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details. GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
- -
/ \ / \
/ \ / \
/ PocketMine \ / PocketMine \
/ MP \ / MP \
|\ @shoghicp /| |\ @shoghicp /|
|. \ / .| |. \ / .|
| .. \ / .. | | .. \ / .. |
| .. | .. | | .. | .. |
| .. | .. | | .. | .. |
\ | / \ | /
\ | / \ | /
\ | / \ | /
\ | / \ | /
```
# PocketMine-MP
PocketMine-MP is a Server for Minecraft Pocket Edition done in PHP. It has a Plugin API that enables a developer to extend it and add new features, or change default ones.
The entire server is done in PHP, and has been tested, profiled and optimized to run smoothly.
[Go to Wiki for more information](https://github.com/shoghicp/PocketMine-MP/wiki)
[FAQ: Frequently Asked Questions](https://github.com/shoghicp/PocketMine-MP/wiki/Frequently-Asked-Questions)
PocketMine-MP ## Current features of the server:
=============
Github repo: https://github.com/shoghicp/PocketMine-MP
Server (and client) Minecraft Pocket Edition library written in PHP.
Currently a work in progress, and used to document http://www.wiki.vg/Pocket_Minecraft_Protocol
Check the wiki! https://github.com/shoghicp/PocketMine-MP/wiki
**Project Status: `ALPHA`**
**Tested in: `v4.0.0, v5.0.0`**
Current features of the server:
-------------------------------
* Full Creative mode! * Full Creative mode!
* Plugin API * Plugin API
* Custom world generation * Custom world generation
* Update Channels!! (stable / dev) * Update Channels!! (stable / dev)
How to contact me ## Third-party Libraries Used
----------------- * __[PHP cURL](http://php.net/manual/en/book.curl.php)__
* Email - <shoghicp@gmail.com> * __[PHP Sockets](http://php.net/manual/en/book.sockets.php)__
* Twitter - [@shoghicp](https://twitter.com/shoghicp) * __[PHP SQLite3](http://php.net/manual/en/book.sqlite3.php)__
* Via IRC - #mcdevs or #mcpedevs on *irc.freenode.net* (or just /msg me there) * __[PHP pthreads](https://github.com/krakjoe/pthreads)__ by _[krakjoe](https://github.com/krakjoe)_: Threading for PHP - Share Nothing, Do Everything
* [MinecraftForums profile](http://www.minecraftforum.net/user/1476633-shoghicp/) * __[PHP NBT](https://github.com/TheFrozenFire/PHP-NBT-Decoder-Encoder/blob/master/nbt.class.php)__ by _[TheFrozenFire](https://github.com/TheFrozenFire)_: Class for reading in NBT-format files (modified to handle Little-Endian files)
Third-party Libraries Used
--------------------------
* __[PHP NBT](https://github.com/TheFrozenFire/PHP-NBT-Decoder-Encoder/blob/master/nbt.class.php)__ by [TheFrozenFire](https://github.com/TheFrozenFire): Class for reading in NBT-format files
* __[Math_BigInteger](http://phpseclib.sourceforge.net/math/intro.html)__ by _[phpseclib](http://phpseclib.sourceforge.net/)_: Pure-PHP arbitrary precission integer arithmetic library * __[Math_BigInteger](http://phpseclib.sourceforge.net/math/intro.html)__ by _[phpseclib](http://phpseclib.sourceforge.net/)_: Pure-PHP arbitrary precission integer arithmetic library
* __[Spyc](https://github.com/mustangostang/spyc/blob/master/Spyc.php)__ by _[Vlad Andersen](https://github.com/mustangostang)_: A simple YAML loader/dumper class for PHP * __[Spyc](https://github.com/mustangostang/spyc/blob/master/Spyc.php)__ by _[Vlad Andersen](https://github.com/mustangostang)_: A simple YAML loader/dumper class for PHP

33
TODO.md
View File

@ -1,33 +0,0 @@
__Check Milestones [here](https://github.com/shoghicp/PocketMine-MP/issues/milestones)__
## Not Fixing
- Random strips and chunks of different land not sticking (the ones shown before world chunks are loaded)
## Known Bugs
- Players jerk sometimes
- Some chunk columns doesn't get loaded
- Players don't despawn
- Players can often hear "echos" of their own block interactions' SFX (torch placed, block destroyed, etc. most noticeable when moving around as you place.)
## Alpha (full Creative)
* Done!
### Alpha v1.1
## Beta (Survival)
- Random Chunk Updates
- Mob spawning, item pick up
- Mob behavior
- Placing Paintings
- Inventory loading and saving
- Entity saving
- Wheat growth
- Melon growth
- Sapling growth
- Mob interaction (hitting)
- Firing Arrows
- Eating
- Nether???

134
compile_php.sh Normal file
View File

@ -0,0 +1,134 @@
#!/bin/bash
echo "[PocketMine] PHP installer and compiler for Linux - by @shoghicp v0.3"
DIR=`pwd`
date > $DIR/install.log
uname -a >> $DIR/install.log
echo "[INFO] Checking dependecies"
type make >> $DIR/install.log 2>&1 || { echo >&2 "[ERROR] Please install \"make\""; exit 1; }
type autoconf >> $DIR/install.log 2>&1 || { echo >&2 "[ERROR] Please install \"autoconf\""; exit 1; }
type automake >> $DIR/install.log 2>&1 || { echo >&2 "[ERROR] Please install \"automake\""; exit 1; }
type gcc >> $DIR/install.log 2>&1 || { echo >&2 "[ERROR] Please install \"gcc\""; exit 1; }
type m4 >> $DIR/install.log 2>&1 || { echo >&2 "[ERROR] Please install \"m4\""; exit 1; }
rm -r -f install_data/ >> $DIR/install.log 2>&1
rm -r -f php5/ >> $DIR/install.log 2>&1
mkdir -m 0777 install_data >> $DIR/install.log 2>&1
mkdir -m 0777 php5 >> $DIR/install.log 2>&1
cd install_data
#PHP 5
echo -n "[PHP5] downloading..."
wget http://php.net/get/php-5.4.10.tar.gz/from/this/mirror -O php-5.4.10.tar.gz >> $DIR/install.log 2>&1
echo -n " extracting..."
tar -zxvf php-5.4.10.tar.gz >> $DIR/install.log 2>&1
rm -f php-5.4.10.tar.gz >> $DIR/install.log 2>&1
mv php-5.4.10 php
echo " done!"
#zlib
echo -n "[zlib] downloading..."
wget http://zlib.net/zlib-1.2.7.tar.gz -O zlib-1.2.7.tar.gz >> $DIR/install.log 2>&1
echo -n " extracting..."
tar -zxvf zlib-1.2.7.tar.gz >> $DIR/install.log 2>&1
rm -f zlib-1.2.7.tar.gz >> $DIR/install.log 2>&1
mv zlib-1.2.7 zlib
echo -n " compiling..."
cd zlib
./configure --prefix=$DIR/install_data/php/ext/zlib >> $DIR/install.log 2>&1
make >> $DIR/install.log 2>&1
echo -n " installing..."
make install >> $DIR/install.log 2>&1
echo -n " cleaning..."
cd ..
rm -r -f ./zlib
echo " done!"
#OpenSSL
#echo -n "[OpenSSL] downloading..."
#wget ftp://ftp.openssl.org/source/openssl-1.0.1c.tar.gz -O openssl-1.0.1c.tar.gz >> $DIR/install.log 2>&1
#echo -n " extracting..."
#tar -zxvf openssl-1.0.1c.tar.gz >> $DIR/install.log 2>&1
#rm -f openssl-1.0.1c.tar.gz >> $DIR/install.log 2>&1
#mv openssl-1.0.1c openssl
#echo -n " compiling..."
#cd openssl
#./config --prefix=$DIR/install_data/php/ext/openssl >> $DIR/install.log 2>&1
#make >> $DIR/install.log 2>&1
#echo -n " installing..."
#make install >> $DIR/install.log 2>&1
#echo -n " cleaning..."
#cd ..
#rm -r -f ./openssl
#echo " done!"
#GMP
echo -n "[GMP] downloading..."
wget ftp://ftp.gmplib.org/pub/gmp-5.1.0/gmp-5.1.0.tar.bz2 -O gmp-5.1.0.tar.bz2 >> $DIR/install.log 2>&1
echo -n " extracting..."
tar -jxvf gmp-5.1.0.tar.bz2 >> $DIR/install.log 2>&1
rm -f gmp-5.1.0.tar.bz2 >> $DIR/install.log 2>&1
mv gmp-5.1.0 gmp
echo -n " compiling..."
cd gmp
./configure --prefix=$DIR/install_data/php/ext/gmp >> $DIR/install.log 2>&1
make >> $DIR/install.log 2>&1
echo -n " installing..."
make install >> $DIR/install.log 2>&1
echo -n " cleaning..."
cd ..
rm -r -f ./gmp
echo " done!"
echo -n "[cURL] downloading..."
wget https://github.com/bagder/curl/archive/master.tar.gz --no-check-certificate -O curl-master.tar.gz >> $DIR/install.log 2>&1
echo -n " extracting..."
tar -zxvf curl-master.tar.gz >> $DIR/install.log 2>&1
rm -f curl-master.tar.gz >> $DIR/install.log 2>&1
mv curl-master curl
echo -n " compiling..."
cd curl
./buildconf >> $DIR/install.log 2>&1
./configure --prefix=$DIR/install_data/php/ext/curl >> $DIR/install.log 2>&1
make >> $DIR/install.log 2>&1
echo -n " installing..."
make install >> $DIR/install.log 2>&1
echo -n " cleaning..."
cd ..
rm -r -f ./curl
echo " done!"
#pthreads
echo -n "[PHP pthreads] downloading..."
wget https://github.com/krakjoe/pthreads/archive/master.tar.gz --no-check-certificate -O pthreads-master.tar.gz >> $DIR/install.log 2>&1
echo -n " extracting..."
tar -zxvf pthreads-master.tar.gz >> $DIR/install.log 2>&1
rm -f pthreads-master.tar.gz >> $DIR/install.log 2>&1
mv pthreads-master $DIR/install_data/php/ext/pthreads
echo " done!"
#--with-openssl=$DIR/install_data/php/ext/openssl
echo -n "[PHP5] compiling..."
cd php
./buildconf --force >> $DIR/install.log 2>&1
./configure --prefix=$DIR/php5 \
--exec-prefix=$DIR/php5 \
--enable-embedded-mysqli \
--enable-bcmath \
--with-gmp=$DIR/install_data/php/ext/gmp \
--with-curl=$DIR/install_data/php/ext/curl \
--with-zlib=$DIR/install_data/php/ext/zlib \
--disable-xml \
--without-pear \
--enable-sockets \
--enable-pthreads \
--enable-maintainer-zts \
--enable-cli >> $DIR/install.log 2>&1
make >> $DIR/install.log 2>&1
make install >> $DIR/install.log 2>&1
echo " done!"
cd $DIR
echo -n "[INFO] Cleaning up..."
rm -r -f install_data/ >> $DIR/install.log 2>&1
rmdir install_data/ >> $DIR/install.log 2>&1
echo " done!"
echo "[PocketMine] You should start the server now using \"./start.sh\""
echo "[PocketMine] If it doesn't works, please send the \"install.log\" file to the Bug Tracker"

View File

@ -1,2 +0,0 @@
To import a Pocket Edition Map, drop to the folder "maps"
the chunk.dat, level.dat and entities.dat from the savegame file.

View File

@ -1,34 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=ExamplePlugin
version=0.0.1
author=shoghicp
class=ExamplePlugin
*/
class ExamplePlugin implements Plugin{
private $api;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$this->api->console->register("example", "Example command", array($this, "handleCommand"));
}
public function __destruct(){
}
public function handleCommand($cmd, $arg){
switch($cmd){
case "example":
console("EXAMPLE!!!");
break;
}
}
}

View File

@ -1,46 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=ReactorAsWater
description=Replaces the Nether Reactor with Water
version=0.0.2
author=shoghicp
class=ReactorAsWater
*/
class ReactorAsWater implements Plugin{
private $api;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$this->api->addHandler("player.block.action", array($this, "handle"), 15); //Priority higher that API
$this->api->addHandler("player.equipment.change", array($this, "handle"), 15);
}
public function __destruct(){
}
public function handle(&$data, $event){
switch($event){
case "player.equipment.change":
if($data["block"] === 247){
$this->api->player->getByEID($data["eid"])->eventHandler("[ReactorAsWater] Placing water", "server.chat");
$data["block"] = 9;
$data["meta"] = 0;
}
break;
case "player.block.action":
if($data["block"] === 247){ //nether reactor
$data["block"] = 9; //water source
$data["meta"] = 0;
}
break;
}
}
}

View File

@ -1,105 +0,0 @@
<?php
/*
__PocketMine Plugin__
name=SpawnChanger
description=Change the spawn coordinates, or make it default for all players!
version=0.0.1
author=shoghicp
class=SpawnChanger
*/
class SpawnChanger implements Plugin{
private $api, $config, $path;
public function __construct(ServerAPI $api, $server = false){
$this->api = $api;
}
public function init(){
$spawn = $this->api->level->getSpawn();
$this->path = $this->api->plugin->createConfig($this, array(
"spawnX" => $spawn["x"],
"spawnY" => $spawn["y"],
"spawnZ" => $spawn["z"],
"custom-spawn" => false,
"force-spawn" => false,
));
$this->config = $this->api->plugin->readYAML($this->path."config.yml");
if($this->config["custom-spawn"] === false){
$this->config["spawnX"] = $spawn["x"];
$this->config["spawnY"] = $spawn["y"];
$this->config["spawnZ"] = $spawn["z"];
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
$this->api->addHandler("api.player.offline.get", array($this, "handle"), 15);
$this->api->console->register("spawnchanger", "SpawnChanger init point managing", array($this, "command"));
$this->api->console->register("spawn", "Teleports to spawn", array($this, "command"));
}
public function __destruct(){
}
public function command($cmd, $args){
switch($cmd){
case "spawnchanger":
switch(strtolower(array_shift($args))){
case "force":
$l = array_shift($args);
if($l != "0" and $l != "1"){
console("[SpawnChanger] Usage: /spawnchanger force <1 | 0>");
}else{
$this->config["force-spawn"] = $l == "0" ? false:true;
if($this->config["force-spawn"] === true){
console("[SpawnChanger] Forced spawn point");
}else{
console("[SpawnChanger] Freed pawn point");
}
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
break;
case "set":
$z = array_pop($args);
$y = array_pop($args);
$x = array_pop($args);
if($x === null or $y === null or $z === null){
console("[SpawnChanger] Usage: /spawnchanger set <x> <y> <z>");
}else{
$this->config["custom-spawn"] = true;
$this->config["spawnX"] = (float) $x;
$this->config["spawnY"] = (float) $y;
$this->config["spawnZ"] = (float) $z;
console("[SpawnChanger] Spawn point set at X ".$this->config["spawnX"]." Y ".$this->config["spawnY"]." Z ".$this->config["spawnZ"]);
$this->api->plugin->writeYAML($this->path."config.yml", $this->config);
}
break;
default:
console("[SpawnChanger] Always spawn player in spawn point: /spawnchanger force <1 | 0>");
console("[SpawnChanger] Set the spawn point: /spawnchanger set <x> <y> <z>");
break;
}
break;
case "spawn":
if($this->api->player->tppos(implode(" ", $args), $this->config["spawnX"], $this->config["spawnY"], $this->config["spawnZ"]) !== false){
console("[SpawnChanger] Teleported to spawn!");
}else{
console("[SpawnChanger] Usage: /spawn <player>");
}
break;
}
}
public function handle(&$data, $event){
switch($event){
case "api.player.offline.get":
if($this->config["force-spawn"] === true){
$data["spawn"]["x"] = $this->config["spawnX"];
$data["spawn"]["y"] = $this->config["spawnY"];
$data["spawn"]["z"] = $this->config["spawnZ"];
}
break;
}
}
}

View File

@ -1,39 +0,0 @@
<?php
/*
-
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
require_once("common/dependencies.php");
require_once("classes/PocketMinecraftServer.class.php");
require_once("classes/API/ServerAPI.php");
while(true){
$server = new ServerAPI();
//You can add simple things here
if($server->start() !== true){
break;
}else{
console("[INFO] Cleaning up...");
hard_unset($server);
$excludeList = array("GLOBALS", "_FILES", "_COOKIE", "_POST", "_GET", "excludeList");
foreach(get_defined_vars() as $key => $value){
if(!in_array($key, $excludeList)){
$$key = null;
unset($$key);
}
}
$server = null;
unset($server);
console("[NOTICE] The server is restarting... (".gc_collect_cycles()." cycles collected)", true, true, 0);
}
}

View File

@ -42,6 +42,53 @@ class BlockAPI{
$this->server->addHandler("world.block.update", array($this, "updateBlockRemote"), 1); $this->server->addHandler("world.block.update", array($this, "updateBlockRemote"), 1);
$this->server->addHandler("player.block.break", array($this, "blockBreak"), 1); $this->server->addHandler("player.block.break", array($this, "blockBreak"), 1);
$this->server->addHandler("player.block.action", array($this, "blockAction"), 1); $this->server->addHandler("player.block.action", array($this, "blockAction"), 1);
$this->server->api->console->register("give", "Give items to a player", array($this, "commandHandler"));
}
public function commandHandler($cmd, $params){
switch($cmd){
case "give":
if(!isset($params[0]) or !isset($params[1])){
console("[INFO] Usage: /give <username> <item> [amount] [damage]");
break;
}
$username = $params[0];
$b = explode(":", $params[1]);
if(!isset($b[1])){
$meta = 0;
}else{
$meta = (int) $b[1];
}
$block = ((int) $b[0]) & 0xFFFF;
if(!isset($params[2])){
$amount = 64;
}else{
$amount = (int) $params[2];
}
if(isset($params[3])){
$meta = (int) $params[3];
}
if(($player = $this->server->api->player->get($username)) !== false){
$this->drop($player->entity->x - 0.5, $player->entity->y, $player->entity->z - 0.5, $block, $meta, $amount);
console("[INFO] Giving ".$amount." of ".$block.":".$meta." to ".$username);
}else{
console("[INFO] Unknown player");
}
break;
}
}
private function cancelAction($block){
$this->server->api->dhandle("world.block.change", array(
"x" => $block[2][0],
"y" => $block[2][1],
"z" => $block[2][2],
"block" => $block[0],
"meta" => $block[1],
"fake" => true,
));
return false;
} }
public function blockBreak($data, $event){ public function blockBreak($data, $event){
@ -50,7 +97,7 @@ class BlockAPI{
} }
$target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]); $target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
if(isset(Material::$unbreakable[$target[0]])){ if(isset(Material::$unbreakable[$target[0]])){
return false; return $this->cancelAction($target);
} }
$drop = array( $drop = array(
$target[0], //Block $target[0], //Block
@ -58,12 +105,29 @@ class BlockAPI{
1, //Count 1, //Count
); );
switch($target[0]){ switch($target[0]){
case 1:
$drop[0] = 4;
break;
case 16: case 16:
$drop = array(263, 0, 1); $drop = array(263, 0, 1);
break; break;
case 21: case 21:
$drop = array(351, 4, mt_rand(4, 8)); $drop = array(351, 4, mt_rand(4, 8));
break; break;
case 62:
$drop[0] = 61;
case 50: //Drop without metadata
case 53:
case 54:
case 61:
case 65:
case 67:
case 96:
case 107:
case 108:
case 109:
$drop[1] = 0;
break;
case 56: case 56:
$drop = array(264, 0, 1); $drop = array(264, 0, 1);
break; break;
@ -113,27 +177,37 @@ class BlockAPI{
2, 2,
); );
break; break;
case 46: //TNT
if(($player = $this->server->api->player->getByEID($data["eid"])) !== false){
$player->dataPacket(MC_EXPLOSION, array(
"x" => $data["x"],
"y" => $data["y"],
"z" => $data["z"],
"radius" => 2,
"records" => array(),
));
}
break;
case 60: case 60:
case 2: case 2:
$drop = array(3, 0, 1); $drop = array(3, 0, 1);
break; break;
case 64: //Door case 64: //Wood Door
$drop = array(324, 0, 1); case 71: //Iron Door
$drop = array(($target[0] === 64 ? 324:330), 0, 1);
if(($target[1] & 0x08) === 0x08){ if(($target[1] & 0x08) === 0x08){
$down = $this->server->api->level->getBlock($data["x"], $data["y"] - 1, $data["z"]); $down = $this->server->api->level->getBlock($data["x"], $data["y"] - 1, $data["z"]);
if($down[0] === 64){ if($down[0] === $target[0]){
$data2 = $data; $data2 = $data;
--$data2["y"]; --$data2["y"];
$this->server->trigger("player.block.break", $data2); $this->server->trigger("player.block.break", $data2);
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL);
} }
}else{ }else{
$up = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]); $up = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]);
if($up[0] === 64){ if($up[0] === $target[0]){
$data2 = $data; $data2 = $data;
++$data2["y"]; ++$data2["y"];
$this->server->trigger("player.block.break", $data2); $this->server->trigger("player.block.break", $data2);
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL);
} }
} }
break; break;
@ -142,7 +216,6 @@ class BlockAPI{
$this->drop($data["x"], $data["y"], $data["z"], $drop[0], $drop[1] & 0x0F, $drop[2] & 0xFF); $this->drop($data["x"], $data["y"], $data["z"], $drop[0], $drop[1] & 0x0F, $drop[2] & 0xFF);
} }
$this->server->trigger("player.block.break", $data); $this->server->trigger("player.block.break", $data);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
return false; return false;
} }
@ -158,10 +231,16 @@ class BlockAPI{
"stack" => $stack, "stack" => $stack,
); );
$data["x"] += mt_rand(2, 8) / 10; $data["x"] += mt_rand(2, 8) / 10;
$data["y"] += mt_rand(2, 8) / 10; $data["y"] += 0.19;
$data["z"] += mt_rand(2, 8) / 10; $data["z"] += mt_rand(2, 8) / 10;
$e = $this->server->api->entity->add(ENTITY_ITEM, $block, $data); if($this->server->api->handle("block.drop", $data) !== false){
$this->server->api->entity->spawnToAll($e->eid); for($count = $stack; $count > 0; ){
$data["stack"] = min(64, $count);
$count -= $data["stack"];
$e = $this->server->api->entity->add(ENTITY_ITEM, $block, $data);
$this->server->api->entity->spawnToAll($e->eid);
}
}
} }
public function blockAction($data, $event){ public function blockAction($data, $event){
@ -172,9 +251,40 @@ class BlockAPI{
return false; return false;
} }
$target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]); $target = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
if($target[0] === 0){ //If no block exists
$this->cancelAction($target);
$block = $this->server->api->level->getBlockFace($target, $data["face"]);
return $this->cancelAction($block);
}
$cancelPlace = false; $cancelPlace = false;
if(isset(Material::$activable[$target[0]])){ if(isset(Material::$activable[$target[0]])){
switch($target[0]){ switch($target[0]){
case 54:
$cancelPlace = true;
if($this->server->gamemode === 1){
break;
}
$this->server->api->player->getByEID($data["eid"])->dataPacket(MC_CONTAINER_OPEN, array(
"windowid" => 1,
"type" => WINDOW_CHEST,
"slots" => 27,
"title" => "Chest",
));
break;
case 61:
case 62:
$cancelPlace = true;
if($this->server->gamemode === 1){
break;
}
$this->server->api->player->getByEID($data["eid"])->dataPacket(MC_CONTAINER_OPEN, array(
"windowid" => 1,
"type" => WINDOW_FURNACE,
"slots" => 3,
"title" => "Furnace",
));
break;
case 6: case 6:
if($data["block"] === 351 and $data["meta"] === 0x0F){ //Bonemeal if($data["block"] === 351 and $data["meta"] === 0x0F){ //Bonemeal
Sapling::growTree($this->server->api->level, $target, $target[1] & 0x03); Sapling::growTree($this->server->api->level, $target, $target[1] & 0x03);
@ -186,24 +296,15 @@ class BlockAPI{
if($data["block"] === 292){ //Hoe if($data["block"] === 292){ //Hoe
$data["block"] = 60; $data["block"] = 60;
$data["meta"] = 0; $data["meta"] = 0;
$this->server->trigger("player.block.place", $data); $this->server->handle("player.block.place", $data);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
$cancelPlace = true; $cancelPlace = true;
} }
case 59: case 59:
case 105: case 105:
if($data["block"] === 351 and $data["meta"] === 0x0F){ //Bonemeal if($data["block"] === 351 and $data["meta"] === 0x0F){ //Bonemeal
switch($target[0]){ $data["block"] = $target[0];
case 59: $data["meta"] = 0x07;
case 105: $this->server->handle("player.block.place", $data);
$data["block"] = $target[0];
$data["meta"] = 0x07;
$this->server->trigger("player.block.place", $data);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
break;
case 6:
break;
}
$cancelPlace = true; $cancelPlace = true;
} }
break; break;
@ -220,22 +321,23 @@ class BlockAPI{
"meta" => $down[1], "meta" => $down[1],
"eid" => $data["eid"], "eid" => $data["eid"],
); );
$this->server->trigger("player.block.update", $data2); if($this->server->handle("player.block.update", $data2) !== false){
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL); $this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL); }
} }
}else{ }else{
$data["block"] = $target[0]; $data["block"] = $target[0];
$data["meta"] = $target[1] ^ 0x04; $data["meta"] = $target[1] ^ 0x04;
$this->server->trigger("player.block.update", $data); if($this->server->handle("player.block.update", $data) !== false){
$up = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]); $up = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]);
if($up[0] === 64){ if($up[0] === 64){
$data2 = $data; $data2 = $data;
$data2["meta"] = $up[1]; $data2["meta"] = $up[1];
++$data2["y"]; ++$data2["y"];
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL); $this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL);
}
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
} }
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
} }
$cancelPlace = true; $cancelPlace = true;
break; break;
@ -243,8 +345,7 @@ class BlockAPI{
case 107: //Fence gates case 107: //Fence gates
$data["block"] = $target[0]; $data["block"] = $target[0];
$data["meta"] = $target[1] ^ 0x04; $data["meta"] = $target[1] ^ 0x04;
$this->server->trigger("player.block.update", $data); $this->server->handle("player.block.update", $data);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
$cancelPlace = true; $cancelPlace = true;
break; break;
default: default:
@ -282,25 +383,33 @@ class BlockAPI{
$block = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]); $block = $this->server->api->level->getBlock($data["x"], $data["y"], $data["z"]);
if($replace === false and !isset(Material::$replaceable[$block[0]])){ if($replace === false and !isset(Material::$replaceable[$block[0]])){
return false; return $this->cancelAction($block);
} }
if(isset(Material::$placeable[$data["block"]])){ if(isset(Material::$placeable[$data["block"]])){
$data["block"] = Material::$placeable[$data["block"]] === true ? $data["block"]:Material::$placeable[$data["block"]]; $data["block"] = Material::$placeable[$data["block"]] === true ? $data["block"]:Material::$placeable[$data["block"]];
}else{ }else{
return false; return $this->cancelAction($block);
}
$entity = $this->server->api->entity->get($data["eid"]);
if(($entity instanceof Entity) !== true){
return $this->cancelAction($block); //No Entity WTF?
} }
$direction = $this->server->api->entity->get($data["eid"])->getDirection(); if($entity->inBlock($block[2][0], $block[2][1], $block[2][2])){
return $this->cancelAction($block); //Entity in block
}
$direction = $entity->getDirection();
switch($data["block"]){ switch($data["block"]){
case 6: case 6:
if($target[0] === 60){ if($target[0] === 60){
return false; break;
} }
case 37: case 37:
case 38: case 38:
if($target[0] !== 2 and $target[0] !== 3){ if(($target[0] !== 2 and $target[0] !== 3) or $data["face"] !== 1){
return false; return false;
} }
break; break;
@ -379,6 +488,10 @@ class BlockAPI{
$data["meta"] = $faces[$direction] & 0x03; $data["meta"] = $faces[$direction] & 0x03;
break; break;
case 64://Door placing case 64://Door placing
case 71:
if($data["face"] !== 1){
return false;
}
$blockUp = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]); $blockUp = $this->server->api->level->getBlock($data["x"], $data["y"] + 1, $data["z"]);
$blockDown = $this->server->api->level->getBlock($data["x"], $data["y"] - 1, $data["z"]); $blockDown = $this->server->api->level->getBlock($data["x"], $data["y"] - 1, $data["z"]);
if(!isset(Material::$replaceable[$blockUp[0]]) or isset(Material::$transparent[$blockDown[0]])){ if(!isset(Material::$replaceable[$blockUp[0]]) or isset(Material::$transparent[$blockDown[0]])){
@ -387,9 +500,18 @@ class BlockAPI{
$data2 = $data; $data2 = $data;
$data2["meta"] = 0x08; $data2["meta"] = 0x08;
$data["meta"] = $direction & 0x03; $data["meta"] = $direction & 0x03;
$face = array(
0 => 3,
1 => 4,
2 => 2,
3 => 5,
);
$next = $this->server->api->level->getBlockFace($block, $face[(($direction + 2) % 4)]);
if($next[0] === $data["block"]){ //Door hinge
$data2["meta"] = $data2["meta"] | 0x01;
}
++$data2["y"]; ++$data2["y"];
$this->server->trigger("player.block.place", $data2); $this->server->handle("player.block.place", $data2);
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL);
} }
break; break;
case 54: case 54:
@ -419,8 +541,7 @@ class BlockAPI{
$data2["x"] = $next[2][0]; $data2["x"] = $next[2][0];
$data2["y"] = $next[2][1]; $data2["y"] = $next[2][1];
$data2["z"] = $next[2][2]; $data2["z"] = $next[2][2];
$this->server->trigger("player.block.place", $data2); $this->server->handle("player.block.place", $data2);
$this->updateBlocksAround($data2["x"], $data2["y"], $data2["z"], BLOCK_UPDATE_NORMAL);
break; break;
case 65: //Ladder case 65: //Ladder
if(isset(Material::$transparent[$target[0]])){ if(isset(Material::$transparent[$target[0]])){
@ -456,9 +577,7 @@ class BlockAPI{
} }
break; break;
} }
$this->server->trigger("player.block.place", $data); $this->server->handle("player.block.place", $data);
$this->updateBlock($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
$this->updateBlocksAround($data["x"], $data["y"], $data["z"], BLOCK_UPDATE_NORMAL);
return false; return false;
} }
@ -467,7 +586,7 @@ class BlockAPI{
} }
public function updateBlockRemote($data, $event){ public function updateBlockRemote($data, $event){
if($event !== "world.block.update"){ if($event !== "block.update"){
return; return;
} }
$this->updateBlock($data["x"], $data["y"], $data["z"], isset($data["type"]) ? $data["type"]:BLOCK_UPDATE_RANDOM); $this->updateBlock($data["x"], $data["y"], $data["z"], isset($data["type"]) ? $data["type"]:BLOCK_UPDATE_RANDOM);
@ -493,7 +612,7 @@ class BlockAPI{
"z" => $spread[2][2], "z" => $spread[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $spread[0], $level | $down); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $spread[0], $level | $down, false);
return true; return true;
} }
}elseif(isset(Material::$flowable[$spread[0]])){ }elseif(isset(Material::$flowable[$spread[0]])){
@ -503,10 +622,10 @@ class BlockAPI{
"z" => $spread[2][2], "z" => $spread[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], 10, $level | $down); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], 10, $level | $down, false);
return true; return true;
}elseif(($source[1] & 0x08) === 0x08){ }elseif(($source[1] & 0x08) === 0x08){
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $source[0], $source[1] & 0x07); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $source[0], $source[1] & 0x07, false);
return true; return true;
} }
return false; return false;
@ -532,7 +651,7 @@ class BlockAPI{
"z" => $spread[2][2], "z" => $spread[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $spread[0], $level | $down); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $spread[0], $level | $down, false);
return true; return true;
} }
}elseif(isset(Material::$flowable[$spread[0]])){ }elseif(isset(Material::$flowable[$spread[0]])){
@ -542,10 +661,10 @@ class BlockAPI{
"z" => $spread[2][2], "z" => $spread[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], 8, $level | $down); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], 8, $level | $down, false);
return true; return true;
}elseif(($source[1] & 0x08) === 0x08){ }elseif(($source[1] & 0x08) === 0x08){
$this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $source[0], $source[1] & 0x07); $this->server->api->level->setBlock($spread[2][0], $spread[2][1], $spread[2][2], $source[0], $source[1] & 0x07, false);
return true; return true;
} }
return false; return false;
@ -624,7 +743,7 @@ class BlockAPI{
"z" => $block[2][2], "z" => $block[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], 0, 0); $this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], 0, 0, false);
}else{ }else{
$block[1] = ($block[1] & 0x08) | $level; $block[1] = ($block[1] & 0x08) | $level;
$this->server->schedule(10, array($this, "blockScheduler"), array( $this->server->schedule(10, array($this, "blockScheduler"), array(
@ -663,7 +782,7 @@ class BlockAPI{
"z" => $block[2][2], "z" => $block[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], $block[0], $block[1]); $this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], $block[0], $block[1], false);
} }
} }
} }
@ -736,7 +855,7 @@ class BlockAPI{
"z" => $block[2][2], "z" => $block[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], 0, 0); $this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], 0, 0, false);
}else{ }else{
$block[1] = ($block[1] & 0x08) | $level; $block[1] = ($block[1] & 0x08) | $level;
$this->server->schedule(20, array($this, "blockScheduler"), array( $this->server->schedule(20, array($this, "blockScheduler"), array(
@ -775,7 +894,7 @@ class BlockAPI{
"z" => $block[2][2], "z" => $block[2][2],
"type" => BLOCK_UPDATE_NORMAL, "type" => BLOCK_UPDATE_NORMAL,
)); ));
$this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], $block[0], $block[1]); $this->server->api->level->setBlock($block[2][0], $block[2][1], $block[2][2], $block[0], $block[1], false);
} }
} }
} }
@ -784,14 +903,14 @@ class BlockAPI{
case 74: case 74:
if($type === BLOCK_UPDATE_SCHEDULED or $type === BLOCK_UPDATE_RANDOM){ if($type === BLOCK_UPDATE_SCHEDULED or $type === BLOCK_UPDATE_RANDOM){
$changed = true; $changed = true;
$this->server->api->level->setBlock($x, $y, $z, 73, $block[1]); $this->server->api->level->setBlock($x, $y, $z, 73, $block[1], false);
$type = BLOCK_UPDATE_WEAK; $type = BLOCK_UPDATE_WEAK;
} }
break; break;
case 73: case 73:
if($type === BLOCK_UPDATE_NORMAL){ if($type === BLOCK_UPDATE_NORMAL){
$changed = true; $changed = true;
$this->server->api->level->setBlock($x, $y, $z, 74, $block[1]); $this->server->api->level->setBlock($x, $y, $z, 74, $block[1], false);
$this->server->schedule(mt_rand(40, 100), array($this, "blockScheduler"), array( $this->server->schedule(mt_rand(40, 100), array($this, "blockScheduler"), array(
"x" => $x, "x" => $x,
"y" => $y, "y" => $y,

51
src/API/ChatAPI.php Normal file
View File

@ -0,0 +1,51 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class ChatAPI{
private $server;
function __construct(PocketMinecraftServer $server){
$this->server = $server;
}
public function init(){
}
public function broadcast($message){
$this->send(false, $message);
}
public function send($owner, $text, $whitelist = false, $blacklist = false){
$message = "";
if($owner !== false){
$message = "<".$owner."> ";
}
$message .= $text;
console("[CHAT] ".$message);
$this->server->handle("server.chat", new Container($message, $whitelist, $blacklist));
}
}

View File

@ -26,25 +26,34 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
class ConsoleAPI{ class ConsoleAPI{
private $input, $server, $event; private $loop, $server, $event, $help, $cmds;
function __construct(PocketMinecraftServer $server){ function __construct(PocketMinecraftServer $server){
$this->help = array(); $this->help = array();
$this->cmds = array();
$this->server = $server; $this->server = $server;
$this->input = fopen(FILE_PATH."console.in", "w+b");
$this->last = microtime(true); $this->last = microtime(true);
} }
public function init(){ public function init(){
$this->event = $this->server->event("server.tick", array($this, "handle")); $this->event = $this->server->event("server.tick", array($this, "handle"));
$this->loop = new ConsoleLoop;
$this->loop->start();
} }
function __destroy(){ function __destruct(){
$this->server->deleteEvent($this->event); $this->server->deleteEvent($this->event);
fclose($this->input); $this->loop->stop = true;
$this->loop->notify();
$this->loop->join();
} }
public function defaultCommands($cmd, $params){ public function defaultCommands($cmd, $params){
switch($cmd){ switch($cmd){
case "crash": //Crashes the server to generate an report
$this->callNotDefinedMethodCrash();
$this->server->api->server; //Access a private property
callNotExistingFunction();
break;
case "invisible": case "invisible":
$p = strtolower(array_shift($params)); $p = strtolower(array_shift($params));
switch($p){ switch($p){
@ -74,6 +83,7 @@ class ConsoleAPI{
$this->server->api->setProperty("last-update", time()); $this->server->api->setProperty("last-update", time());
break; break;
case "stop": case "stop":
$this->loop->stop = true;
console("[INFO] Stopping the server..."); console("[INFO] Stopping the server...");
$this->server->close(); $this->server->close();
break; break;
@ -142,7 +152,7 @@ class ConsoleAPI{
console("[INFO] Usage: /say <message>"); console("[INFO] Usage: /say <message>");
break; break;
} }
$this->server->chat(false, $s); $this->server->api->chat->broadcast($s);
break; break;
case "whitelist": case "whitelist":
$p = strtolower(array_shift($params)); $p = strtolower(array_shift($params));
@ -212,7 +222,7 @@ class ConsoleAPI{
console("[INFO] /stop: Stops the server"); console("[INFO] /stop: Stops the server");
//console("[INFO] /restart: Restarts the server"); //console("[INFO] /restart: Restarts the server");
foreach($this->help as $c => $h){ foreach($this->help as $c => $h){
console("[INFO] /$c: ".$h[0]); console("[INFO] /$c: ".$h);
} }
break; break;
default: default:
@ -221,30 +231,49 @@ class ConsoleAPI{
} }
} }
public function register($cmd, $help,$callback){ public function alias($alias, $cmd){
$this->cmds[strtolower(trim($alias))] = &$this->cmds[$cmd];
}
public function register($cmd, $help, $callback){
if(!is_callable($callback)){ if(!is_callable($callback)){
return false; return false;
} }
$this->help[strtolower(trim($cmd))] = array($help, $callback); $cmd = strtolower(trim($cmd));
$this->cmds[$cmd] = $callback;
$this->help[$cmd] = $help;
} }
public function handle($time){ public function handle($time){
while(($line = fgets($this->input)) !== false){ if($this->loop->line !== false){
$line = trim($line); $line = trim($this->loop->line);
if($line === ""){ $this->loop->line = false;
continue; if($line !== ""){
} $params = explode(" ", $line);
$params = explode(" ", $line); $cmd = strtolower(array_shift($params));
$cmd = strtolower(array_shift($params)); console("[INFO] Issued server command: /$cmd ".implode(" ", $params));
console("[INFO] Issued server command: /$cmd ".implode(" ", $params)); if(isset($this->cmds[$cmd]) and is_callable($this->cmds[$cmd])){
if(isset($this->help[$cmd]) and is_callable($this->help[$cmd][1])){ call_user_func($this->cmds[$cmd], $cmd, $params);
call_user_func($this->help[$cmd][1], $cmd, $params); }elseif($this->server->api->dhandle("api.console.command", array("cmd" => $cmd, "params" => $params)) !== false){
}elseif($this->server->trigger("api.console.command", array("cmd" => $cmd, "params" => $params)) !== false){ $this->defaultCommands($cmd, $params);
$this->defaultCommands($cmd, $params); }
} }
}else{
$this->loop->notify();
} }
ftruncate($this->input, 0);
fseek($this->input, 0);
} }
} }
class ConsoleLoop extends Thread{
var $line = false, $stop = false;
public function run(){
$fp = fopen("php://stdin", "r");
while($this->stop === false and ($line = fgets($fp)) !== false){
$this->line = $line;
$this->wait();
$this->line = false;
}
exit(0);
}
}

View File

@ -31,44 +31,6 @@ class EntityAPI{
$this->server = $server; $this->server = $server;
} }
public function init(){
$this->server->addHandler("player.death", array($this, "handle"), 1);
$this->server->api->console->register("give", "Give items to a player [DUMMY]", array($this, "commandHandler"));
}
public function handle($data, $event){
switch($event){
case "player.death":
$message = $data["name"];
if(is_numeric($data["cause"]) and isset($this->entities[$data["cause"]])){
$e = $this->api->entity->get($data["cause"]);
switch($e->class){
case ENTITY_PLAYER:
$message .= " was killed by ".$e->name;
break;
default:
$message .= " was killed";
break;
}
}else{
switch($data["cause"]){
default:
$message .= " was killed";
break;
}
}
$this->server->chat(false, $message);
break;
}
}
public function commandHandler($cmd, $params){
switch($cmd){
case "give":
break;
}
}
public function get($eid){ public function get($eid){
if(isset($this->server->entities[$eid])){ if(isset($this->server->entities[$eid])){
return $this->server->entities[$eid]; return $this->server->entities[$eid];
@ -76,6 +38,10 @@ class EntityAPI{
return false; return false;
} }
public function init(){
}
public function getAll(){ public function getAll(){
return $this->server->entities; return $this->server->entities;
} }
@ -95,6 +61,7 @@ class EntityAPI{
public function add($class, $type = 0, $data = array()){ public function add($class, $type = 0, $data = array()){
$eid = $this->server->eidCnt++; $eid = $this->server->eidCnt++;
$this->server->entities[$eid] = new Entity($this->server, $eid, $class, $type, $data); $this->server->entities[$eid] = new Entity($this->server, $eid, $class, $type, $data);
$this->server->handle("entity.add", $this->server->entities[$eid]);
return $this->server->entities[$eid]; return $this->server->entities[$eid];
} }
@ -126,8 +93,11 @@ class EntityAPI{
public function remove($eid){ public function remove($eid){
if(isset($this->server->entities[$eid])){ if(isset($this->server->entities[$eid])){
$this->server->entities[$eid]->close(); $entity = $this->server->entities[$eid];
unset($this->server->entities[$eid]); unset($this->server->entities[$eid]);
$this->server->query("DELETE FROM entities WHERE EID = ".$entity->eid.";");
$this->server->api->dhandle("entity.remove", $entity);
unset($entity);
} }
} }
} }

View File

@ -47,13 +47,12 @@ class LevelAPI{
$this->setBlock($data["x"], $data["y"], $data["z"], $data["block"], $data["meta"]); $this->setBlock($data["x"], $data["y"], $data["z"], $data["block"], $data["meta"]);
break; break;
case "player.block.break": case "player.block.break":
$block = $this->getBlock($data["x"], $data["y"], $data["z"]); $block = $this->getBlock($data["x"], $data["y"], $data["z"]);
console("[DEBUG] EID ".$data["eid"]." broke block ".$block[0].":".$block[1]." at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2); console("[DEBUG] EID ".$data["eid"]." broke ".$block[0].":".$block[1]." at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2);
if($block[0] === 0){
if($block[0] === 0){ break;
break; }
} $this->setBlock($data["x"], $data["y"], $data["z"], 0, 0);
$this->setBlock($data["x"], $data["y"], $data["z"], 0, 0);
break; break;
} }
} }
@ -85,16 +84,21 @@ class LevelAPI{
return $this->heightMap[$z][$x]; return $this->heightMap[$z][$x];
} }
public function setBlock($x, $y, $z, $block, $meta = 0){ public function setBlock($x, $y, $z, $block, $meta = 0, $update = true){
$this->map->setBlock($x, $y, $z, $block, $meta); $this->map->setBlock($x, $y, $z, $block, $meta);
$this->heightMap[$z][$x] = $this->map->getFloor($x, $z); $this->heightMap[$z][$x] = $this->map->getFloor($x, $z);
$this->server->trigger("world.block.change", array( if($this->server->api->dhandle("block.change", array(
"x" => $x, "x" => $x,
"y" => $y, "y" => $y,
"z" => $z, "z" => $z,
"block" => $block, "block" => $block,
"meta" => $meta, "meta" => $meta,
)); )) !== false){
if($update === true){
$this->server->api->block->updateBlock($x, $y, $z, BLOCK_UPDATE_NORMAL);
$this->server->api->block->updateBlocksAround($x, $y, $z, BLOCK_UPDATE_NORMAL);
}
}
} }
public function getOrderedChunk($X, $Z, $columnsPerPacket = 2){ public function getOrderedChunk($X, $Z, $columnsPerPacket = 2){

View File

@ -32,7 +32,8 @@ class PlayerAPI{
} }
public function init(){ public function init(){
$this->server->event("server.regeneration", array($this, "handle")); $this->server->addHandler("server.regeneration", array($this, "handle"));
$this->server->addHandler("player.death", array($this, "handle"), 1);
$this->server->api->console->register("list", "Shows connected player list", array($this, "commandHandler")); $this->server->api->console->register("list", "Shows connected player list", array($this, "commandHandler"));
$this->server->api->console->register("kill", "Kills a player", array($this, "commandHandler")); $this->server->api->console->register("kill", "Kills a player", array($this, "commandHandler"));
$this->server->api->console->register("tppos", "Teleports a player to a position", array($this, "commandHandler")); $this->server->api->console->register("tppos", "Teleports a player to a position", array($this, "commandHandler"));
@ -42,13 +43,36 @@ class PlayerAPI{
public function handle($data, $event){ public function handle($data, $event){
switch($event){ switch($event){
case "server.regeneration": case "server.regeneration":
$result = $this->server->query("SELECT ip,port FROM players WHERE EID = (SELECT EID FROM entities WHERE health < 20);", true); $result = $this->server->query("SELECT EID FROM players WHERE EID = (SELECT EID FROM entities WHERE health < 20);");
if($result !== true and $result !== false){ if($result !== true and $result !== false){
while(false !== ($player = $result->fetchArray())){ while(false !== ($player = $result->fetchArray())){
$player->entity->setHealth(min(20, $player->entity->getHealth() + $data), "regeneration"); if(($player = $this->server->api->player->getByEID($player["EID"])) !== false){
$player->entity->setHealth(min(20, $player->entity->getHealth() + $data), "regeneration");
}
} }
} }
break; break;
case "player.death":
$message = $data["name"];
if(is_numeric($data["cause"]) and isset($this->entities[$data["cause"]])){
$e = $this->api->entity->get($data["cause"]);
switch($e->class){
case ENTITY_PLAYER:
$message .= " was killed by ".$e->name;
break;
default:
$message .= " was killed";
break;
}
}else{
switch($data["cause"]){
default:
$message .= " was killed";
break;
}
}
$this->server->chat(false, $message);
break;
} }
} }
@ -178,18 +202,17 @@ class PlayerAPI{
public function remove($CID){ public function remove($CID){
if(isset($this->server->clients[$CID])){ if(isset($this->server->clients[$CID])){
$player = $this->server->clients[$CID]; $player = $this->server->clients[$CID];
if(is_object($player->entity)){ unset($this->server->clients[$CID]);
$player->entity->close(); $player->close();
}
$this->saveOffline($player->username, $player->data); $this->saveOffline($player->username, $player->data);
$this->server->query("DELETE FROM players WHERE name = '".$player->username."';"); $this->server->query("DELETE FROM players WHERE name = '".$player->username."';");
unset($this->server->entities[$player->eid]); $this->server->api->entity->remove($player->eid);
unset($this->server->clients[$player->CID]); unset($player);
} }
} }
public function getOffline($name){ public function getOffline($name){
if(!file_exists(FILE_PATH."data/players/".$name.".dat")){ if(!file_exists(FILE_PATH."players/".$name.".dat")){
console("[NOTICE] Player data not found for \"".$name."\", creating new profile"); console("[NOTICE] Player data not found for \"".$name."\", creating new profile");
$data = array( $data = array(
"spawn" => array( "spawn" => array(
@ -203,7 +226,7 @@ class PlayerAPI{
); );
$this->saveOffline($name, $data); $this->saveOffline($name, $data);
}else{ }else{
$data = unserialize(file_get_contents(FILE_PATH."data/players/".$name.".dat")); $data = unserialize(file_get_contents(FILE_PATH."players/".$name.".dat"));
} }
$this->server->handle("api.player.offline.get", $data); $this->server->handle("api.player.offline.get", $data);
return $data; return $data;
@ -211,6 +234,6 @@ class PlayerAPI{
public function saveOffline($name, $data){ public function saveOffline($name, $data){
$this->server->handle("api.player.offline.save", $data); $this->server->handle("api.player.offline.save", $data);
file_put_contents(FILE_PATH."data/players/".str_replace("/", "", $name).".dat", serialize($data)); file_put_contents(FILE_PATH."players/".str_replace("/", "", $name).".dat", serialize($data));
} }
} }

View File

@ -115,7 +115,7 @@ class PluginAPI extends stdClass{
if($p === false){ if($p === false){
return false; return false;
} }
$path = FILE_PATH."data/plugins/".$p[1]["name"]."/"; $path = FILE_PATH."plugins/".$p[1]["name"]."/";
$this->plugins[$p[1]["class"]][1]["path"] = $path; $this->plugins[$p[1]["class"]][1]["path"] = $path;
if(!file_exists($path."config.yml")){ if(!file_exists($path."config.yml")){
@mkdir($path, 0777); @mkdir($path, 0777);
@ -155,11 +155,11 @@ class PluginAPI extends stdClass{
public function loadAll(){ public function loadAll(){
console("[INFO] Loading Plugins..."); console("[INFO] Loading Plugins...");
$dir = dir(FILE_PATH."data/plugins/"); $dir = dir(FILE_PATH."plugins/");
while(false !== ($file = $dir->read())){ while(false !== ($file = $dir->read())){
if($file !== "." and $file !== ".."){ if($file{0} !== "."){
if(strtolower(substr($file, -3)) === "php"){ if(strtolower(substr($file, -3)) === "php"){
$this->load(FILE_PATH."data/plugins/" . $file); $this->load(FILE_PATH."plugins/" . $file);
} }
} }
} }

View File

@ -25,15 +25,24 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
class ServerAPI extends stdClass{ //Yay! I can add anything to this class in runtime! class ServerAPI{
var $restart = false; var $restart = false;
private $server, $config, $apiList = array(); private $server, $config, $apiList = array();
function __construct(){
public function run(){
$this->load();
return $this->init();
}
public function load(){
@mkdir(FILE_PATH."logs/", 0777, true);
@mkdir(FILE_PATH."players/", 0777);
@mkdir(FILE_PATH."worlds/", 0777);
@mkdir(FILE_PATH."plugins/", 0777);
console("[INFO] Starting ServerAPI server handler..."); console("[INFO] Starting ServerAPI server handler...");
file_put_contents(FILE_PATH."packets.log", ""); file_put_contents(FILE_PATH."logs/packets.log", "");
file_put_contents(FILE_PATH."console.in", ""); if(!file_exists(FILE_PATH."logs/test.bin.log") or md5_file(FILE_PATH."logs/test.bin.log") !== TEST_MD5){
if(!file_exists(FILE_PATH."test.bin.log") or md5_file(FILE_PATH."test.bin.log") !== TEST_MD5){ console("[NOTICE] Executing tests...");
console("[NOTICE] Executing integrity tests...");
console("[INFO] OS: ".PHP_OS.", ".Utils::getOS()); console("[INFO] OS: ".PHP_OS.", ".Utils::getOS());
console("[INFO] uname -a: ".php_uname("a")); console("[INFO] uname -a: ".php_uname("a"));
console("[INFO] PHP Version: ".phpversion()); console("[INFO] PHP Version: ".phpversion());
@ -56,12 +65,14 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
$test .= Utils::writeLong("-1152921504606846976"); $test .= Utils::writeLong("-1152921504606846976");
$test .= Utils::writeTriad(16777215); $test .= Utils::writeTriad(16777215);
$test .= Utils::writeTriad(16777216); $test .= Utils::writeTriad(16777216);
$str = new Java_String("THIS_IS_ a TEsT_SEED1_123456789^.,.,\xff\x00\x15"); $str = new Java_String("TESTING\x00\n\r\t\xff");
$test .= Utils::writeLong($str->hashCode()); $test .= Utils::writeLong($str->hashCode());
$test .= Utils::writeDataArray(array("a", "b", "c", "\xff\xff\xff\xff")); $test .= Utils::writeDataArray(array("a", "b", "c", "\xff\xff\xff\xff"));
$test .= Utils::hexToStr("012334567890"); $test .= Utils::hexToStr("012334567890");
file_put_contents(FILE_PATH."test.bin.log", $test); file_put_contents(FILE_PATH."logs/test.bin.log", $test);
if(md5($test) !== TEST_MD5){ $md5 = md5($test);
console("[INFO] MD5 of test: ".$md5);
if($md5 !== TEST_MD5){
console("[ERROR] Test error, please send your console.log + test.bin.log to the Github repo"); console("[ERROR] Test error, please send your console.log + test.bin.log to the Github repo");
die(); die();
} }
@ -79,19 +90,21 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
if(!file_exists(FILE_PATH."server.properties")){ if(!file_exists(FILE_PATH."server.properties")){
console("[NOTICE] No server.properties found, using default settings"); console("[NOTICE] No server.properties found, using default settings");
copy(FILE_PATH."common/default.properties", FILE_PATH."server.properties"); copy(FILE_PATH."src/common/default.properties", FILE_PATH."server.properties");
} }
console("[DEBUG] Checking data folders...", true, true, 2);
@mkdir(FILE_PATH."data/players/", 0777, true);
@mkdir(FILE_PATH."data/maps/", 0777);
@mkdir(FILE_PATH."data/plugins/", 0777);
console("[DEBUG] Loading server.properties...", true, true, 2); console("[DEBUG] Loading server.properties...", true, true, 2);
$this->parseProperties(); $this->parseProperties();
define("DEBUG", $this->config["debug"]); define("DEBUG", $this->getProperty("debug"));
$this->server = new PocketMinecraftServer($this->getProperty("server-name"), $this->getProperty("gamemode"), false, CURRENT_PROTOCOL, $this->getProperty("port"), $this->getProperty("server-id")); $this->server = new PocketMinecraftServer($this->getProperty("server-name"), $this->getProperty("gamemode"), false, $this->getProperty("port"), $this->getProperty("server-id"));
$this->server->api = $this; $this->server->api = $this;
if($this->getProperty("upnp-forwarding") === true){
console("[INFO] [UPnP] Trying to port forward...");
UPnP_PortForward($this->getProperty("port"));
}
if(($ip = Utils::getIP()) !== false){
console("[INFO] External IP: ".$ip);
}
if($this->getProperty("last-update") === false or ($this->getProperty("last-update") + 3600) < time()){ if($this->getProperty("last-update") === false or ($this->getProperty("last-update") + 3600) < time()){
console("[INFO] Checking for new server version"); console("[INFO] Checking for new server version");
console("[INFO] Last check: ".date("Y-m-d H:i:s", $this->getProperty("last-update"))); console("[INFO] Last check: ".date("Y-m-d H:i:s", $this->getProperty("last-update")));
@ -123,12 +136,23 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
if($info === false or !isset($info[0])){ if($info === false or !isset($info[0])){
console("[ERROR] GitHub API Error"); console("[ERROR] GitHub API Error");
}else{ }else{
$info = $info[0];
if($info["name"] != MAJOR_VERSION){ $newest = new VersionString(MAJOR_VERSION);
$newest = array(-1, $newest->getNumber());
foreach($info as $i => $tag){
$update = new VersionString($tag["name"]);
$update = $update->getNumber();
if($update > $newest[1]){
$newest = array($i, $update);
}
}
if($newest[0] !== -1){
$target = $info[$newest[0]];
console("[NOTICE] A new STABLE version of PocketMine-MP has been released"); console("[NOTICE] A new STABLE version of PocketMine-MP has been released");
console("[NOTICE] Version \"".$info["name"]."\" [".substr($info["commit"]["sha"], 0, 10)."]"); console("[NOTICE] Version \"".(new VersionString($newest[1]))."\" #".$newest[1]." [".substr($target["commit"]["sha"], 0, 10)."]");
console("[NOTICE] Download it at ".$info["zipball_url"]); console("[NOTICE] Download it at ".$target["zipball_url"]);
console("[NOTICE] This message will dissapear as soon as you update\""); console("[NOTICE] This message will dissapear as soon as you update");
sleep(5); sleep(5);
}else{ }else{
$this->setProperty("last-update", time()); $this->setProperty("last-update", time());
@ -138,17 +162,17 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
} }
} }
if(file_exists(FILE_PATH."data/maps/level.dat")){ if(file_exists(FILE_PATH."worlds/level.dat")){
console("[NOTICE] Detected unimported map data. Importing..."); console("[NOTICE] Detected unimported map data. Importing...");
$this->importMap(FILE_PATH."data/maps/", true); $this->importMap(FILE_PATH."worlds/", true);
} }
$this->server->mapName = $this->getProperty("level-name"); $this->server->mapName = $this->getProperty("level-name");
$this->server->mapDir = FILE_PATH."data/maps/".$this->server->mapName."/"; $this->server->mapDir = FILE_PATH."worlds/".$this->server->mapName."/";
if($this->server->mapName === false or trim($this->server->mapName) === "" or !file_exists($this->server->mapDir."chunks.dat")){ if($this->server->mapName === false or trim($this->server->mapName) === "" or (!file_exists($this->server->mapDir."chunks.dat") and !file_exists($this->server->mapDir."chunks.dat.gz"))){
if($this->server->mapName === false or trim($this->server->mapName) === ""){ if($this->server->mapName === false or trim($this->server->mapName) === ""){
$this->server->mapName = "world"; $this->server->mapName = "world";
} }
$this->server->mapDir = FILE_PATH."data/maps/".$this->server->mapName."/"; $this->server->mapDir = FILE_PATH."worlds/".$this->server->mapName."/";
$generator = "SuperflatGenerator"; $generator = "SuperflatGenerator";
if($this->getProperty("generator") !== false and class_exists($this->getProperty("generator"))){ if($this->getProperty("generator") !== false and class_exists($this->getProperty("generator"))){
$generator = $this->getProperty("generator"); $generator = $this->getProperty("generator");
@ -168,9 +192,9 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
//Autoload all default APIs //Autoload all default APIs
console("[INFO] Loading default APIs"); console("[INFO] Loading default APIs");
$dir = dir(FILE_PATH."classes/API/"); $dir = dir(FILE_PATH."src/API/");
while(false !== ($file = $dir->read())){ while(false !== ($file = $dir->read())){
if($file !== "." and $file !== ".."){ if($file{0} !== "."){ //Hidden and upwards folders
$API = basename($file, ".php"); $API = basename($file, ".php");
if(strtolower($API) !== "serverapi"){ if(strtolower($API) !== "serverapi"){
$name = strtolower(substr($API, 0, -3)); $name = strtolower(substr($API, 0, -3));
@ -187,9 +211,19 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
$this->server->loadEntities(); $this->server->loadEntities();
} }
public function __destruct(){
foreach($this->apiList as $ob){
if(is_callable($ob, "__destruct")){
$ob->__destruct();
unset($this->apiList[$ob]);
}
}
}
private function loadProperties(){ private function loadProperties(){
if(isset($this->config["memory-limit"])){ if($this->getProperty("memory-limit") !== false){
@ini_set("memory_limit", $this->config["memory-limit"]); @ini_set("memory_limit", $this->getProperty("memory-limit"));
}else{ }else{
$this->config["memory-limit"] = "256M"; $this->config["memory-limit"] = "256M";
} }
@ -197,15 +231,15 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
$this->config["invisible"] = false; $this->config["invisible"] = false;
} }
if(is_object($this->server)){ if(is_object($this->server)){
$this->server->setType($this->config["server-type"]); $this->server->setType($this->getProperty("server-type"));
$this->server->timePerSecond = $this->config["time-per-second"]; $this->server->timePerSecond = $this->getProperty("time-per-second");
$this->server->invisible = $this->config["invisible"]; $this->server->invisible = $this->getProperty("invisible");
$this->server->maxClients = $this->config["max-players"]; $this->server->maxClients = $this->getProperty("max-players");
$this->server->description = $this->config["description"]; $this->server->description = $this->getProperty("description");
$this->server->motd = $this->config["motd"]; $this->server->motd = $this->getProperty("motd");
$this->server->gamemode = $this->config["gamemode"]; $this->server->gamemode = $this->getProperty("gamemode");
$this->server->difficulty = $this->config["difficulty"]; $this->server->difficulty = $this->getProperty("difficulty");
$this->server->whitelist = $this->config["white-list"]; $this->server->whitelist = $this->getProperty("white-list");
$this->server->reloadConfig(); $this->server->reloadConfig();
} }
} }
@ -217,6 +251,7 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
$config = $this->config; $config = $this->config;
$config["white-list"] = $config["white-list"] === true ? "true":"false"; $config["white-list"] = $config["white-list"] === true ? "true":"false";
$config["invisible"] = $config["invisible"] === true ? "true":"false"; $config["invisible"] = $config["invisible"] === true ? "true":"false";
$config["upnp-forwarding"] = $config["upnp-forwarding"] === true ? "true":"false";
$prop = "#Pocket Minecraft PHP server properties\r\n#".date("D M j H:i:s T Y")."\r\n"; $prop = "#Pocket Minecraft PHP server properties\r\n#".date("D M j H:i:s T Y")."\r\n";
foreach($config as $n => $v){ foreach($config as $n => $v){
$prop .= $n."=".$v."\r\n"; $prop .= $n."=".$v."\r\n";
@ -273,10 +308,15 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
} }
} }
public function start(){ public function init(){
$this->server->start(); $this->server->init();
unregister_tick_function(array($this->server, "tick")); unregister_tick_function(array($this->server, "tick"));
$this->__destruct();
unset($this->server); unset($this->server);
if($this->getProperty("upnp-forwarding") === true ){
console("[INFO] [UPnP] Removing port forward...");
UPnP_RemovePortForward($this->getProperty("port"));
}
return $this->restart; return $this->restart;
} }
@ -286,6 +326,10 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
return $this->server->addHandler($e, $c, $p); return $this->server->addHandler($e, $c, $p);
} }
public function dhandle($e, $d){
return $this->server->handle($e, $d);
}
public function handle($e, &$d){ public function handle($e, &$d){
return $this->server->handle($e, $d); return $this->server->handle($e, $d);
} }
@ -317,24 +361,24 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
console("[DEBUG] Importing map \"".$level["LevelName"]."\" gamemode ".$level["GameType"]." with seed ".$level["RandomSeed"], true, true, 2); console("[DEBUG] Importing map \"".$level["LevelName"]."\" gamemode ".$level["GameType"]." with seed ".$level["RandomSeed"], true, true, 2);
unset($level["Player"]); unset($level["Player"]);
$lvName = $level["LevelName"]."/"; $lvName = $level["LevelName"]."/";
@mkdir(FILE_PATH."data/maps/".$lvName, 0777); @mkdir(FILE_PATH."worlds/".$lvName, 0777);
file_put_contents(FILE_PATH."data/maps/".$lvName."level.dat", serialize($level)); file_put_contents(FILE_PATH."worlds/".$lvName."level.dat", serialize($level));
$entities = parseNBTData($nbt->loadFile($dir."entities.dat")); $entities = parseNBTData($nbt->loadFile($dir."entities.dat"));
file_put_contents(FILE_PATH."data/maps/".$lvName."entities.dat", serialize($entities["Entities"])); file_put_contents(FILE_PATH."worlds/".$lvName."entities.dat", serialize($entities["Entities"]));
if(!isset($entities["TileEntities"])){ if(!isset($entities["TileEntities"])){
$entities["TileEntities"] = array(); $entities["TileEntities"] = array();
} }
file_put_contents(FILE_PATH."data/maps/".$lvName."tileEntities.dat", serialize($entities["TileEntities"])); file_put_contents(FILE_PATH."worlds/".$lvName."tileEntities.dat", serialize($entities["TileEntities"]));
console("[DEBUG] Imported ".count($entities["Entities"])." Entities and ".count($entities["TileEntities"])." TileEntities", true, true, 2); console("[DEBUG] Imported ".count($entities["Entities"])." Entities and ".count($entities["TileEntities"])." TileEntities", true, true, 2);
if($remove === true){ if($remove === true){
rename($dir."chunks.dat", FILE_PATH."data/maps/".$lvName."chunks.dat"); rename($dir."chunks.dat", FILE_PATH."worlds/".$lvName."chunks.dat");
unlink($dir."level.dat"); unlink($dir."level.dat");
@unlink($dir."level.dat_old"); @unlink($dir."level.dat_old");
@unlink($dir."player.dat"); @unlink($dir."player.dat");
unlink($dir."entities.dat"); unlink($dir."entities.dat");
}else{ }else{
copy($dir."chunks.dat", FILE_PATH."data/maps/".$lvName."chunks.dat"); copy($dir."chunks.dat", FILE_PATH."worlds/".$lvName."chunks.dat");
} }
if($this->getProperty("level-name") === false){ if($this->getProperty("level-name") === false){
console("[INFO] Setting default level to \"".$level["LevelName"]."\""); console("[INFO] Setting default level to \"".$level["LevelName"]."\"");
@ -351,7 +395,48 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
return false; return false;
} }
public function getProperties(){
return $this->config;
}
public function getProperty($name){ public function getProperty($name){
if(($v = arg($name)) !== false){ //Allow for command-line arguments
switch(strtolower(trim($v))){
case "on":
case "true":
case "yes":
$v = true;
break;
case "off":
case "false":
case "no":
$v = false;
break;
}
switch($name){
case "last-update":
if($v === false){
$v = time();
}else{
$v = (int) $v;
}
break;
case "gamemode":
case "max-players":
case "port":
case "debug":
case "difficulty":
case "time-per-second":
$v = (int) $v;
break;
case "server-id":
if($v !== false){
$v = preg_match("/[^0-9\-]/", $v) > 0 ? Utils::readInt(substr(md5($v, true), 0, 4)):$v;
}
break;
}
return $v;
}
if(isset($this->config[$name])){ if(isset($this->config[$name])){
return $this->config[$name]; return $this->config[$name];
} }
@ -370,7 +455,7 @@ class ServerAPI extends stdClass{ //Yay! I can add anything to this class in run
public function loadAPI($name, $class, $dir = false){ public function loadAPI($name, $class, $dir = false){
if($dir === false){ if($dir === false){
$dir = FILE_PATH."classes/API/"; $dir = FILE_PATH."src/API/";
} }
$file = $dir.$class.".php"; $file = $dir.$class.".php";
if(!file_exists($file)){ if(!file_exists($file)){

View File

@ -0,0 +1,71 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class Async extends Thread {
/**
* Provide a passthrough to call_user_func_array
**/
public function __construct($method, $params = array()){
$this->method = $method;
$this->params = $params;
$this->result = null;
$this->joined = false;
}
/**
* The smallest thread in the world
**/
public function run(){
if(($this->result=call_user_func_array($this->method, $this->params))){
return true;
}else{
return false;
}
}
/**
* Static method to create your threads from functions ...
**/
public static function call($method, $params = array()){
$thread = new Async($method, $params);
if($thread->start()){
return $thread;
} /** else throw Nastyness **/
}
/**
* Do whatever, result stored in $this->result, don't try to join twice
**/
public function __toString(){
if(!$this->joined) {
$this->joined = true;
$this->join();
}
return $this->result;
}
}

View File

@ -59,11 +59,20 @@ class ChunkParser{
} }
public function loadFile($file){ public function loadFile($file){
if(!file_exists($file)){ if(ZLIB_EXTENSION === true and file_exists($file.".gz")){
$this->raw = gzinflate(file_get_contents($file.".gz"));
$r = @gzinflate($this->raw);
if($r !== false and $r != ""){
$this->raw = $r;
}
@unlink($file.".gz");
file_put_contents($file, $this->raw);
}elseif(!file_exists($file)){
return false; return false;
}else{
$this->raw = file_get_contents($file);
} }
$this->file = $file; $this->file = $file;
$this->raw = file_get_contents($file);
$this->chunkLength = $this->sectorLength * ord($this->raw{0}); $this->chunkLength = $this->sectorLength * ord($this->raw{0});
return true; return true;
} }
@ -147,8 +156,9 @@ class ChunkParser{
console("[DEBUG] Chunks loaded!", true, true, 2); console("[DEBUG] Chunks loaded!", true, true, 2);
} }
public function saveMap(){ public function saveMap($final = false){
console("[DEBUG] Saving chunks...", true, true, 2); console("[DEBUG] Saving chunks...", true, true, 2);
$fp = fopen($this->file, "r+b"); $fp = fopen($this->file, "r+b");
flock($fp, LOCK_EX); flock($fp, LOCK_EX);
foreach($this->map as $x => $d){ foreach($this->map as $x => $d){
@ -159,6 +169,15 @@ class ChunkParser{
} }
flock($fp, LOCK_UN); flock($fp, LOCK_UN);
fclose($fp); fclose($fp);
if(ZLIB_EXTENSION === true){
$original = filesize($this->file);
file_put_contents($this->file .".gz", gzdeflate(gzdeflate(file_get_contents($this->file),9),9)); //Double compression for flat maps
$compressed = filesize($this->file .".gz");
console("[DEBUG] Saved chunks.dat.gz with ".round(($compressed/$original)*100, 2)."% (".round($compressed/1024, 2)."KB) of the original size", true, true, 2);
if($final === true){
@unlink($this->file);
}
}
} }
public function getFloor($x, $z){ public function getFloor($x, $z){

View File

@ -56,6 +56,15 @@ class CustomPacketHandler{
$this->raw .= Utils::writeLong($this->data["payload"]); $this->raw .= Utils::writeLong($this->data["payload"]);
} }
break; break;
case 0x03:
if($this->c === false){
$this->data["unknown1"] = Utils::readLong($this->get(8));
$this->data["unknown2"] = Utils::readLong($this->get(8));
}else{
$this->raw .= Utils::writeLong($this->data["unknown1"]);
$this->raw .= Utils::writeLong($this->data["unknown2"]);
}
break;
case MC_CLIENT_CONNECT: case MC_CLIENT_CONNECT:
if($this->c === false){ if($this->c === false){
$this->data["clientID"] = Utils::readLong($this->get(8)); $this->data["clientID"] = Utils::readLong($this->get(8));
@ -279,6 +288,15 @@ class CustomPacketHandler{
$this->raw .= Utils::writeByte($this->data["roll"]); $this->raw .= Utils::writeByte($this->data["roll"]);
} }
break; break;
case MC_TAKE_ITEM_ENTITY:
if($this->c === false){
$this->data["target"] = Utils::readInt($this->get(4));
$this->data["eid"] = Utils::readInt($this->get(4));
}else{
$this->raw .= Utils::writeInt($this->data["target"]);
$this->raw .= Utils::writeInt($this->data["eid"]);
}
break;
case MC_MOVE_ENTITY: case MC_MOVE_ENTITY:
if($this->c === false){ if($this->c === false){
$this->data["eid"] = Utils::readInt($this->get(4)); $this->data["eid"] = Utils::readInt($this->get(4));
@ -373,6 +391,31 @@ class CustomPacketHandler{
$this->raw .= chr($this->data["meta"]); $this->raw .= chr($this->data["meta"]);
} }
break; break;
case MC_EXPLOSION:
if($this->c === false){
$this->data["x"] = Utils::readFloat($this->get(4));
$this->data["y"] = Utils::readFloat($this->get(4));
$this->data["z"] = Utils::readFloat($this->get(4));
$this->data["radius"] = Utils::readFloat($this->get(4));
$this->data["count"] = Utils::readInt($this->get(4));
$this->data["records"] = array();
for($r = 0; $r < $this->data["count"]; ++$r){
$this->data["records"][] = array(Utils::readByte($this->get(1)), Utils::readByte($this->get(1)), Utils::readByte($this->get(1)));
}
}else{
$this->raw .= Utils::writeFloat($this->data["x"]);
$this->raw .= Utils::writeFloat($this->data["y"]);
$this->raw .= Utils::writeFloat($this->data["z"]);
$this->raw .= Utils::writeFloat($this->data["radius"]);
$this->data["records"] = (array) $this->data["records"];
$this->raw .= Utils::writeInt(count($this->data["records"]));
if(count($this->data["records"]) > 0){
foreach($this->data["records"] as $record){
$this->raw .= Utils::writeByte($record[0]) . Utils::writeByte($record[1]) . Utils::writeByte($record[2]);
}
}
}
break;
case MC_REQUEST_CHUNK: case MC_REQUEST_CHUNK:
if($this->c === false){ if($this->c === false){
$this->data["x"] = Utils::readInt($this->get(4)); $this->data["x"] = Utils::readInt($this->get(4));
@ -445,9 +488,9 @@ class CustomPacketHandler{
break; break;
case MC_SET_HEALTH: case MC_SET_HEALTH:
if($this->c === false){ if($this->c === false){
$this->data["health"] = ord($this->get(1)); $this->data["health"] = Utils::readByte($this->get(1));
}else{ }else{
$this->raw .= chr($this->data["health"]); $this->raw .= Utils::writeByte($this->data["health"]);
} }
break; break;
case MC_ANIMATE: case MC_ANIMATE:
@ -472,6 +515,56 @@ class CustomPacketHandler{
$this->raw .= Utils::writeFloat($this->data["z"]); $this->raw .= Utils::writeFloat($this->data["z"]);
} }
break; break;
case MC_DROP_ITEM:
if($this->c === false){
$this->data["eid"] = Utils::readInt($this->get(4));
$this->data["unknown1"] = ord($this->get(1));
$this->data["block"] = Utils::readShort($this->get(2), false);
$this->data["stack"] = ord($this->get(1));
$this->data["meta"] = Utils::readShort($this->get(2), false);
}else{
$this->raw .= Utils::writeInt($this->data["eid"]);
$this->raw .= chr($this->data["unknown1"]);
$this->raw .= Utils::writeShort($this->data["block"]);
$this->raw .= chr($this->data["stack"]);
$this->raw .= Utils::writeShort($this->data["meta"]);
}
break;
case MC_CONTAINER_OPEN:
if($this->c === false){
$this->data["windowid"] = ord($this->get(1));
$this->data["type"] = ord($this->get(1));
$this->data["slots"] = Utils::readShort($this->get(2), false);
$this->data["title"] = $this->get(Utils::readShort($this->get(2), false));
}else{
$this->raw .= chr($this->data["windowid"]);
$this->raw .= chr($this->data["type"]);
$this->raw .= Utils::writeShort($this->data["slots"]);
$this->raw .= Utils::writeShort(strlen($this->data["title"])).$this->data["title"];
}
break;
case MC_CONTAINER_CLOSE:
if($this->c === false){
$this->data["windowid"] = ord($this->get(1));
}else{
$this->raw .= chr($this->data["windowid"]);
}
break;
case MC_CONTAINER_SET_SLOT:
if($this->c === false){
$this->data["windowid"] = ord($this->get(1));
$this->data["slot"] = Utils::readShort($this->get(2), false);
$this->data["block"] = Utils::readShort($this->get(2), false);
$this->data["stack"] = ord($this->get(1));
$this->data["meta"] = Utils::readShort($this->get(2), false);
}else{
$this->raw .= chr($this->data["windowid"]);
$this->raw .= Utils::writeShort($this->data["slot"]);
$this->raw .= Utils::writeShort($this->data["block"]);
$this->raw .= chr($this->data["stack"]);
$this->raw .= Utils::writeShort($this->data["meta"]);
}
break;
case MC_CLIENT_MESSAGE: case MC_CLIENT_MESSAGE:
if($this->c === false){ if($this->c === false){
$this->data["message"] = $this->get(Utils::readShort($this->get(2), false)); $this->data["message"] = $this->get(Utils::readShort($this->get(2), false));
@ -509,6 +602,13 @@ class CustomPacketHandler{
$this->raw .= $this->data["unknown2"]; $this->raw .= $this->data["unknown2"];
} }
break; break;
default:
if($this->c === false){
console("[DEBUG] Received unknown Data Packet ID 0x".dechex($pid), true, true, 2);
}else{
console("[DEBUG] Sent unknown Data Packet ID 0x".dechex($pid), true, true, 2);
}
break;
} }
} }

View File

@ -138,7 +138,7 @@ class Material{
6 => true, 6 => true,
26 => true, 26 => true,
31 => true, 31 => true,
46 => true, //46 => true,
51 => true, 51 => true,
54 => true, 54 => true,
58 => true, 58 => true,
@ -146,6 +146,7 @@ class Material{
61 => true, 61 => true,
62 => true, 62 => true,
64 => true, 64 => true,
71 => true,
78 => true, 78 => true,
96 => true, 96 => true,
105 => true, 105 => true,
@ -204,6 +205,7 @@ class Material{
324 => 64, 324 => 64,
65 => true, 65 => true,
67 => true, 67 => true,
330 => 71,
73 => true, 73 => true,
79 => true, 79 => true,
80 => true, 80 => true,

View File

@ -2,20 +2,20 @@
/* /*
- -
/ \ / \
/ \ / \
/ PocketMine \ / PocketMine \
/ MP \ / MP \
|\ @shoghicp /| |\ @shoghicp /|
|. \ / .| |. \ / .|
| .. \ / .. | | .. \ / .. |
| .. | .. | | .. | .. |
| .. | .. | | .. | .. |
\ | / \ | /
\ | / \ | /
\ | / \ | /
\ | / \ | /
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@ -33,8 +33,8 @@ define("ENTITY_ITEM", 3);
define("ENTITY_PAINTING", 4); define("ENTITY_PAINTING", 4);
class Entity extends stdClass{ class Entity extends stdClass{
var $eid, $type, $name, $x, $y, $z, $yaw, $pitch, $dead, $data, $class, $attach, $metadata, $closed, $player; var $invincible = false, $eid, $type, $name, $x, $y, $z, $speedX, $speedY, $speedZ, $last = array(0, 0, 0, 0), $yaw, $pitch, $dead, $data, $class, $attach, $metadata, $closed, $player, $onTick;
private $server;
function __construct($server, $eid, $class, $type = 0, $data = array()){ function __construct($server, $eid, $class, $type = 0, $data = array()){
$this->server = $server; $this->server = $server;
$this->eid = (int) $eid; $this->eid = (int) $eid;
@ -49,10 +49,14 @@ class Entity extends stdClass{
$this->closed = false; $this->closed = false;
$this->name = ""; $this->name = "";
$this->server->query("INSERT OR REPLACE INTO entities (EID, type, class, health) VALUES (".$this->eid.", ".$this->type.", ".$this->class.", ".$this->health.");"); $this->server->query("INSERT OR REPLACE INTO entities (EID, type, class, health) VALUES (".$this->eid.", ".$this->type.", ".$this->class.", ".$this->health.");");
$this->server->schedule(20, array($this, "update"), array(), true);
$this->metadata = array(); $this->metadata = array();
$this->x = isset($this->data["x"]) ? $this->data["x"]:0; $this->x = isset($this->data["x"]) ? $this->data["x"]:0;
$this->y = isset($this->data["y"]) ? $this->data["y"]:0; $this->y = isset($this->data["y"]) ? $this->data["y"]:0;
$this->z = isset($this->data["z"]) ? $this->data["z"]:0; $this->z = isset($this->data["z"]) ? $this->data["z"]:0;
$this->speedX = isset($this->data["speedX"]) ? $this->data["speedX"]:0;
$this->speedY = isset($this->data["speedY"]) ? $this->data["speedY"]:0;
$this->speedZ = isset($this->data["speedZ"]) ? $this->data["speedZ"]:0;
$this->yaw = isset($this->data["yaw"]) ? $this->data["yaw"]:0; $this->yaw = isset($this->data["yaw"]) ? $this->data["yaw"]:0;
$this->pitch = isset($this->data["pitch"]) ? $this->data["pitch"]:0; $this->pitch = isset($this->data["pitch"]) ? $this->data["pitch"]:0;
$this->position = array("x" => &$this->x, "y" => &$this->y, "z" => &$this->z, "yaw" => &$this->yaw, "pitch" => &$this->pitch); $this->position = array("x" => &$this->x, "y" => &$this->y, "z" => &$this->z, "yaw" => &$this->yaw, "pitch" => &$this->pitch);
@ -74,6 +78,23 @@ class Entity extends stdClass{
} }
} }
public function update(){
if($this->class === ENTITY_ITEM and $this->closed === false){
$this->server->api->dhandle("entity.move", $this);
$player = $this->server->query("SELECT EID FROM entities WHERE class == ".ENTITY_PLAYER." AND abs(x - {$this->x}) <= 1.5 AND abs(y - {$this->y}) <= 1.5 AND abs(z - {$this->z}) <= 1.5 LIMIT 1;", true);
if($player !== true and $player !== false){
if($this->server->api->dhandle("player.pickup", array(
"eid" => $player["EID"],
"block" => $this->type,
"meta" => $this->meta,
"target" => $this->eid
)) !== false){
$this->close();
}
}
}
}
public function getDirection(){ public function getDirection(){
$rotation = ($this->yaw - 90) % 360; $rotation = ($this->yaw - 90) % 360;
if ($rotation < 0) { if ($rotation < 0) {
@ -145,8 +166,7 @@ class Entity extends stdClass{
public function close(){ public function close(){
if($this->closed === false){ if($this->closed === false){
$this->server->query("DELETE FROM entities WHERE EID = ".$this->eid.";"); $this->server->api->entity->remove($this->eid);
$this->server->trigger("entity.remove", $this->eid);
$this->closed = true; $this->closed = true;
} }
} }
@ -181,6 +201,8 @@ class Entity extends stdClass{
$this->y = $y; $this->y = $y;
$this->z = $z; $this->z = $z;
$this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z." WHERE EID = ".$this->eid.";"); $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z." WHERE EID = ".$this->eid.";");
$this->updateVelocity();
$this->server->api->dhandle("entity.move", $this);
} }
public function move($x, $y, $z, $yaw = 0, $pitch = 0){ public function move($x, $y, $z, $yaw = 0, $pitch = 0){
@ -192,6 +214,8 @@ class Entity extends stdClass{
$this->pitch += $pitch; $this->pitch += $pitch;
$this->pitch %= 90; $this->pitch %= 90;
$this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";"); $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";");
$this->updateVelocity();
$this->server->api->dhandle("entity.move", $this);
} }
public function setPosition($x, $y, $z, $yaw, $pitch){ public function setPosition($x, $y, $z, $yaw, $pitch){
@ -201,7 +225,32 @@ class Entity extends stdClass{
$this->yaw = $yaw; $this->yaw = $yaw;
$this->pitch = $pitch; $this->pitch = $pitch;
$this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";"); $this->server->query("UPDATE entities SET x = ".$this->x.", y = ".$this->y.", z = ".$this->z.", pitch = ".$this->pitch.", yaw = ".$this->yaw." WHERE EID = ".$this->eid.";");
return true; $this->updateVelocity();
$this->server->api->dhandle("entity.move", $this);
}
public function inBlock($x, $y, $z){
$block = new Vector3($x + 0.5, $y, $z + 0.5);
$me = new Vector3($this->x, $this->y, $this->z);
$up = new Vector3($this->x, $this->y + 1, $this->z);
if($block->distance($me) < 0.8 or $block->distance($up) < 0.8){
return true;
}
return false;
}
public function updateVelocity(){
$diffTime = microtime(true) - $this->last[3];
$this->last[3] = microtime(true);
$speedX = ($this->x - $this->last[0]) / $diffTime;
$this->last[0] = $this->x;
$speedY = ($this->y - $this->last[1]) / $diffTime;
$this->last[1] = $this->y;
$speedZ = ($this->z - $this->last[2]) / $diffTime;
$this->last[2] = $this->z;
$this->speedX = $speedX;
$this->speedY = $speedY;
$this->speedZ = $speedZ;
} }
public function getPosition($round = false){ public function getPosition($round = false){
@ -211,7 +260,7 @@ class Entity extends stdClass{
public function setHealth($health, $cause = ""){ public function setHealth($health, $cause = ""){
$this->health = (int) $health; $this->health = (int) $health;
$this->server->query("UPDATE entities SET health = ".$this->health." WHERE EID = ".$this->eid.";"); $this->server->query("UPDATE entities SET health = ".$this->health." WHERE EID = ".$this->eid.";");
$this->server->trigger("entity.health.change", array("eid" => $this->eid, "health" => $health, "cause" => $cause)); $this->server->api->dhandle("entity.health.change", array("eid" => $this->eid, "health" => $health, "cause" => $cause));
if($this->player !== false){ if($this->player !== false){
$this->player->dataPacket(MC_SET_HEALTH, array( $this->player->dataPacket(MC_SET_HEALTH, array(
"health" => $this->health, "health" => $this->health,
@ -220,7 +269,7 @@ class Entity extends stdClass{
if($this->health <= 0 and $this->dead === false){ if($this->health <= 0 and $this->dead === false){
$this->dead = true; $this->dead = true;
if($this->player !== false){ if($this->player !== false){
$this->server->handle("player.death", array("name" => $this->name, "cause" => $cause)); $this->server->api->dhandle("player.death", array("name" => $this->name, "cause" => $cause));
} }
}elseif($this->health > 0){ }elseif($this->health > 0){
$this->dead = false; $this->dead = false;

View File

@ -26,10 +26,11 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
class WorldGenerator{ class WorldGenerator{
private $gen, $seed, $raw; private $gen, $seed, $genName, $raw;
public function __construct($genName, $seed){ public function __construct($genName, $seed){
$this->seed = (int) $seed; $this->seed = (int) $seed;
$this->raw = b""; $this->raw = b"";
$this->genName = $genName;
$this->gen = new $genName($this->seed); $this->gen = new $genName($this->seed);
} }
@ -345,6 +346,7 @@ class WorldGenerator{
"Time" => 0, "Time" => 0,
"Gamemode" => 1, "Gamemode" => 1,
"RandomSeed" => $this->seed, "RandomSeed" => $this->seed,
"Generator" => $this->genName,
"SpawnX" => $s[0], "SpawnX" => $s[0],
"SpawnY" => $s[1], "SpawnY" => $s[1],
"SpawnZ" => $s[2], "SpawnZ" => $s[2],

View File

@ -26,14 +26,13 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
class MinecraftInterface{ class MinecraftInterface{
var $pstruct, $name, $protocol, $client, $dataName; var $pstruct, $name, $client, $dataName;
private $socket, $data; private $socket, $data;
function __construct($server, $protocol = CURRENT_PROTOCOL, $port = 25565, $listen = false, $client = true){ function __construct($server, $port = 25565, $listen = false, $client = true){
$this->socket = new UDPSocket($server, $port, (bool) $listen); $this->socket = new UDPSocket($server, $port, (bool) $listen);
$this->protocol = (int) $protocol;
require("pstruct/RakNet.php"); require("pstruct/RakNet.php");
require("pstruct/packetName.php"); require("pstruct/packetName.php");
require("pstruct/".$this->protocol.".php"); require("pstruct/protocol.php");
require("pstruct/dataName.php"); require("pstruct/dataName.php");
$this->pstruct = $pstruct; $this->pstruct = $pstruct;
$this->name = $packetName; $this->name = $packetName;
@ -93,7 +92,6 @@ class MinecraftInterface{
} }
$packet = new Packet($pid, $struct, $data[0]); $packet = new Packet($pid, $struct, $data[0]);
$packet->protocol = $this->protocol;
$packet->parse(); $packet->parse();
$this->data[] = array($pid, $packet->data, $data[0], $data[1], $data[2]); $this->data[] = array($pid, $packet->data, $data[0], $data[1], $data[2]);
return $this->popPacket(); return $this->popPacket();
@ -119,7 +117,6 @@ class MinecraftInterface{
$struct = $this->getStruct($pid); $struct = $this->getStruct($pid);
if($raw === false){ if($raw === false){
$packet = new Packet($pid, $struct); $packet = new Packet($pid, $struct);
$packet->protocol = $this->protocol;
$packet->data = $data; $packet->data = $data;
$packet->create(); $packet->create();
$write = $this->socket->write($packet->raw, $dest, $port); $write = $this->socket->write($packet->raw, $dest, $port);

View File

@ -0,0 +1,66 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class NormalGenerator{
private $config, $spawn, $structure;
public function __construct($seed){
$this->config = array(
"seed" => (int) $seed,
);
}
public function set($name, $value){
$this->config[$name] = $value;
}
public function init(){
$this->spawn = array(128, 128, 128);
}
public function getSpawn(){
return $this->spawn;
}
public function getColumn($x, $z){
$x = (int) $x;
$z = (int) $z;
$column = $this->structure;
if(floor(sqrt(pow($x - $this->spawn[0], 2) + pow($z - $this->spawn[2], 2))) <= $this->config["spawn-radius"]){
$column[0]{strlen($column[0])-1} = chr($this->config["spawn-surface"]);
}
if(($x % 8) === 0 and ($z % 8) === 0 and $this->config["torches"] == "1"){
$column[0] .= chr(50);
}
$column[0] .= str_repeat(chr(0), 128 - strlen($column[0]));
$column[1] .= str_repeat(chr(0), 64 - strlen($column[1]));
$column[2] .= str_repeat(chr(0), 64 - strlen($column[2]));
$column[3] .= str_repeat(chr(0), 64 - strlen($column[3]));
return $column;
}
}

View File

@ -29,7 +29,7 @@ the Free Software Foundation, either version 3 of the License, or
class Packet{ class Packet{
private $struct, $sock; private $struct, $sock;
protected $pid, $packet; protected $pid, $packet;
public $data, $raw, $protocol; public $data, $raw;
function __construct($pid, $struct, $data = ""){ function __construct($pid, $struct, $data = ""){
$this->pid = $pid; $this->pid = $pid;
@ -75,7 +75,7 @@ class Packet{
case 0x40: case 0x40:
$reply = new CustomPacketHandler($this->data[$field]["id"], "", $this->data[$field], true); $reply = new CustomPacketHandler($this->data[$field]["id"], "", $this->data[$field], true);
$this->addRaw(Utils::writeShort((strlen($reply->raw) + 1) << 3)); $this->addRaw(Utils::writeShort((strlen($reply->raw) + 1) << 3));
$this->addRaw(Utils::writeTriad($this->data[$field]["count"])); $this->addRaw(Utils::writeTriad(strrev($this->data[$field]["count"])));
$this->addRaw(chr($this->data[$field]["id"])); $this->addRaw(chr($this->data[$field]["id"]));
$this->addRaw($reply->raw); $this->addRaw($reply->raw);
break; break;

View File

@ -27,7 +27,7 @@ the Free Software Foundation, either version 3 of the License, or
class Player{ class Player{
private $server, $timeout, $connected, $evid, $queue, $buffer; private $server, $timeout, $connected, $queue, $buffer, $evid = array();
var $clientID, $ip, $port, $counter, $username, $eid, $data, $entity, $auth, $CID, $MTU, $spawned, $equipment; var $clientID, $ip, $port, $counter, $username, $eid, $data, $entity, $auth, $CID, $MTU, $spawned, $equipment;
function __construct($server, $clientID, $ip, $port, $MTU){ function __construct($server, $clientID, $ip, $port, $MTU){
$this->queue = array(); $this->queue = array();
@ -42,7 +42,6 @@ class Player{
$this->entity = false; $this->entity = false;
$this->port = $port; $this->port = $port;
$this->timeout = microtime(true) + 25; $this->timeout = microtime(true) + 25;
$this->evid = array();
$this->equipment = array(1, 0); $this->equipment = array(1, 0);
$this->spawned = false; $this->spawned = false;
$this->evid[] = $this->server->event("server.tick", array($this, "onTick")); $this->evid[] = $this->server->event("server.tick", array($this, "onTick"));
@ -92,59 +91,67 @@ class Player{
} }
public function close($reason = "", $msg = true){ public function close($reason = "", $msg = true){
$reason = $reason == "" ? "server stop":$reason; if($this->connected === true){
$this->save(); foreach($this->evid as $ev){
foreach($this->evid as $ev){ $this->server->deleteEvent($ev);
$this->server->deleteEvent($ev); }
} $this->server->api->dhandle("player.quit", $this);
$this->eventHandler("You have been kicked. Reason: ".$reason, "server.chat"); $reason = $reason == "" ? "server stop":$reason;
$this->dataPacket(MC_LOGIN_STATUS, array( $this->save();
"status" => 1, $this->eventHandler(new Container("You have been kicked. Reason: ".$reason), "server.chat");
)); $this->dataPacket(MC_LOGIN_STATUS, array(
$this->dataPacket(MC_DISCONNECT); "status" => 1,
));
$this->dataPacket(MC_DISCONNECT);
$this->connected = false; $this->connected = false;
if($msg === true){ if($msg === true){
$this->server->trigger("server.chat", $this->username." left the game"); $this->server->api->chat->broadcast($this->username." left the game");
}
console("[INFO] Session with ".$this->ip.":".$this->port." Client ID ".$this->clientID." closed due to ".$reason);
$this->server->api->player->remove($this->CID);
} }
console("[INFO] Session with ".$this->ip.":".$this->port." Client ID ".$this->clientID." closed due to ".$reason);
$this->server->api->player->remove($this->CID);
} }
public function eventHandler($data, $event){ public function eventHandler($data, $event){
switch($event){ switch($event){
case "player.pickup":
if($data["eid"] === $this->eid){
$data["eid"] = 0;
}
$this->dataPacket(MC_TAKE_ITEM_ENTITY, $data);
break;
case "player.equipment.change": case "player.equipment.change":
if($data["eid"] === $this->eid){ if($data["eid"] === $this->eid){
break; break;
} }
$this->dataPacket(MC_PLAYER_EQUIPMENT, $data); $this->dataPacket(MC_PLAYER_EQUIPMENT, $data);
break; break;
case "world.block.change": case "block.change":
$this->dataPacket(MC_UPDATE_BLOCK, $data); $this->dataPacket(MC_UPDATE_BLOCK, $data);
break; break;
case "entity.move": case "entity.move":
if($data === $this->eid){ if($data->eid === $this->eid){
break; break;
} }
$entity = $this->server->entities[$data];
$this->dataPacket(MC_MOVE_ENTITY_POSROT, array( $this->dataPacket(MC_MOVE_ENTITY_POSROT, array(
"eid" => $data, "eid" => $data->eid,
"x" => $entity->x, "x" => $data->x,
"y" => $entity->y, "y" => $data->y,
"z" => $entity->z, "z" => $data->z,
"yaw" => $entity->yaw, "yaw" => $data->yaw,
"pitch" => $entity->pitch, "pitch" => $data->pitch,
)); ));
break; break;
case "entity.remove": case "entity.remove":
if($data === $this->eid){ if($data->eid === $this->eid){
break; break;
} }
$this->dataPacket(MC_ENTITY_REMOVE, array( $this->dataPacket(MC_REMOVE_ENTITY, array(
"eid" => $data, "eid" => $data->eid,
)); ));
break; break;
case "server.time.change": case "server.time":
$this->dataPacket(MC_SET_TIME, array( $this->dataPacket(MC_SET_TIME, array(
"time" => $data, "time" => $data,
)); ));
@ -159,8 +166,17 @@ class Player{
)); ));
break; break;
case "server.chat": case "server.chat":
if(($data instanceof Container) === true){
if(!$data->check($this->username)){
return;
}else{
$message = $data->get();
}
}else{
$message = (string) $data;
}
$this->dataPacket(MC_CHAT, array( $this->dataPacket(MC_CHAT, array(
"message" => str_replace("@username", $this->username, $data), "message" => str_replace("@username", $this->username, $message),
)); ));
break; break;
} }
@ -207,9 +223,21 @@ class Player{
)); ));
break; break;
case 0x80: case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84: case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88: case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c: case 0x8c:
case 0x8d:
case 0x8e:
case 0x8f:
if(isset($data[0])){ if(isset($data[0])){
$diff = $data[0] - $this->counter[1]; $diff = $data[0] - $this->counter[1];
if($diff > 1){ //Packet recovery if($diff > 1){ //Packet recovery
@ -223,8 +251,14 @@ class Player{
$this->send(0xc0, array(1, true, $data[0])); $this->send(0xc0, array(1, true, $data[0]));
} }
switch($data["id"]){ switch($data["id"]){
case MC_KEEP_ALIVE:
break;
case 0x03:
break;
case MC_DISCONNECT: case MC_DISCONNECT:
$this->connected = false;
$this->close("client disconnect"); $this->close("client disconnect");
break; break;
case MC_CLIENT_CONNECT: case MC_CLIENT_CONNECT:
@ -238,7 +272,7 @@ class Player{
break; break;
case MC_LOGIN: case MC_LOGIN:
$this->username = str_replace("/", "", $data["username"]); $this->username = str_replace(array("\x00", "/", " ", "\r", "\n"), array("", "-", "_", "", ""), $data["username"]);
if($this->username == ""){ if($this->username == ""){
$this->close("bad username", false); $this->close("bad username", false);
break; break;
@ -256,6 +290,10 @@ class Player{
if($c !== false){ if($c !== false){
$c->close("logged in from another location"); $c->close("logged in from another location");
} }
if($this->server->api->dhandle("player.join", $this) === false){
$this->close();
return;
}
$this->server->api->player->add($this->CID); $this->server->api->player->add($this->CID);
$this->auth = true; $this->auth = true;
$this->data["lastIP"] = $this->ip; $this->data["lastIP"] = $this->ip;
@ -275,35 +313,42 @@ class Player{
)); ));
break; break;
case MC_READY: case MC_READY:
if($this->spawned !== false){ switch($data["status"]){
break; case 1:
} if($this->spawned !== false){
$this->spawned = true; break;
$this->entity = $this->server->api->entity->add(ENTITY_PLAYER, 0, array("player" => $this)); }
$this->eid = $this->entity->eid; $this->spawned = true;
$this->server->query("UPDATE players SET EID = ".$this->eid." WHERE clientID = ".$this->clientID.";"); $this->entity = $this->server->api->entity->add(ENTITY_PLAYER, 0, array("player" => $this));
$this->entity->setName($this->username); $this->eid = $this->entity->eid;
$this->entity->data["clientID"] = $this->clientID; $this->server->query("UPDATE players SET EID = ".$this->eid." WHERE clientID = ".$this->clientID.";");
$this->server->api->entity->spawnAll($this); $this->entity->setName($this->username);
$this->server->api->entity->spawnToAll($this->eid); $this->entity->data["clientID"] = $this->clientID;
$this->evid[] = $this->server->event("server.time.change", array($this, "eventHandler")); $this->server->api->entity->spawnAll($this);
$this->evid[] = $this->server->event("server.chat", array($this, "eventHandler")); $this->server->api->entity->spawnToAll($this->eid);
$this->evid[] = $this->server->event("entity.remove", array($this, "eventHandler")); $this->evid[] = $this->server->event("server.timee", array($this, "eventHandler"));
$this->evid[] = $this->server->event("entity.move", array($this, "eventHandler")); $this->evid[] = $this->server->event("server.chat", array($this, "eventHandler"));
$this->evid[] = $this->server->event("entity.animate", array($this, "eventHandler")); $this->evid[] = $this->server->event("entity.remove", array($this, "eventHandler"));
$this->evid[] = $this->server->event("player.equipment.change", array($this, "eventHandler")); $this->evid[] = $this->server->event("entity.move", array($this, "eventHandler"));
$this->evid[] = $this->server->event("world.block.change", array($this, "eventHandler")); $this->evid[] = $this->server->event("entity.animate", array($this, "eventHandler"));
console("[DEBUG] Player with EID ".$this->eid." \"".$this->username."\" spawned!", true, true, 2); $this->evid[] = $this->server->event("player.equipment.change", array($this, "eventHandler"));
$this->evid[] = $this->server->event("player.pickup", array($this, "eventHandler"));
$this->evid[] = $this->server->event("block.change", array($this, "eventHandler"));
console("[DEBUG] Player with EID ".$this->eid." \"".$this->username."\" spawned!", true, true, 2);
$this->eventHandler($this->server->motd, "server.chat"); $this->eventHandler(new Container($this->server->motd), "server.chat");
if($this->MTU <= 548){ if($this->MTU <= 548){
$this->eventHandler("Your connection is bad, you may experience lag and slow map loading.", "server.chat"); $this->eventHandler("Your connection is bad, you may experience lag and slow map loading.", "server.chat");
}
break;
case 2://Chunk loaded?
break;
} }
break; break;
case MC_MOVE_PLAYER: case MC_MOVE_PLAYER:
if(is_object($this->entity)){ if(is_object($this->entity)){
$this->entity->setPosition($data["x"], $data["y"], $data["z"], $data["yaw"], $data["pitch"]); $this->entity->setPosition($data["x"], $data["y"], $data["z"], $data["yaw"], $data["pitch"]);
$this->server->trigger("entity.move", $this->eid); $this->server->api->dhandle("player.move", $this->entity);
} }
break; break;
case MC_PLAYER_EQUIPMENT: case MC_PLAYER_EQUIPMENT:
@ -330,31 +375,51 @@ class Player{
break; break;
case MC_USE_ITEM: case MC_USE_ITEM:
$data["eid"] = $this->eid; $data["eid"] = $this->eid;
if(Utils::distance($this->entity->position, $data) > 10){
break;
}
$this->server->handle("player.block.action", $data); $this->server->handle("player.block.action", $data);
break;
case MC_PLACE_BLOCK:
break; break;
case MC_REMOVE_BLOCK: case MC_REMOVE_BLOCK:
$data["eid"] = $this->eid; $data["eid"] = $this->eid;
if(Utils::distance($this->entity->position, $data) > 8){
break;
}
$this->server->handle("player.block.break", $data); $this->server->handle("player.block.break", $data);
break; break;
case MC_INTERACT: case MC_INTERACT:
if(isset($this->server->entities[$data["target"]]) and Utils::distance($this->entity->position, $this->server->entities[$data["target"]]->position) <= 8){ if(isset($this->server->entities[$data["target"]]) and Utils::distance($this->entity->position, $this->server->entities[$data["target"]]->position) <= 8){
console("[DEBUG] EID ".$this->eid." attacked EID ".$data["target"], true, true, 2); if($this->handle("player.interact", $data) !== false){
if($this->server->gamemode !== 1 and $this->server->difficulty > 0){ console("[DEBUG] EID ".$this->eid." attacked EID ".$data["target"], true, true, 2);
$this->server->api->entity->harm($data["target"], $this->server->difficulty, $this->eid); if($this->server->gamemode !== 1 and $this->server->difficulty > 0){
$this->server->api->entity->harm($data["target"], $this->server->difficulty, $this->eid);
}
} }
} }
break; break;
case MC_ANIMATE: case MC_ANIMATE:
$this->server->trigger("entity.animate", array("eid" => $this->eid, "action" => $data["action"])); $this->server->api->dhandle("entity.animate", array("eid" => $this->eid, "action" => $data["action"]));
break; break;
case MC_RESPAWN: case MC_RESPAWN:
$this->entity->invincible = true;
$this->entity->setHealth(20, "respawn"); $this->entity->setHealth(20, "respawn");
$this->entity->setPosition($data["x"], $data["y"], $data["z"], 0, 0); $this->entity->setPosition($data["x"], $data["y"], $data["z"], 0, 0);
$this->entity->invincible = false;
break;
case MC_SET_HEALTH:
if($this->server->gamemode === 1){
break;
}
//$this->entity->setHealth($data["health"], "client");
break;
case MC_DROP_ITEM:
if($this->server->handle("player.drop", $data) !== false){
$this->server->api->block->drop($this->entity->x, $this->entity->y, $this->entity->z, $data["block"], $data["meta"], $data["stack"]);
}
break;
default:
console("[DEBUG] Unhandled 0x".dechex($data["id"])." Data Packet for Client ID ".$this->clientID.": ".print_r($data, true), true, true, 2);
break; break;
} }
break; break;
} }

View File

@ -25,28 +25,31 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
class PocketMinecraftServer extends stdClass{ class PocketMinecraftServer{
var $invisible, $tickMeasure, $preparedSQL, $seed, $protocol, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond, $responses, $spawn, $entities, $mapDir, $mapName, $map, $level, $tileEntities; var $version, $invisible, $api, $tickMeasure, $preparedSQL, $seed, $gamemode, $name, $maxClients, $clients, $eidCnt, $custom, $description, $motd, $timePerSecond, $spawn, $entities, $mapDir, $mapName, $map, $levelData, $tileEntities;
private $database, $interface, $evCnt, $handCnt, $events, $handlers, $version, $serverType, $lastTick; private $database, $interface, $evCnt, $handCnt, $events, $handlers, $serverType, $lastTick, $ticker;
function __construct($name, $gamemode = 1, $seed = false, $protocol = CURRENT_PROTOCOL, $port = 19132, $serverID = false, $version = CURRENT_VERSION){
$this->port = (int) $port; //19132 - 19135 private function load(){
console("[INFO] PocketMine-MP ".MAJOR_VERSION." by @shoghicp, LGPL License. http://bit.ly/TbrimG", true, true, 0); $this->version = new VersionString();
console("[INFO] PocketMine-MP ".MAJOR_VERSION." #".$this->version->getNumber()." by @shoghicp, LGPL License", true, true, 0);
console("[DEBUG] Target Minecraft PE: ".CURRENT_MINECRAFT_VERSION.", protocol #".CURRENT_PROTOCOL, true, true, 2);
console("[INFO] Starting Minecraft PE Server at *:".$this->port); console("[INFO] Starting Minecraft PE Server at *:".$this->port);
if($this->port < 19132 or $this->port > 19135){ if($this->port < 19132 or $this->port > 19135){
console("[WARNING] You've selected a not-standard port. Normal port range is from 19132 to 19135 included"); console("[WARNING] You've selected a not-standard port. Normal port range is from 19132 to 19135 included");
} }
$this->serverID = $this->serverID === false ? Utils::readLong(Utils::getRandomBytes(8)):$this->serverID;
$this->seed = $this->seed === false ? Utils::readInt(Utils::getRandomBytes(4)):$this->seed;
console("[INFO] Loading database..."); console("[INFO] Loading database...");
$this->startDatabase(); $this->startDatabase();
$this->gamemode = (int) $gamemode; $this->doTick = false;
$this->version = (int) $version; $this->api = false;
$this->name = $name;
$this->mapDir = false; $this->mapDir = false;
$this->mapName = false; $this->mapName = false;
$this->events = array(); $this->events = array();
$this->handlers = array(); $this->handlers = array();
$this->map = false; $this->map = false;
$this->invisible = false; $this->invisible = false;
$this->level = false; $this->levelData = false;
$this->difficulty = 1; $this->difficulty = 1;
$this->tileEntities = array(); $this->tileEntities = array();
$this->entities = array(); $this->entities = array();
@ -60,25 +63,29 @@ class PocketMinecraftServer extends stdClass{
$this->description = ""; $this->description = "";
$this->whitelist = false; $this->whitelist = false;
$this->bannedIPs = array(); $this->bannedIPs = array();
$this->motd = "Welcome to ".$name;
$this->serverID = $serverID === false ? Utils::readLong(Utils::getRandomBytes(8)):$serverID;
$this->seed = $seed === false ? Utils::readInt(Utils::getRandomBytes(4)):$seed;
$this->clients = array(); $this->clients = array();
$this->protocol = (int) $protocol;
$this->spawn = array("x" => 128.5,"y" => 100,"z" => 128.5); $this->spawn = array("x" => 128.5,"y" => 100,"z" => 128.5);
$this->time = 0; $this->time = 0;
$this->timePerSecond = 10; $this->timePerSecond = 10;
$this->tickMeasure = array_fill(0, 40, 0); $this->tickMeasure = array_fill(0, 40, 0);
$this->setType("normal"); $this->setType("normal");
$this->interface = new MinecraftInterface("255.255.255.255", $this->protocol, $this->port, true, false); $this->interface = new MinecraftInterface("255.255.255.255", $this->port, true, false);
$this->reloadConfig(); $this->reloadConfig();
console("[INFO] Server Name: ".$this->name); console("[INFO] Server Name: ".$this->name);
console("[INFO] Server GUID: ".$this->serverID); console("[DEBUG] Server GUID: ".$this->serverID, true, true, 2);
console("[INFO] Protocol Version: ".$this->protocol);
console("[INFO] Max Clients: ".$this->maxClients);
$this->stop = false; $this->stop = false;
} }
function __construct($name, $gamemode = 1, $seed = false, $port = 19132, $serverID = false){
$this->port = (int) $port; //19132 - 19135
$this->gamemode = (int) $gamemode;
$this->name = $name;
$this->motd = "Welcome to ".$name;
$this->serverID = $serverID;
$this->seed = $seed;
$this->load();
}
public function getTPS(){ public function getTPS(){
$v = array_values($this->tickMeasure); $v = array_values($this->tickMeasure);
$tps = 40 / ($v[39] - $v[0]); $tps = 40 / ($v[39] - $v[0]);
@ -86,17 +93,15 @@ class PocketMinecraftServer extends stdClass{
} }
public function loadEvents(){ public function loadEvents(){
$this->event("server.chat", array($this, "eventHandler")); $this->event("player.add", array($this, "eventHandler"));
$this->event("player.new", array($this, "eventHandler")); $this->action(500000, '$this->time += (int) ($this->timePerSecond / 2);$this->api->dhandle("server.time", $this->time);');
$this->action(5000000, 'if($this->difficulty < 2){$this->api->dhandle("server.regeneration", 1);}');
$this->action(500000, '$this->time += (int) ($this->timePerSecond / 2);$this->trigger("server.time.change", $this->time);');
$this->action(5000000, 'if($this->difficulty < 2){$this->trigger("server.regeneration", 1);}');
$this->action(1000000 * 60, '$this->reloadConfig();'); $this->action(1000000 * 60, '$this->reloadConfig();');
$this->action(1000000 * 60 * 10, '$this->custom = array();'); $this->action(1000000 * 60 * 10, '$this->custom = array();');
if($this->api !== false){ if($this->api !== false){
$this->action(1000000 * 80, '$this->chat(false, count($this->clients)."/".$this->maxClients." online: ".implode(", ",$this->api->player->online()));'); $this->action(1000000 * 80, '$cnt = count($this->clients); if($cnt > 1){$this->api->chat->broadcast("Online (".$cnt."): ".implode(", ",$this->api->player->online()));}');
} }
$this->action(1000000 * 75, '$this->debugInfo(true);'); $this->action(1000000 * 120, '$this->debugInfo(true);');
} }
public function startDatabase(){ public function startDatabase(){
@ -112,8 +117,8 @@ class PocketMinecraftServer extends stdClass{
$this->query("CREATE TABLE events (ID INTEGER PRIMARY KEY, name TEXT);"); $this->query("CREATE TABLE events (ID INTEGER PRIMARY KEY, name TEXT);");
$this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);"); $this->query("CREATE TABLE handlers (ID INTEGER PRIMARY KEY, name TEXT, priority NUMERIC);");
//$this->query("PRAGMA synchronous = OFF;"); //$this->query("PRAGMA synchronous = OFF;");
$this->preparedSQL->selectHandlers = $this->database->prepare("SELECT ID FROM handlers WHERE name = :name ORDER BY priority DESC;"); $this->preparedSQL->selectHandlers = $this->database->prepare("SELECT DISTINCT ID FROM handlers WHERE name = :name ORDER BY priority DESC;");
$this->preparedSQL->selectEvents = $this->database->prepare("SELECT ID FROM events WHERE name = :name;"); $this->preparedSQL->selectEvents = $this->database->prepare("SELECT DISTINCT ID FROM events WHERE name = :name;");
$this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);"); $this->preparedSQL->selectActions = $this->database->prepare("SELECT ID,code,repeat FROM actions WHERE last <= (:time - interval);");
$this->preparedSQL->updateActions = $this->database->prepare("UPDATE actions SET last = :time WHERE last <= (:time - interval);"); $this->preparedSQL->updateActions = $this->database->prepare("UPDATE actions SET last = :time WHERE last <= (:time - interval);");
} }
@ -146,6 +151,7 @@ class PocketMinecraftServer extends stdClass{
$info["actions"] = $this->query("SELECT count(ID) as count FROM actions;", true); $info["actions"] = $this->query("SELECT count(ID) as count FROM actions;", true);
$info["actions"] = $info["actions"]["count"]; $info["actions"] = $info["actions"]["count"];
$info["garbage"] = gc_collect_cycles(); $info["garbage"] = gc_collect_cycles();
$this->handle("server.debug", $info);
if($console === true){ if($console === true){
console("[DEBUG] TPS: ".$info["tps"].", Memory usage: ".$info["memory_usage"]." (Peak ".$info["memory_peak_usage"]."), Entities: ".$info["entities"].", Events: ".$info["events"].", Actions: ".$info["actions"].", Garbage: ".$info["garbage"], true, true, 2); console("[DEBUG] TPS: ".$info["tps"].", Memory usage: ".$info["memory_usage"]." (Peak ".$info["memory_peak_usage"]."), Entities: ".$info["entities"].", Events: ".$info["events"].", Actions: ".$info["actions"].", Garbage: ".$info["garbage"], true, true, 2);
} }
@ -154,21 +160,17 @@ class PocketMinecraftServer extends stdClass{
public function close($reason = "stop"){ public function close($reason = "stop"){
if($this->stop !== true){ if($this->stop !== true){
$this->chat(false, "Stopping server..."); $this->api->chat->send(false, "Stopping server...");
$this->save(); $this->ticker->stop = true;
$this->save(true);
$this->stop = true; $this->stop = true;
$this->trigger("server.close"); $this->trigger("server.close", $reason);
$this->interface->close(); $this->interface->close();
} }
} }
public function chat($owner, $text, $target = true){ public function chat($owner, $text, $target = false){
$message = ""; $this->api->chat->send($owner, $text, $target);
if($owner !== false){
$message = "<".$owner."> ";
}
$message .= $text;
$this->trigger("server.chat", $message);
} }
public function setType($type = "normal"){ public function setType($type = "normal"){
@ -201,17 +203,27 @@ class PocketMinecraftServer extends stdClass{
$handlers = $this->preparedSQL->selectHandlers->execute(); $handlers = $this->preparedSQL->selectHandlers->execute();
$result = true; $result = true;
if($handlers !== false and $handlers !== true){ if($handlers !== false and $handlers !== true){
while(false !== ($hn = $handlers->fetchArray(SQLITE3_ASSOC)) and $result !== false){ console("[INTERNAL] Handling ".$event, true, true, 3);
$handler = $this->handlers[(int) $hn["ID"]]; $call = array();
if(is_array($handler)){ while(($hn = $handlers->fetchArray(SQLITE3_ASSOC)) !== false){
$method = $handler[1]; $call[(int) $hn["ID"]] = true;
$result = $handler[0]->$method($data, $event); }
$handlers->finalize();
foreach($call as $hnid => $boolean){
if($result !== false){
$called[$hnid] = true;
$handler = $this->handlers[$hnid];
if(is_array($handler)){
$method = $handler[1];
$result = $handler[0]->$method($data, $event);
}else{
$result = $handler($data, $event);
}
}else{ }else{
$result = $handler($data, $event); break;
} }
} }
} }
$handlers->finalize();
if($result !== false){ if($result !== false){
$this->trigger($event, $data); $this->trigger($event, $data);
} }
@ -220,30 +232,27 @@ class PocketMinecraftServer extends stdClass{
public function eventHandler($data, $event){ public function eventHandler($data, $event){
switch($event){ switch($event){
case "player.new": case "player.add":
console("[DEBUG] Player \"".$data["username"]."\" EID ".$data["eid"]." spawned at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2); console("[DEBUG] Player \"".$data["username"]."\" EID ".$data["eid"]." spawned at X ".$data["x"]." Y ".$data["y"]." Z ".$data["z"], true, true, 2);
break; break;
case "server.chat":
console("[CHAT] $data");
break;
} }
} }
public function loadMap(){ public function loadMap(){
if($this->mapName !== false and trim($this->mapName) !== ""){ if($this->mapName !== false and trim($this->mapName) !== ""){
$this->level = unserialize(file_get_contents($this->mapDir."level.dat")); $this->levelData = unserialize(file_get_contents($this->mapDir."level.dat"));
console("[INFO] Map: ".$this->level["LevelName"]); console("[INFO] Map: ".$this->levelData["LevelName"]);
$this->time = (int) $this->level["Time"]; $this->time = (int) $this->levelData["Time"];
$this->seed = (int) $this->level["RandomSeed"]; $this->seed = (int) $this->levelData["RandomSeed"];
if(isset($this->level["SpawnX"])){ if(isset($this->levelData["SpawnX"])){
$this->spawn = array("x" => $this->level["SpawnX"], "y" => $this->level["SpawnY"], "z" => $this->level["SpawnZ"]); $this->spawn = array("x" => $this->levelData["SpawnX"], "y" => $this->levelData["SpawnY"], "z" => $this->levelData["SpawnZ"]);
}else{ }else{
$this->level["SpawnX"] = $this->spawn["x"]; $this->levelData["SpawnX"] = $this->spawn["x"];
$this->level["SpawnY"] = $this->spawn["y"]; $this->levelData["SpawnY"] = $this->spawn["y"];
$this->level["SpawnZ"] = $this->spawn["z"]; $this->levelData["SpawnZ"] = $this->spawn["z"];
} }
$this->level["Time"] = &$this->time; $this->levelData["Time"] = $this->time;
console("[INFO] Spawn: X ".$this->level["SpawnX"]." Y ".$this->level["SpawnY"]." Z ".$this->level["SpawnZ"]); console("[INFO] Spawn: X ".$this->levelData["SpawnX"]." Y ".$this->levelData["SpawnY"]." Z ".$this->levelData["SpawnZ"]);
console("[INFO] Time: ".$this->time); console("[INFO] Time: ".$this->time);
console("[INFO] Seed: ".$this->seed); console("[INFO] Seed: ".$this->seed);
console("[INFO] Gamemode: ".($this->gamemode === 0 ? "survival":"creative")); console("[INFO] Gamemode: ".($this->gamemode === 0 ? "survival":"creative"));
@ -252,7 +261,7 @@ class PocketMinecraftServer extends stdClass{
console("[INFO] Loading map..."); console("[INFO] Loading map...");
$this->map = new ChunkParser(); $this->map = new ChunkParser();
if(!$this->map->loadFile($this->mapDir."chunks.dat")){ if(!$this->map->loadFile($this->mapDir."chunks.dat")){
console("[ERROR] Couldn't load the map \"".$this->level["LevelName"]."\"!", true, true, 0); console("[ERROR] Couldn't load the map \"".$this->levelData["LevelName"]."\"!", true, true, 0);
$this->map = false; $this->map = false;
}else{ }else{
$this->map->loadMap(); $this->map->loadMap();
@ -292,37 +301,71 @@ class PocketMinecraftServer extends stdClass{
} }
} }
console("[DEBUG] Loaded ".count($this->entities)." Entities", true, true, 2); console("[DEBUG] Loaded ".count($this->entities)." Entities", true, true, 2);
$this->action(1000000 * 60 * 15, '$this->chat(false, "Forcing save...");$this->save();$this->chat(false, "Done");'); $this->action(1000000 * 60 * 15, '$this->api->chat->broadcast("Forcing save...");$this->save();');
} }
} }
public function save(){ public function save($final = false){
if($this->mapName !== false){ if($this->mapName !== false){
file_put_contents($this->mapDir."level.dat", serialize($this->level)); $this->levelData["Time"] = $this->time;
$this->map->saveMap(); file_put_contents($this->mapDir."level.dat", serialize($this->levelData));
$this->map->saveMap($final);
console("[INFO] Saving entities..."); console("[INFO] Saving entities...");
foreach($this->entities as $entity){ foreach($this->entities as $entity){
} }
$this->trigger("server.save", $final);
} }
} }
public function start(){ public function init(){
if($this->mapName !== false and $this->map === false){ if($this->mapName !== false and $this->map === false){
$this->loadMap(); $this->loadMap();
$this->loadEntities(); $this->loadEntities();
} }
console("[INFO] Loading events..."); console("[INFO] Loading events...");
$this->loadEvents(); $this->loadEvents();
$this->ticker = new TickLoop($this);
$this->ticker->start();
declare(ticks=15); declare(ticks=15);
register_tick_function(array($this, "tick")); register_tick_function(array($this, "tick"));
register_shutdown_function(array($this, "dumpError"));
register_shutdown_function(array($this, "close")); register_shutdown_function(array($this, "close"));
$this->trigger("server.start", microtime(true)); $this->trigger("server.start", microtime(true));
console("[INFO] Server started!"); console("[INFO] Server started!");
$this->process(); $this->process();
} }
public function dumpError(){
console("[ERROR] An Unrecovereable has ocurred and the server has Crashed. Creating an Error Dump");
$dump = "# PocketMine-MP Error Dump ".date("D M j H:i:s T Y")."\r\n";
$dump .= "Error: ".var_export(error_get_last(), true)."\r\n\r\n";
$version = new VersionString();
$dump .= "PM Version: ".$version." #".$version->getNumber()." [Protocol ".CURRENT_PROTOCOL."]\r\n";
$dump .= "uname -a: ".php_uname("a")."\r\n";
$dump .= "PHP Version: " .phpversion()."\r\n";
$dump .= "Zend version: ".zend_version()."\r\n";
$dump .= "OS : " .PHP_OS.", ".Utils::getOS()."\r\n";
$dump .= "Debug Info: ".var_export($this->debugInfo(false), true)."\r\n\r\n\r\n";
global $arguments;
$dump .= "Parameters: ".var_export($arguments, true)."\r\n\r\n\r\n";
$dump .= "server.properties: ".var_export($this->api->getProperties(), true)."\r\n\r\n\r\n";
$dump .= "Loaded Modules: ".var_export(get_loaded_extensions(), true)."\r\n\r\n";
$name = "error_dump_".time();
logg($dump, $name, true, 0, true);
console("[ERROR] Please submit the \"logs/{$name}.log\" file to the Bug Reporting page. Give as much info as you can.", true, true, 0);
}
public function tick(){ public function tick(){
/*if($this->ticker->tick === true and $this->ticker->isWaiting() === true){
$this->ticker->tick = false;
$time = microtime(true);
array_shift($this->tickMeasure);
$this->tickMeasure[] = $this->lastTick = $time;
$this->tickerFunction($time);
$this->trigger("server.tick", $time);
$this->ticker->notify();
}*/
$time = microtime(true); $time = microtime(true);
if($this->lastTick <= ($time - 0.05)){ if($this->lastTick <= ($time - 0.05)){
array_shift($this->tickMeasure); array_shift($this->tickMeasure);
@ -333,7 +376,7 @@ class PocketMinecraftServer extends stdClass{
} }
public function clientID($ip, $port){ public function clientID($ip, $port){
return md5($pi . $port, true); return md5($ip . $port, true);
} }
public function packetHandler($packet){ public function packetHandler($packet){
@ -342,6 +385,9 @@ class PocketMinecraftServer extends stdClass{
if(isset($this->clients[$CID])){ if(isset($this->clients[$CID])){
$this->clients[$CID]->handle($packet["pid"], $data); $this->clients[$CID]->handle($packet["pid"], $data);
}else{ }else{
if($this->handle("server.noauthpacket", $packet) === false){
return;
}
switch($packet["pid"]){ switch($packet["pid"]){
case 0x02: case 0x02:
if($this->invisible === true){ if($this->invisible === true){
@ -397,9 +443,9 @@ class PocketMinecraftServer extends stdClass{
} }
$version = $data[1]; $version = $data[1];
$size = strlen($data[2]); $size = strlen($data[2]);
if($version !== $this->protocol){ if($version !== CURRENT_PROTOCOL){
$this->send(0x1a, array( $this->send(0x1a, array(
$this->protocol, CURRENT_PROTOCOL,
MAGIC, MAGIC,
$this->serverID, $this->serverID,
), false, $packet["ip"], $packet["port"]); ), false, $packet["ip"], $packet["port"]);
@ -464,26 +510,26 @@ class PocketMinecraftServer extends stdClass{
if($events === false or $events === true){ if($events === false or $events === true){
return; return;
} }
while(false !== ($evn = $events->fetchArray(SQLITE3_ASSOC))){ $call = array();
$ev = $this->events[(int) $evn["ID"]]; while(($evn = $events->fetchArray(SQLITE3_ASSOC)) !== false){
if(is_array($ev)){ $call[(int) $evn["ID"]] = true;
$method = $ev[1];
$this->responses[(int) $evn["ID"]] = $ev[0]->$method($data, $event);
}else{
$this->responses[(int) $evn["ID"]] = $ev($data, $event);
}
} }
$events->finalize(); $events->finalize();
return true; foreach($call as $evid => $boolean){
} $ev = $this->events[$evid];
if(!is_callable($ev)){
public function response($eid){ $this->deleteEvent($evid);
if(isset($this->responses[$eid])){ continue;
$res = $this->responses[$eid]; }
unset($this->responses[$eid]); if(is_array($ev)){
return $res; $method = $ev[1];
$ev[0]->$method($data, $event);
}else{
$ev($data, $event);
}
} }
return false;
return true;
} }
public function schedule($ticks, $callback, $data = array(), $repeat = false, $eventName = "server.schedule"){ public function schedule($ticks, $callback, $data = array(), $repeat = false, $eventName = "server.schedule"){
@ -495,7 +541,7 @@ class PocketMinecraftServer extends stdClass{
$add = ' unset($this->schedule['.$this->scheduleCnt.']);'; $add = ' unset($this->schedule['.$this->scheduleCnt.']);';
} }
$this->schedule[$this->scheduleCnt] = array($callback, $data, $eventName); $this->schedule[$this->scheduleCnt] = array($callback, $data, $eventName);
$this->action(50000 * $ticks, '$schedule = $this->schedule['.$this->scheduleCnt.'];'.$add.' call_user_func($schedule[0], $schedule[1], $schedule[2]);', (bool) $repeat); $this->action(50000 * $ticks, '$schedule = $this->schedule['.$this->scheduleCnt.'];'.$add.'if(!is_callable($schedule[0])){unset($this->schedule['.$this->scheduleCnt.']);return false;} call_user_func($schedule[0], $schedule[1], $schedule[2]);', (bool) $repeat);
return $this->scheduleCnt++; return $this->scheduleCnt++;
} }
@ -515,8 +561,8 @@ class PocketMinecraftServer extends stdClass{
return; return;
} }
while(false !== ($action = $actions->fetchArray(SQLITE3_ASSOC))){ while(false !== ($action = $actions->fetchArray(SQLITE3_ASSOC))){
eval(base64_decode($action["code"])); $return = eval(base64_decode($action["code"]));
if($action["repeat"] === 0){ if($action["repeat"] === 0 or $return === false){
$this->query("DELETE FROM actions WHERE ID = ".$action["ID"].";"); $this->query("DELETE FROM actions WHERE ID = ".$action["ID"].";");
} }
} }

View File

@ -62,7 +62,7 @@ class SerializedPacketHandler{
$len = ceil(Utils::readShort($this->get(2), false) / 8); //Utils::readShort($this->get(2), false) >> 3; $len = ceil(Utils::readShort($this->get(2), false) / 8); //Utils::readShort($this->get(2), false) >> 3;
if($pid !== 0x00){ if($pid !== 0x00){
$c = Utils::readTriad($this->get(3)); $c = Utils::readTriad(strrev($this->get(3)));
} }
if($pid === 0x60 and $i === 0){ if($pid === 0x60 and $i === 0){
$this->data["unknown1"] = $this->get(4); $this->data["unknown1"] = $this->get(4);

View File

@ -25,11 +25,12 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
$fp = fopen(dirname(__FILE__)."/console.in","wb"); class TileEntity extends stdClass{
while(true){ private $server;
$l = fgets(STDIN); function __construct(PocketMinecraftServer $server){
fwrite($fp, $l);
if(strtolower(trim($l)) === "stop" and isset($argv[1]) and trim($argv[1]) == "1"){
die();
} }
} }
?>

View File

@ -30,7 +30,6 @@ the Free Software Foundation, either version 3 of the License, or
class UDPSocket{ class UDPSocket{
private $encrypt; private $encrypt;
var $buffer, $connected, $errors, $sock, $server; var $buffer, $connected, $errors, $sock, $server;
function __construct($server, $port, $listen = false, $socket = false){ function __construct($server, $port, $listen = false, $socket = false){
$this->errors = array_fill(88,(125 - 88) + 1, true); $this->errors = array_fill(88,(125 - 88) + 1, true);
$this->server = $server; $this->server = $server;
@ -58,7 +57,7 @@ class UDPSocket{
} }
} }
function listenSocket(){ public function listenSocket(){
$sock = @socket_accept($this->sock); $sock = @socket_accept($this->sock);
if($sock !== false){ if($sock !== false){
$sock = new Socket(false, false, false, $sock); $sock = new Socket(false, false, false, $sock);

View File

@ -35,12 +35,33 @@ define("BIG_ENDIAN", 0x00);
define("LITTLE_ENDIAN", 0x01); define("LITTLE_ENDIAN", 0x01);
define("ENDIANNESS", (pack("d", 1) === "\77\360\0\0\0\0\0\0" ? BIG_ENDIAN:LITTLE_ENDIAN)); define("ENDIANNESS", (pack("d", 1) === "\77\360\0\0\0\0\0\0" ? BIG_ENDIAN:LITTLE_ENDIAN));
abstract class Utils{ class Utils extends Thread{
public function run(){
}
public static function getIP(){
$ip = trim(Utils::curl_get("http://automation.whatismyip.com/n09230945.asp"));
if($ip != ""){
return $ip;
}else{
$ip = trim(Utils::curl_get("http://checkip.dyndns.org/"));
$ip = preg_replace("/Current IP Address: (.*)/", '$1', $ip);
if($ip != ""){
return $ip;
}else{
return false;
}
}
}
public static function getOS(){ public static function getOS(){
$uname = strtoupper(php_uname("s")); $uname = strtoupper(php_uname("s"));
if(strpos($uname, "WIN") !== false){ if(strpos($uname, "WIN") !== false){
return "win"; return "win";
}elseif(strpos($uname, "DARWIN") !== false){
return "mac";
}else{ }else{
return "linux"; return "linux";
} }
@ -304,7 +325,7 @@ abstract class Utils{
public static function curl_get($page){ public static function curl_get($page){
$ch = curl_init($page); $ch = curl_init($page);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Minecraft PHP Client 2")); curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP"));
curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
@ -322,7 +343,7 @@ abstract class Utils{
curl_setopt($ch, CURLOPT_POSTFIELDS, $args); curl_setopt($ch, CURLOPT_POSTFIELDS, $args);
curl_setopt($ch, CURLOPT_AUTOREFERER, true); curl_setopt($ch, CURLOPT_AUTOREFERER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Minecraft PHP Client 2")); curl_setopt($ch, CURLOPT_HTTPHEADER, array("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 PocketMine-MP"));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout);
$ret = curl_exec($ch); $ret = curl_exec($ch);

View File

@ -25,16 +25,15 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
define("WINDOW_CHEST", 0);
define("WINDOW_WORKBENCH", 1);
define("WINDOW_FURNACE", 2);
class TreeObject{ class Window{
private $server;
public function __construct(PocketMinecraftServer $server){
public static function growTree(LevelAPI $level, $block, $type){
switch($type){
default:
case Sapling::OAK:
$tree = new SmallTreeObject();
break;
}
$tree->placeObject($level, $block[2][0], $block[2][1], $block[2][2], $type);
} }
} }
?>

View File

@ -32,12 +32,12 @@ error_reporting(E_ALL ^ E_NOTICE);
ini_set("allow_url_fopen", 1); ini_set("allow_url_fopen", 1);
ini_set("display_errors", 1); ini_set("display_errors", 1);
ini_set('default_charset', 'utf-8'); ini_set('default_charset', 'utf-8');
define("FILE_PATH", dirname(__FILE__)."/../"); define("FILE_PATH", dirname(__FILE__)."/../../");
set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/classes/"); set_include_path(get_include_path() . PATH_SEPARATOR . FILE_PATH . PATH_SEPARATOR . FILE_PATH . "/src/" . PATH_SEPARATOR . FILE_PATH . "/src/classes/");
ini_set("memory_limit", "256M"); ini_set("memory_limit", "512M");
define("CURRENT_PROTOCOL", 5);
define("CURRENT_VERSION", 1);
define("LOG", true); define("LOG", true);
define("MAGIC", "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78"); define("MAGIC", "\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78");
define("TEST_MD5", "5ca8eced50a5801619f7ae86d631a4e7"); define("TEST_MD5", "d0ca3786e53b615bb4fb9f5094d5c9a7");
define("MAJOR_VERSION", "Alpha_1.0.1"); define("MAJOR_VERSION", "Alpha_1.0.6");
define("CURRENT_PROTOCOL", 5);
define("CURRENT_MINECRAFT_VERSION", "v0.5.0 alpha");

View File

@ -25,7 +25,6 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
require_once(dirname(__FILE__)."/config.php"); require_once(dirname(__FILE__)."/config.php");
require_once("common/functions.php"); require_once("common/functions.php");
//set_error_handler("error_handler"); //set_error_handler("error_handler");
@ -35,6 +34,9 @@ $errors = 0;
if(version_compare("5.3.3", PHP_VERSION) > 0){ if(version_compare("5.3.3", PHP_VERSION) > 0){
console("[ERROR] Use PHP >= 5.3.3", true, true, 0); console("[ERROR] Use PHP >= 5.3.3", true, true, 0);
++$errors; ++$errors;
}elseif(version_compare("5.5.0", PHP_VERSION) <= 0){
console("[NOTICE] PocketMine-MP hasn't been tested with PHP >= 5.5", true, true, 0);
++$errors;
} }
if(version_compare("5.4.0", PHP_VERSION) > 0){ if(version_compare("5.4.0", PHP_VERSION) > 0){
@ -49,27 +51,39 @@ if(php_sapi_name() !== "cli"){
++$errors; ++$errors;
} }
if(!extension_loaded("sockets")){ if(!extension_loaded("sockets") and @dl((PHP_SHLIB_SUFFIX === "dll" ? "php_":"") . "sockets." . PHP_SHLIB_SUFFIX) === false){
console("[ERROR] Unable to find Socket extension", true, true, 0); console("[ERROR] Unable to find Socket extension", true, true, 0);
++$errors; ++$errors;
} }
if(!extension_loaded("curl")){ if(!extension_loaded("pthreads") and @dl((PHP_SHLIB_SUFFIX === "dll" ? "php_":"") . "pthreads." . PHP_SHLIB_SUFFIX) === false){
console("[ERROR] Unable to find pthreads extension. Use the Installer available in the Homepage", true, true, 0);
++$errors;
}
if(!extension_loaded("curl") and @dl((PHP_SHLIB_SUFFIX === "dll" ? "php_":"") . "curl." . PHP_SHLIB_SUFFIX) === false){
console("[ERROR] Unable to find cURL extension", true, true, 0); console("[ERROR] Unable to find cURL extension", true, true, 0);
++$errors; ++$errors;
} }
if(!extension_loaded("sqlite3")){ if(!extension_loaded("sqlite3") and @dl((PHP_SHLIB_SUFFIX === "dll" ? "php_":"") . "sqlite3." . PHP_SHLIB_SUFFIX) === false){
console("[ERROR] Unable to find SQLite3 extension", true, true, 0); console("[ERROR] Unable to find SQLite3 extension", true, true, 0);
++$errors; ++$errors;
} }
if(!extension_loaded("zlib") and @dl((PHP_SHLIB_SUFFIX === "dll" ? "php_":"") . "zlib." . PHP_SHLIB_SUFFIX) === false){
console("[ERROR] Unable to find Zlib extension. Compressed worlds won't be loaded", true, true, 0);
define("ZLIB_EXTENSION", false);
}else{
define("ZLIB_EXTENSION", true);
}
if($errors > 0){ if($errors > 0){
die(); die();
} }
require_once("classes/Async.class.php");
require_once("classes/Data.class.php"); require_once("classes/Data.class.php");
require_once("classes/Player.class.php"); require_once("classes/Player.class.php");
require_once("classes/Generator.class.php"); require_once("classes/Generator.class.php");
@ -78,6 +92,8 @@ require_once("classes/Utils.class.php");
require_once("classes/UDPSocket.class.php"); require_once("classes/UDPSocket.class.php");
require_once("classes/Packet.class.php"); require_once("classes/Packet.class.php");
require_once("classes/Entity.class.php"); require_once("classes/Entity.class.php");
require_once("classes/TileEntity.class.php");
require_once("classes/Window.class.php");
require_once("classes/ChunkParser.class.php"); require_once("classes/ChunkParser.class.php");
require_once("classes/NBT.class.php"); require_once("classes/NBT.class.php");
require_once("classes/Java.class.php"); require_once("classes/Java.class.php");
@ -85,6 +101,6 @@ require_once("classes/SerializedPacketHandler.class.php");
require_once("classes/CustomPacketHandler.class.php"); require_once("classes/CustomPacketHandler.class.php");
require_once("classes/MinecraftInterface.class.php"); require_once("classes/MinecraftInterface.class.php");
require_once("classes/BigInteger.class.php"); require_once("classes/BigInteger.class.php");
require_all("misc/"); require_all(FILE_PATH . "src/misc/");
?> ?>

View File

@ -26,6 +26,24 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
function kill($pid){
switch(Utils::getOS()){
case "win":
ob_start();
passthru("%WINDIR%\\System32\\taskkill.exe /F /PID ".((int) $pid));
ob_end_clean();
break;
case "mac":
case "linux":
default:
ob_start();
passthru("kill -9 ".((int) $pid));
ob_end_clean();
}
}
function require_all($path, &$count = 0){ function require_all($path, &$count = 0){
$dir = dir($path."/"); $dir = dir($path."/");
while(false !== ($file = $dir->read())){ while(false !== ($file = $dir->read())){
@ -89,7 +107,7 @@ function parseNBTData($data){
} }
function arg($name, $default){ function arg($name, $default = false){
global $arguments, $argv; global $arguments, $argv;
if(!isset($arguments)){ if(!isset($arguments)){
$arguments = arguments($argv); $arguments = arguments($argv);
@ -109,7 +127,7 @@ function arguments ( $args ){
array_shift( $args ); array_shift( $args );
$args = join( $args, ' ' ); $args = join( $args, ' ' );
preg_match_all('/ (--\w+ (?:[= ] [^-]+ [^\s-] )? ) | (-\w+) | (\w+) /x', $args, $match ); preg_match_all('/ (--[\w\-]+ (?:[= ] [^-]+ [^\s-] )? ) | (-\w+) | (\w+) /x', $args, $match );
$args = array_shift( $match ); $args = array_shift( $match );
$ret = array( $ret = array(
@ -147,7 +165,6 @@ function arguments ( $args ){
} }
function console($message, $EOL = true, $log = true, $level = 1){ function console($message, $EOL = true, $log = true, $level = 1){
//global $path;
if(!defined("DEBUG") or DEBUG >= $level){ if(!defined("DEBUG") or DEBUG >= $level){
$message .= $EOL === true ? PHP_EOL:""; $message .= $EOL === true ? PHP_EOL:"";
$message = date("H:i:s"). " ". $message; $message = date("H:i:s"). " ". $message;
@ -169,10 +186,10 @@ function logg($message, $name, $EOL = true, $level = 2, $close = false){
if(!isset($fpointers)){ if(!isset($fpointers)){
$fpointers = array(); $fpointers = array();
} }
if(!isset($fpointers[$name])){ if(!isset($fpointers[$name]) or $fpointers[$name] === false){
$fpointers[$name] = fopen(FILE_PATH."/".$name.".log", "ab"); $fpointers[$name] = @fopen(FILE_PATH."logs/".$name.".log", "ab");
} }
fwrite($fpointers[$name], $message); @fwrite($fpointers[$name], $message);
if($close === true){ if($close === true){
fclose($fpointers[$name]); fclose($fpointers[$name]);
unset($fpointers[$name]); unset($fpointers[$name]);

View File

@ -24,8 +24,8 @@ the Free Software Foundation, either version 3 of the License, or
*/ */
/*
class ChatAPI{ class BlockIterator implements Iterator{
} }*/

110
src/misc/math/Vector2.php Normal file
View File

@ -0,0 +1,110 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class Vector2{
public $x, $y;
public function __construct($x = 0, $y = 0){
if(($x instanceof Vector2) === true){
$this->__construct($x->x, $x->y);
}else{
$this->x = $x;
$this->y = $y;
}
}
public function getX(){
return $this->x;
}
public function getY(){
return $this->y;
}
public function getFloorX(){
return (int) $this->x;
}
public function getFloorY(){
return (int) $this->y;
}
public function add($x = 0, $y = 0){
if(($x instanceof Vector2) === true){
return $this->add($x->x, $x->y);
}else{
$this->x += $x;
$this->y += $y;
return new Vector3($this->x + $x, $this->y + $y);
}
}
public function subtract($x = 0, $y = 0){
if(($x instanceof Vector2) === true){
return $this->add(-$x->x, -$x->y);
}else{
return $this->add(-$x, -$y);
}
}
public function ceil(){
return new Vector2((int) ($this->x + 1), (int) ($this->y + 1));
}
public function floor(){
return new Vector2((int) $this->x, (int) $this->y);
}
public function round(){
return new Vector2(round($this->x), round($this->y));
}
public function abs(){
return new Vector2(abs($this->x), abs($this->y));
}
public function distance($x = 0, $y = 0){
if(($x instanceof Vector2) === true){
return sqrt($this->distanceSquared($x->x, $x->y));
}else{
return sqrt($this->distanceSquared($x, $y));
}
}
public function distanceSquared($x = 0, $y = 0){
if(($x instanceof Vector2) === true){
return $this->distanceSquared($x->x, $x->y);
}else{
return pow($this->x - $x, 2) + pow($this->y - $y, 2);
}
}
public function __toString(){
return "Vector2(x=".$this->x.",y=".$this->y.")";
}
}

140
src/misc/math/Vector3.php Normal file
View File

@ -0,0 +1,140 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class Vector3{
public $x, $y, $z;
public function __construct($x = 0, $y = 0, $z = 0){
if(($x instanceof Vector3) === true){
$this->__construct($x->x, $x->y, $x->z);
}else{
$this->x = $x;
$this->y = $y;
$this->z = $z;
}
}
public function getX(){
return $this->x;
}
public function getY(){
return $this->y;
}
public function getZ(){
return $this->z;
}
public function getFloorX(){
return (int) $this->x;
}
public function getFloorY(){
return (int) $this->y;
}
public function getFloorZ(){
return (int) $this->z;
}
public function getRight(){
return $this->getX();
}
public function getUp(){
return $this->getY();
}
public function getForward(){
return $this->getZ();
}
public function getSouth(){
return $this->getX();
}
public function getWest(){
return $this->getZ();
}
public function add($x = 0, $y = 0, $z = 0){
if(($x instanceof Vector3) === true){
return $this->add($x->x, $x->y, $x->z);
}else{
$this->x += $x;
$this->y += $y;
$this->z += $z;
return new Vector3($this->x + $x, $this->y + $y, $this->z + $z);
}
}
public function subtract($x = 0, $y = 0, $z = 0){
if(($x instanceof Vector3) === true){
return $this->add(-$x->x, -$x->y, -$x->z);
}else{
return $this->add(-$x, -$y, -$z);
}
}
public function ceil(){
return new Vector3((int) ($this->x + 1), (int) ($this->y + 1), (int) ($this->z + 1));
}
public function floor(){
return new Vector3((int) $this->x, (int) $this->y, (int) $this->z);
}
public function round(){
return new Vector3(round($this->x), round($this->y), round($this->z));
}
public function abs(){
return new Vector3(abs($this->x), abs($this->y), abs($this->z));
}
public function distance($x = 0, $y = 0, $z = 0){
if(($x instanceof Vector3) === true){
return sqrt($this->distanceSquared($x->x, $x->y, $x->z));
}else{
return sqrt($this->distanceSquared($x, $y, $z));
}
}
public function distanceSquared($x = 0, $y = 0, $z = 0){
if(($x instanceof Vector3) === true){
return $this->distanceSquared($x->x, $x->y, $x->z);
}else{
return pow($this->x - $x, 2) + pow($this->y - $y, 2) + pow($this->z - $z, 2);
}
}
public function __toString(){
return "Vector3(x=".$this->x.",y=".$this->y.",z=".$this->z.")";
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class Container{
private $payload = "", $whitelist = false, $blacklist = false;
public function __construct($payload = "", $whitelist = false, $blacklist = false){
$this->payload = $payload;
if(is_array($whitelist)){
$this->whitelist = $whitelist;
}
if(is_array($blacklist)){
$this->blacklist = $blacklist;
}
}
public function get(){
return $this->payload;
}
public function check($target){
$w = true;
$b = false;
if($this->whitelist !== false){
$w = false;
if(in_array($target, $this->whitelist, true)){
$w = true;
}
}else{
$w = true;
}
if($this->blacklist !== false){
$b = true;
if(in_array($target, $this->blacklist, true)){
$b = false;
}
}else{
$b = false;
}
if($w === false or $b === true){
return false;
}
return true;
}
public function __toString(){
return $this->payload;
}
}

View File

@ -0,0 +1,47 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class TickLoop extends Thread{
var $tick = false, $stop = false, $lastTick = 0;
private $server;
public function __construct(PocketMinecraftServer $server){
$this->server = $server;
}
public function run(){
while($this->stop !== true){
usleep(1);
$time = microtime(true);
if($this->lastTick <= ($time - 0.05)){
$this->lastTick = $time;
$this->tick = true;
$this->wait();
$this->tick = false;
}
}
exit(0);
}
}

61
src/misc/utils/UPnP.php Normal file
View File

@ -0,0 +1,61 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
function UPnP_PortForward($port){
if(Utils::getOS() != "win" or !class_exists("COM")){
return false;
}
$port = (int) $port;
$myLocalIP = gethostbyname(trim(`hostname`));
try{
$com = new COM("HNetCfg.NATUPnP");
if($com === false or !is_object($com->StaticPortMappingCollection)){
return false;
}
$com->StaticPortMappingCollection->Add($port, "UDP", $port, $myLocalIP, true, "PocketMine-MP");
}catch(Exception $e){
return false;
}
return true;
}
function UPnP_RemovePortForward($port){
if(Utils::getOS() != "win" or !class_exists("COM")){
return false;
}
$port = (int) $port;
try{
$com = new COM("HNetCfg.NATUPnP") or false;
if($com === false or !is_object($com->StaticPortMappingCollection)){
return false;
}
$com->StaticPortMappingCollection->Remove($port, "UDP");
}catch(Exception $e){
return false;
}
return true;
}

View File

@ -0,0 +1,96 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class VersionString{
public static $stageOrder = array(
"alpha" => 0,
"beta" => 1,
"final" => 2,
);
private $stage, $major, $release, $minor;
public function __construct($version = MAJOR_VERSION){
if(is_int($version)){
$this->minor = $version & 0x1F;
$this->major = ($version >> 5) & 0x0F;
$this->generation = ($version >> 9) & 0x0F;
$this->stage = array_search(($version >> 13) & 0x0F, VersionString::$stageOrder, true);
}else{
$version = preg_split("/([A-Za-z]*)[ _\-]([0-9]*)\.([0-9]*)\.{0,1}([0-9]*)/", $version, -1, PREG_SPLIT_DELIM_CAPTURE);
$this->stage = strtolower($version[1]); //0-15
$this->generation = (int) $version[2]; //0-15
$this->major = (int) $version[3]; //0-15
$this->minor = (int) $version[4]; //0-31
}
}
public function getNumber(){
return (int) (VersionString::$stageOrder[$this->stage] << 13) + ($this->generation << 9) + ($this->major << 5) + $this->minor;
}
public function getStage(){
return $this->stage;
}
public function getGeneration(){
return $this->generation;
}
public function getMajor(){
return $this->major;
}
public function getMinor(){
return $this->minor;
}
public function getRelease(){
return $this->generation . "." . $this->major . "." . $this->minor;
}
public function __toString(){
return ucfirst($this->stage) . "_" . $this->generation . "." . $this->major . "." . $this->minor;
}
public function compare($target, $diff = false){
if(($target instanceof VersionString) === false){
$target = new VersionString($target);
}
$number = $this->getNumber();
$tNumber = $target->getNumber();
if($diff === true){
return $tNumber - $number;
}
if($number > $tNumber){
return -1; //Target is older
}elseif($number < $tNumber){
return 1; //Target is newer
}else{
return 0; //Same version
}
}
}

View File

@ -0,0 +1,89 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
require_once("misc/world/generator/object/tree/TreeObject.php");
class BigTreeObject extends TreeObject{
private $trunkHeightMultiplier = 0.618;
private $trunkHeight;
private $leafAmount = 1;
private $leafDistanceLimit = 5;
private $widthScale = 1;
private $branchSlope = 0.381;
private $totalHeight = 6;
private $leavesHeight = 3;
protected $radiusIncrease = 0;
private $addLeavesVines = false;
private $addLogVines = false;
private $addCocoaPlants = false;
public function canPlaceObject(LevelAPI $level, $x, $y, $z){
return false;
}
public function placeObject(LevelAPI $level, $x, $y, $z, $type){
$this->trunkHeight = (int) ($this->totalHeight * $this->trunkHeightMultiplier);
$leaves = $this->getLeafGroupPoints($level, $x, $y, $z);
foreach($leaves as $leafGroup){
$groupX = $leafGroup->getBlockX();
$groupY = $leafGrou->getBlockY();
$groupZ = $leafGroup->getBlockZ();
for ($yy = $groupY; $yy < $groupY + $this->leafDistanceLimit; ++$yy) {
$this->generateGroupLayer($level, $groupX, $yy, $groupZ, $this->getLeafGroupLayerSize($yy - $groupY));
}
}
/*final BlockIterator trunk = new BlockIterator(new Point(w, x, y - 1, z), new Point(w, x, y + trunkHeight, z));
while (trunk.hasNext()) {
trunk.next().setMaterial(VanillaMaterials.LOG, logMetadata);
}
generateBranches(w, x, y, z, leaves);
$level->setBlock($x, $y - 1, $z, 3, 0);
$this->totalHeight += mt_rand(-1, 3);
$this->leavesHeight += mt_rand(0, 1);
for($yy = ($this->totalHeight - $this->leavesHeight); $yy < ($this->totalHeight + 1); ++$yy){
$yRadius = ($yy - $this->totalHeight);
$xzRadius = (int) (($this->radiusIncrease + 1) - $yRadius / 2);
for($xx = -$xzRadius; $xx < ($xzRadius + 1); ++$xx){
for($zz = -$xzRadius; $zz < ($xzRadius + 1); ++$zz){
if((abs($xx) != $xzRadius or abs($zz) != $xzRadius) and $yRadius != 0){
$level->setBlock($x + $xx, $y + $yy, $z + $zz, 18, $type);
}
}
}
}
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){
$level->setBlock($x, $y + $yy, $z, 17, $type);
}
*/
}
}

View File

@ -0,0 +1,97 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
require_once("misc/world/generator/object/tree/TreeObject.php");
class PineTreeObject extends TreeObject{
var $type = 1;
private $totalHeight = 8;
private $leavesSizeY = -1;
private $leavesAbsoluteMaxRadius = -1;
public function canPlaceObject(LevelAPI $level, $x, $y, $z){
$this->findRandomLeavesSize();
$checkRadius = 0;
for($yy = 0; $yy < ($this->totalHeight + 2); ++$yy) {
if($yy === $this->leavesSizeY) {
$checkRadius = $this->leavesAbsoluteMaxRadius;
}
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
$block = $level->getBlock($x + $xx, $y + $yy, $z + $zz);
if(!isset($this->overridable[$block[0]])){
return false;
}
}
}
}
return true;
}
private function findRandomLeavesSize(){
$this->totalHeight += mt_rand(-1, 2);
$this->leavesSizeY = 1 + mt_rand(0,2);
$this->leavesAbsoluteMaxRadius = 2 + mt_rand(0, 2);
}
public function placeObject(LevelAPI $level, $x, $y, $z){
if($this->leavesSizeY === -1 or $this->leavesAbsoluteMaxRadius === -1) {
$this->findRandomLeavesSize();
}
$level->setBlock($x, $y - 1, $z, 3, 0);
$leavesRadius = mt_rand(0,2);
$leavesMaxRadius = 1;
$leavesBottomY = $this->totalHeight - $this->leavesSizeY;
$firstMaxedRadius = false;
for($leavesY = 0; $leavesY < ($leavesBottomY + 1); ++$leavesY) {
$yy = $this->totalHeight - $leavesY;
for ($xx = -$leavesRadius; $xx < ($leavesRadius + 1); ++$xx) {
for ($zz = -$leavesRadius; $zz < ($leavesRadius + 1); ++$zz) {
if (abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0) {
$level->setBlock($x + $xx, $y + $yy, $z + $zz, 18, $this->type);
}
}
}
if ($leavesRadius >= $leavesMaxRadius) {
$leavesRadius = $firstMaxedRadius ? 1 : 0;
$firstMaxedRadius = true;
if (++$leavesMaxRadius > $this->leavesAbsoluteMaxRadius) {
$leavesMaxRadius = $this->leavesAbsoluteMaxRadius;
}
}else{
++$leavesRadius;
}
}
$trunkHeightReducer = mt_rand(0,3);
for($yy = 0; $yy < ($this->totalHeight - $trunkHeightReducer); ++$yy){
$level->setBlock($x, $y + $yy, $z, 17, $this->type);
}
}
}

View File

@ -29,6 +29,7 @@ the Free Software Foundation, either version 3 of the License, or
require_once("misc/world/generator/object/tree/TreeObject.php"); require_once("misc/world/generator/object/tree/TreeObject.php");
class SmallTreeObject extends TreeObject{ class SmallTreeObject extends TreeObject{
var $type = 0;
private $totalHeight = 6; private $totalHeight = 6;
private $leavesHeight = 3; private $leavesHeight = 3;
protected $radiusIncrease = 0; protected $radiusIncrease = 0;
@ -36,7 +37,25 @@ class SmallTreeObject extends TreeObject{
private $addLogVines = false; private $addLogVines = false;
private $addCocoaPlants = false; private $addCocoaPlants = false;
public function placeObject(LevelAPI $level, $x, $y, $z, $type){ public function canPlaceObject(LevelAPI $level, $x, $y, $z){
$radiusToCheck = $this->radiusIncrease;
for ($yy = 0; $yy < $this->totalHeight + 2; ++$yy) {
if ($yy == 1 or $yy === $this->totalHeight - 1) {
++$radiusToCheck;
}
for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){
for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){
$block = $level->getBlock($x + $xx, $y + $yy, $z + $zz);
if(!isset($this->overridable[$block[0]])){
return false;
}
}
}
}
return true;
}
public function placeObject(LevelAPI $level, $x, $y, $z){
$level->setBlock($x, $y - 1, $z, 3, 0); $level->setBlock($x, $y - 1, $z, 3, 0);
$this->totalHeight += mt_rand(-1, 3); $this->totalHeight += mt_rand(-1, 3);
$this->leavesHeight += mt_rand(0, 1); $this->leavesHeight += mt_rand(0, 1);
@ -46,13 +65,13 @@ class SmallTreeObject extends TreeObject{
for($xx = -$xzRadius; $xx < ($xzRadius + 1); ++$xx){ for($xx = -$xzRadius; $xx < ($xzRadius + 1); ++$xx){
for($zz = -$xzRadius; $zz < ($xzRadius + 1); ++$zz){ for($zz = -$xzRadius; $zz < ($xzRadius + 1); ++$zz){
if((abs($xx) != $xzRadius or abs($zz) != $xzRadius) and $yRadius != 0){ if((abs($xx) != $xzRadius or abs($zz) != $xzRadius) and $yRadius != 0){
$level->setBlock($x + $xx, $y + $yy, $z + $zz, 18, $type); $level->setBlock($x + $xx, $y + $yy, $z + $zz, 18, $this->type);
} }
} }
} }
} }
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){ for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){
$level->setBlock($x, $y + $yy, $z, 17, $type); $level->setBlock($x, $y + $yy, $z, 17, $this->type);
} }
} }

View File

@ -0,0 +1,88 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
require_once("misc/world/generator/object/tree/TreeObject.php");
class SpruceTreeObject extends TreeObject{
var $type = 1;
private $totalHeight = 8;
private $leavesBottomY = -1;
private $leavesMaxRadius = -1;
public function canPlaceObject(LevelAPI $level, $x, $y, $z){
$this->findRandomLeavesSize();
$checkRadius = 0;
for($yy = 0; $yy < $this->totalHeight + 2; ++$yy) {
if($yy === $this->leavesBottomY) {
$checkRadius = $this->leavesMaxRadius;
}
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
$block = $level->getBlock($x + $xx, $y + $yy, $z + $zz);
if(!isset($this->overridable[$block[0]])){
return false;
}
}
}
}
return true;
}
private function findRandomLeavesSize(){
$this->totalHeight += mt_rand(-1, 2);
$this->leavesBottomY = (int) ($this->totalHeight - mt_rand(1,2) - 3);
$this->leavesMaxRadius = 1 + mt_rand(0, 1);
}
public function placeObject(LevelAPI $level, $x, $y, $z){
if($this->leavesBottomY === -1 or $this->leavesMaxRadius === -1) {
$this->findRandomLeavesSize();
}
$level->setBlock($x, $y - 1, $z, 3, 0);
$leavesRadius = 0;
for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){
for ($xx = -$leavesRadius; $xx < ($leavesRadius + 1); ++$xx) {
for ($zz = -$leavesRadius; $zz < ($leavesRadius + 1); ++$zz) {
if (abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0) {
$level->setBlock($x + $xx, $y + $yy, $z + $zz, 18, $this->type);
}
}
}
if ($leavesRadius > 0 and $yy === ($y + $this->leavesBottomY + 1)) {
--$leavesRadius;
}elseif($leavesRadius < $this->leavesMaxRadius){
++$leavesRadius;
}
}
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){
$level->setBlock($x, $y + $yy, $z, 17, $this->type);
}
}
}

View File

@ -0,0 +1,62 @@
<?php
/*
-
/ \
/ \
/ PocketMine \
/ MP \
|\ @shoghicp /|
|. \ / .|
| .. \ / .. |
| .. | .. |
| .. | .. |
\ | /
\ | /
\ | /
\ | /
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
*/
class TreeObject{
var $overridable = array(
0 => true,
6 => true,
17 => true,
18 => true,
);
public static function growTree(LevelAPI $level, $block, $type){
switch($type){
case Sapling::SPRUCE:
if(mt_rand(0,1) == 2){
$tree = new SpruceTreeObject();
}else{
$tree = new PineTreeObject();
}
break;
case Sapling::BIRCH:
$tree = new SmallTreeObject();
$tree->type = Sapling::BIRCH;
break;
default:
case Sapling::OAK:
if(mt_rand(0,9) === 0){
$tree = new BigTreeObject();
}else{
$tree = new SmallTreeObject();
}
break;
}
if($tree->canPlaceObject($level, $block[2][0], $block[2][1], $block[2][2])){
$tree->placeObject($level, $block[2][0], $block[2][1], $block[2][2]);
}
}
}

View File

@ -50,6 +50,7 @@ $dataName = array(
MC_ADD_ENTITY => "AddEntity", MC_ADD_ENTITY => "AddEntity",
MC_REMOVE_ENTITY => "RemoveEntity", MC_REMOVE_ENTITY => "RemoveEntity",
MC_ADD_ITEM_ENTITY => "AddItemEntity", MC_ADD_ITEM_ENTITY => "AddItemEntity",
MC_TAKE_ITEM_ENTITY => "TakeItemEntity",
MC_MOVE_ENTITY => "MoveEntity", MC_MOVE_ENTITY => "MoveEntity",
@ -59,6 +60,8 @@ $dataName = array(
MC_REMOVE_BLOCK => "RemoveBlock", MC_REMOVE_BLOCK => "RemoveBlock",
MC_UPDATE_BLOCK => "UpdateBlock", MC_UPDATE_BLOCK => "UpdateBlock",
MC_EXPLOSION => "Explosion",
MC_REQUEST_CHUNK => "RequestChunk", MC_REQUEST_CHUNK => "RequestChunk",
MC_CHUNK_DATA => "ChunkData", MC_CHUNK_DATA => "ChunkData",
@ -74,6 +77,11 @@ $dataName = array(
MC_ANIMATE => "Animate", MC_ANIMATE => "Animate",
MC_RESPAWN => "Respawn", MC_RESPAWN => "Respawn",
MC_DROP_ITEM => "DropItem",
MC_CONTAINER_OPEN => "ContainerOpen",
MC_CONTAINER_CLOSE => "ContainerClose",
MC_CONTAINER_SET_SLOT => "ContainerSetSlot",
MC_CLIENT_MESSAGE => "ClientMessage", MC_CLIENT_MESSAGE => "ClientMessage",
MC_SIGN_UPDATE => "SignUpdate", MC_SIGN_UPDATE => "SignUpdate",
MC_ADVENTURE_SETTINGS => "AdventureSettings", MC_ADVENTURE_SETTINGS => "AdventureSettings",

View File

@ -50,6 +50,7 @@ define("MC_ADD_PLAYER", 0x89);
define("MC_ADD_ENTITY", 0x8c); define("MC_ADD_ENTITY", 0x8c);
define("MC_REMOVE_ENTITY", 0x8d); define("MC_REMOVE_ENTITY", 0x8d);
define("MC_ADD_ITEM_ENTITY", 0x8e); define("MC_ADD_ITEM_ENTITY", 0x8e);
define("MC_TAKE_ITEM_ENTITY", 0x8f);
define("MC_MOVE_ENTITY", 0x90); define("MC_MOVE_ENTITY", 0x90);
@ -59,6 +60,8 @@ define("MC_PLACE_BLOCK", 0x95);
define("MC_REMOVE_BLOCK", 0x96); define("MC_REMOVE_BLOCK", 0x96);
define("MC_UPDATE_BLOCK", 0x97); define("MC_UPDATE_BLOCK", 0x97);
define("MC_EXPLOSION", 0x99);
define("MC_REQUEST_CHUNK", 0x9d); define("MC_REQUEST_CHUNK", 0x9d);
define("MC_CHUNK_DATA", 0x9e); define("MC_CHUNK_DATA", 0x9e);
@ -74,6 +77,11 @@ define("MC_SET_SPAWN_POSITION", 0xa6);
define("MC_ANIMATE", 0xa7); define("MC_ANIMATE", 0xa7);
define("MC_RESPAWN", 0xa8); define("MC_RESPAWN", 0xa8);
define("MC_DROP_ITEM", 0xaa);
define("MC_CONTAINER_OPEN", 0xab);
define("MC_CONTAINER_CLOSE", 0xac);
define("MC_CONTAINER_SET_SLOT", 0xad);
define("MC_CLIENT_MESSAGE", 0xb1); define("MC_CLIENT_MESSAGE", 0xb1);
define("MC_SIGN_UPDATE", 0xb2); define("MC_SIGN_UPDATE", 0xb2);
define("MC_ADVENTURE_SETTINGS", 0xb3); define("MC_ADVENTURE_SETTINGS", 0xb3);

View File

@ -26,7 +26,6 @@ if not "%PHPOUTPUT%"=="1" (
echo [ERROR] Couldn't find PHP binary in PATH. echo [ERROR] Couldn't find PHP binary in PATH.
ping 127.0.0.1 -n 3 -w 1000>nul ping 127.0.0.1 -n 3 -w 1000>nul
) else ( ) else (
START /B CMD /C CALL php server.php START /B /WAIT php -d enable_dl=On PocketMine-MP.php
START /B /WAIT php input.php 1
ping 127.0.0.1 -n 5 -w 1000>nul
) )
pause

View File

@ -1,3 +1,8 @@
#!/bin/bash #!/bin/bash
php server.php & DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cat>console.in cd $DIR
if [ -f php5/bin/php ]; then
./php5/bin/php -d enable_dl=On PocketMine-MP.php
exit 0
fi
php -d enable_dl=On PocketMine-MP.php