GithubHelp home page GithubHelp logo

pmmp / raklib Goto Github PK

View Code? Open in Web Editor NEW
80.0 16.0 36.0 826 KB

RakNet server implementation written in PHP

License: Other

PHP 100.00%
on-packagist phpstan-strict github-actions-enabled php81 phpstan-l9 php82

raklib's Introduction

RakLib

CI

UDP network library that follows the RakNet protocol for PHP

This library is very lightweight on actual implementation - it provides the bare minimum to get a Minecraft Pocket Edition server functional. It only currently provides server functionality, and does not support most RakNet features.

This project is not affiliated with Jenkins Software LLC nor RakNet.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

## Exceptions
The usage of this library with PocketMine-MP will allow the creation
of derivated non open-source works, without direct usage of RakLib.
Only PocketMine-MP and their plugins will be able to use the library
without license restrictions.

raklib's People

Contributors

0x15f avatar alejandroliu avatar boi1216 avatar dependabot-preview[bot] avatar dependabot[bot] avatar dktapps avatar falkirks avatar frago9876543210 avatar humerus avatar icecruelstuff avatar intyre avatar shoghicp avatar sof3 avatar svilex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

raklib's Issues

E_Warning

 [RakLibServer thread/DEBUG]: An E_WARNING error happened: "time_sleep_until(): Sleep until to time is less than current time" in "/src/raklib/server/SessionManager" at line 108
[23:34:22] [RakLibServer thread/DEBUG]: #0 /src/raklib/server/SessionManager(94): raklib\server\SessionManager->tickProcessor()
[23:34:22] [RakLibServer thread/DEBUG]: #1 /src/raklib/server/SessionManager(82): raklib\server\SessionManager->run()
[23:34:22] [RakLibServer thread/DEBUG]: #2 /src/raklib/server/RakLibServer(236): raklib\server\SessionManager->__construct(raklib\server\RakLibServer object, raklib\server\UDPServerSocket object)
[23:34:22] [RakLibServer thread/DEBUG]: #3 (): raklib\server\RakLibServer->run()

IPv6 addresses can't be decoded correctly when PHP is compiled without IPv6 support

The client can send IPv6 internal addresses in ID_NEW_INCOMING_CONNECTION even if the connection is over IPv4. This causes decode errors when on a PHP build without IPv6 support.

This should either:
a) make IPv6 support a requirement
b) dump addresses which are IPv6 when IPv6 is not enabled (they are a fixed length, so this shouldn't be hard)

I cannot use PocketMine as part of 'Big server'

Hello, I've found one bug which 'cashes' the whole server. I'm using Waterdog proxy, from which I normally redirect players to other servers (as usual), that works fine... But happend that the pocketmine server handled more packets than were allowed. So it blocked address 127.0.0.1 for 300 seconds. Also because of all the players log with ip 127.0.0.1 they weren't able to play.

Add ability to cache reusable data RakLib-side

php-pthreads is very inefficient. One thing that came to my eye is that PM chunk sending causes copying of the same data over and over again to the RakLib thread for sending.

This proposes a caching method whereby the application can prime a cache on the RakLib side of the fence, and then send a small cache identifier to have RakLib reuse the data for sending instead of copying the entire data lots of times.

A test script to demonstrate the difference:

$data = random_bytes(2000);

$start = microtime(true);
$queue = new \Threaded();
for($i = 0; $i < 100000; ++$i){
	$queue[] = $data;
}


var_dump((microtime(true) - $start) * 1000);

$queue = null;

$start = microtime(true);
$queue = new \Threaded();
for($i = 0; $i < 100000; ++$i){
	$queue[] = 1;
}
var_dump((microtime(true) - $start) * 1000);

The second version is 5-10x faster than the first version. Therefore, $data could be copied once, assigned an ID, then just push the ID through the queue to use the data.

Caveats

This would be ineffectual when MCPE high-level encryption is used. A possible solution to this is to add pre-send and post-receive processing hooks to allow encryption to be handled on the RakLib side instead of the main thread.

Data packets in one class

For all the DATA_PACKET_ classes, couldnt they just be merged into a single class? Either as constants or nested classes.

Delay NACK sending to allow misordered packets to arrive

Currently we send out NACKs at the same time as ACKs, which might not be beneficial for packets that arrived in the wrong order. It might make sense to allow them more time to arrive before sending a NACK.

The tradeoff here is that taking longer to NACK a packet which is actually lost will cause it to take longer to get resent, potentially causing brief processing latency spikes (since MCPE always uses reliable-ordered).

Unknown IP address version.

[16:45:02] [RakLibServer thread/DEBUG]: #0 vendor/pmmp/raklib/protocol/NewIncomingConnection(48): raklib\protocol\Packet->getAddress(string 0.0.0.0, integer 0, integer 255)
[16:45:02] [RakLibServer thread/DEBUG]: #1 vendor/pmmp/raklib/server/Session(460): raklib\protocol\NewIncomingConnection->decode()
[16:45:02] [RakLibServer thread/DEBUG]: #2 vendor/pmmp/raklib/server/Session(406): raklib\server\Session->handleEncapsulatedPacketRoute(raklib\protocol\EncapsulatedPacket object)
[16:45:02] [RakLibServer thread/DEBUG]: #3 vendor/pmmp/raklib/server/Session(540): raklib\server\Session->handleEncapsulatedPacket(raklib\protocol\EncapsulatedPacket object)
[16:45:02] [RakLibServer thread/DEBUG]: #4 vendor/pmmp/raklib/server/SessionManager(213): raklib\server\Session->handlePacket(raklib\protocol\DATA_PACKET_4 object)
[16:45:02] [RakLibServer thread/DEBUG]: #5 vendor/pmmp/raklib/server/SessionManager(138): raklib\server\SessionManager->receivePacket()
[16:45:02] [RakLibServer thread/DEBUG]: #6 vendor/pmmp/raklib/server/SessionManager(129): raklib\server\SessionManager->tickProcessor()
[16:45:02] [RakLibServer thread/DEBUG]: #7 vendor/pmmp/raklib/server/SessionManager(109): raklib\server\SessionManager->run()
[16:45:02] [RakLibServer thread/DEBUG]: #8 vendor/pmmp/raklib/server/RakLibServer(237): raklib\server\SessionManager->__construct(raklib\server\RakLibServer object, raklib\server\UDPServerSocket object)
[16:45:02] [RakLibServer thread/DEBUG]: #9 (): raklib\server\RakLibServer->run()
[16:45:02] [RakLibServer thread/NOTICE]: Blocked 77.87.102.132 for 5 seconds

Asymmetric client/server MTU

Due to various bugs, the Minecraft client must be limited at 1400 MTU in common cases, because of edge cases on some devices and older routers. In these cases, the MTU probe in OpenConnectionRequest/Reply1 yields a higher MTU than is actually realistically available.

However, reducing max MTU is sub-optimal for server bandwidth, because it increases total bandwidth usage by packet headers from 4.0% to 4.3%. This doesn't sound like much, but it can make a difference on large servers.

We can avoid this by capping the client to a fixed MTU of 1400 (the client rarely sends huge packets anyway) and having the server dynamically probe the connection after the initial sequence to find out where the real MTU limit is by figuring out where packets actually get lost, and limiting its output to that instead. This way, only players with dodgy hardware experience reduced MTU, and everyone else will be unaffected.

This idea was suggested by @MCMrARM.

Implement disk cache for large split packet reassembly

The current max split size is problematic for Minecraft usage due to extremely oversized skins. It's also problematic to increase it due to exposing servers to denial-of-service attacks.

A workaround for this would be to cache parts of very large split packets (above a certain threshold) on disk temporarily, so that RAM doesn't get exhausted trying to reassemble huge splits for many sessions at the same time. Since disk space is typically more abundant than RAM, this would allow a higher limit on split count, which would alleviate some issues.

Detected spam

SO reduced this issue because it is spam and Fe2+ oxidized this issue to base64 because it is a 24-hour no-life disrespectful watcher of issues.
Original content:

VGl0bGU6ICFzcGFtCkJvZHk6ICFbaW1hZ2VdKGh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNl
cmNvbnRlbnQuY29tLzI3NTM5ODcwLzMzNzg2MjY0LTk0NmI1MGUwLWRjNGYtMTFlNy05ZmJiLWRk
ODUwNGRlNDc2Mi5wbmcpDQo=

NewIncomingConnection decode issue on iOS (10 system addresses instead of expected 20)

/NOTICE Blocked <ip address here> for 5 seconds
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #9 (): raklib\server\RakLibServer->run()
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #8 src/raklib/server/RakLibServer(235): raklib\server\SessionManager->__construct(raklib\server\RakLibServer object, raklib\server\UDPServerSocket object)
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #7 src/raklib/server/SessionManager(109): raklib\server\SessionManager->run()
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #6 src/raklib/server/SessionManager(129): raklib\server\SessionManager->tickProcessor()
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #5 src/raklib/server/SessionManager(138): raklib\server\SessionManager->receivePacket()
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #4 src/raklib/server/SessionManager(213): raklib\server\Session->handlePacket(raklib\protocol\DATA_PACKET_4 object)
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #3 src/raklib/server/Session(540): raklib\server\Session->handleEncapsulatedPacket(raklib\protocol\EncapsulatedPacket object)
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #2 src/raklib/server/Session(406): raklib\server\Session->handleEncapsulatedPacketRoute(raklib\protocol\EncapsulatedPacket object)
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #1 src/raklib/server/Session(460): raklib\protocol\NewIncomingConnection->decode()
06.11 13:25:16 [Server] RakLibServer thread/DEBUG #0 src/raklib/protocol/NewIncomingConnection(48): raklib\protocol\Packet->getAddress(string 0.0.0.0, integer 0, integer 255)
06.11 13:25:16 [Server] RakLibServer thread/CRITICAL UnexpectedValueException: "Unknown IP address version 255" (EXCEPTION) in "src/raklib/protocol/Packet" at line 90

Server crashes on startup

Link to crashdump: https://pastebin.com/3p0dDKty

Additional comments (optional)

Git commit version: 4ae6428
Ubuntu version: 18.04

When PMMP is run on WSL (Windows Subsystem for Linux), server crashes on startup with following message:
socket_set_option(): unable to set socket option [22]: Invalid argument

Session still tries to send pending packets after receiving disconnect notification from client

Issue description

With xbox-auth=off during fast rejoin player to server it will be disconnected due to already opened raklib session.

  • Expected result: You can rejoin to server without any problems
  • Actual result: Due to opened raklib session you can't rejoin and will be kicked until it will not be closed

Steps to reproduce the issue

  1. Join to server with not xbox client
  2. Leave server
  3. Rejoin fast as you can

OS and versions

  • PocketMine-MP: 676bacbee18d5eafeddf7b89ed7b70a2ce911f70
  • PHP: 8.0.8
  • Server OS: Ubuntu 18.04

Backtrace or other files

https://pastebin.com/raw/DVTJRmvw

Split up internal & RakNet encode/decode for EncapsulatedPacket

Lots of null things that don't need to be encoded for thread communication get encoded when copying packets between threads. This is wasteful and additionally causes strict type issues.
Also, having the internal encoding in the same method as RakNet encoding is very confusing for people reading the code. RakLib is a reference point for many developers writing their own servers.

Incorrect MTU size sent in open connection reply 1

RakNet includes the size of the IP+UDP headers, RakLib does not. Since RakNet maxes out at 1492 bytes, I tested this with 2 vanilla clients, and then 1 client + PocketMine-MP server, on the same connection. The buffer size of OPEN_CONNECTION_REQUEST_1 was the same both times.

  • RakNet MTU size in OPEN_CONNECTION_REPLY_1: 1492 bytes
  • RakLib MTU size in OPEN_CONNECTION_REPLY_1: 1464 bytes

This information was obtained using Wireshark.

Unsure what issues this may cause.

Implement congestion control

Currently RakLib does not properly rate limit packet sending for sessions. This is a big problem because connections may have smaller bandwidth capability than RakLib sessions are trying to send. This causes the client to panic and flood the server with NAKs, causing the server to IP-ban the client for denial of service attack.

Generally this manifests in PM when sending resource packs or lots of chunks over bad connections resulting in an IP ban by the server.

AdvertiseSystem is unused dead code

There is no reason to keep something we don't use if we aren't implementing all unused packets anyway. This is inconsistent and looks confusing to people referring to RakLib as a protocol reference (which was what PocketMine was initially supposed to be).

Query server broken on stable branch

Issue description

eb01243 ignores the fact that packets may be received from query

Steps to reproduce the issue

  1. send query to raklib server
  2. as result you will get Ignored datagram from $address due to no session opened (0xfe)

Work around possible incorrect path MTU size establishment due to VPN compression

There have been a lot of intermittent reports for a long time about connection problems to PM servers when using a VPN.

In the process of attempting to debug this issue, I noted that ID_OPEN_CONNECTION_REQUEST_1 has a payload padded with 0 bytes. This is a potential problem if used over a VPN using compression which also has a lower path MTU than the usual 1492 bytes. The reason is that the usual RakNet assumption (if this is too big, we won't get a reply) is broken in the case of compressed VPN since the connection request itself will be compressed.

A possible solution to this is to append a padding of randomly-generated bytes to the end of ID_OPEN_CONNECTION_REPLY_1 which will not be possible to compress. This will prevent the client receiving the response if the MTU size is bullshit due to compression.

To be clear, this bug/oversight is a problem with a RakNet client so it can't be directly fixed by us, only worked around.

Do not resend unreliable packets on NACK

Unreliable packets are expected to potentially never turn up, so we shouldn't be resending them when they get NACKed. This is a waste of bandwidth (although, considering the use case, it's not a significant issue anyway).

Dynamically adjust IP rate limit based on expected ACK/NACK counts

When we send N outbound non-acknowledgement datagrams, we expect that the client will respond with up to N acknowledgement packets in response.

When sending a large amount of outbound data, it's likely that the client will send a large amount of acknowledgement packets in response - perhaps enough to exceed the maximum inbound per-tick packet limit and trigger an IP ban.

Allow reconnection from an IP/port to replace a session stuck in DISCONNECTING state

I frequently see cases where the MCPE client improperly hangs up the connection during disconnect, leading to RakLib to refuse new connection from the same client when quickly quitting and rejoining the server. To combat this, it should be possible to have the existing session removed directly without waiting if the session was already in disconnecting state.

Error

2020-03-22 15:30:53 <RakLibServer thread/Debug> #0 vendor/pocketmine/raklib/src/server/SessionManager(354): raklib\server\RakLibServer->pushThreadToMainPacket(string[77] ..bandwidtha:2:{s:2:"up";d:98.08352106238462;s:4:"down";d:32.69450702079487;})
2020-03-22 15:30:53 <RakLibServer thread/Debug> #1 vendor/pocketmine/raklib/src/server/SessionManager(195): raklib\server\SessionManager->streamOption(string[9] bandwidth, string[66] a:2:{s:2:"up";d:98.08352106238462;s:4:"down";d:32.69450702079487;})
2020-03-22 15:30:53 <RakLibServer thread/Debug> #2 vendor/pocketmine/raklib/src/server/SessionManager(173): raklib\server\SessionManager->tick()
2020-03-22 15:30:53 <RakLibServer thread/Debug> #3 vendor/pocketmine/raklib/src/server/SessionManager(150): raklib\server\SessionManager->tickProcessor()
2020-03-22 15:30:53 <RakLibServer thread/Debug> #4 vendor/pocketmine/raklib/src/server/SessionManager(122): raklib\server\SessionManager->run()
2020-03-22 15:30:53 <RakLibServer thread/Debug> #5 vendor/pocketmine/raklib/src/server/RakLibServer(284): raklib\server\SessionManager->__construct(object raklib\server\RakLibServer, object raklib\server\UDPServerSocket, integer 1492)
2020-03-22 15:30:53 <RakLibServer thread/Debug> #6 (): raklib\server\RakLibServer->run()

Remove InternetAddress mutability

There is no point in reusing an InternetAddress object just for premature optimization and risking internal race conditions of using the same InternetAddress object.

Typo in UnconnectedMessageHandler.php

I was creating a RakNet protocol handler in python and was looking for how the client GUID gets generated. While researching I came across this repository and noticed you have a typo on line #75 in UnconnectedMessageHandler.php where the function is spelt "hanlde", and should be corrected to "handle". While someone's at it, if they have time can you please tell me how the client ID gets generated as I am not familiar with php.

Encapsulated packets and raw UDP packets should be listed separately

Some packets are only received/sent as UDP packets, and some are only inside the EncapsulatedPacket buffer. However both types of packets are declared in MessageIdentifiers.php and src/protocol without distinction (probably legacy from RakNet itself).

They should be declared separately for clarity.

Implement ordering channels

Currently RakLib does packet ordering based on the message index. This is rather astonishing behaviour, since this means that all reliable packets received will be ordered based on this message index, and the unreliable-sequenced ordering will not work at all.

Simply put, RakLib does not implement anything except reliable-ordered and unreliable correctly.

Combine IP Address with Client ID (or something) for multiple players from same IP

With the packet limiting portion (possibly also related to #32) - I have 3 clients connecting to my server from the same public IP - I think the rate limiting is based on the IP only, instead of a composite key of sorts with the IP and the client. Thus, enough activity from 3 players on the same IP - causes a server crash / IP ban with too many packets.

Disconnect messages getting lost on shutdown

for($i = 0; $i < 100 && $stream && !$this->shutdown; ++$i){ //if we received a shutdown event, we don't care about any more messages from the event source

Due to a recent change in the way disconnects are handled, they are no longer the final message to come through the channel, meaning that the server may be flagged as shut down while there are still packets in the channels waiting to be processed.

The above line stops processing packets immediately when shutdown becomes true, which could cause things like disconnection screen messages to get lost during server shutdown.

Currently shutdown only becomes true during waitShutdown(), so it's actually useless anyway; however, the issue with messages remaining to be processed on shutdown remains.

Wrong max encapsulated packet header size.

Why is it counted as 20 when the correct size is 23?

//IP header size (20 bytes) + UDP header size (8 bytes) + RakNet weird (8 bytes) + datagram header size (4 bytes) + max encapsulated packet header size (20 bytes)

You can calculate this using these lines:

public function toBinary() : string{

1 byte (flags) + 2 bytes (payload length) + 3 bytes (reliable index) + 3 bytes (sequence index) + 3 bytes (order index) + 1 byte (order channel) + 4 bytes (compound size) + 2 bytes (compound id) + 4 bytes (compound index) = 23

ACK receipt does not work for unreliable packets

The current mechanism for ACK receipts silently ignores unreliable packets which need ACK receipts.

Obviously, under normal circumstances we might never get an ACK receipt for an unreliable packet, in which case a NACK receipt should be delivered instead (needs to be implemented).

Refused Connection from x.x.x.x 50678 due to incompatible RakNet protocol version (expected 8, got 9)

So, I'm trying to connect to my PMMP server, which I just port-forwarded and made into a custom-ip with www.no-ip.org, and now I get this error on the console:

[19:41:09] [RakLibServer thread/NOTICE]: Refused connection from x.x.x.x (just blanked that out for ya, and this wasn't in the console) 50678 due to incompatible RakNet protocol version (expected 8, got 9).

And I'm thinking, <iframe src="https://giphy.com/embed/3o7527pa7qs9kCG78A" width="480" height="480" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/nehumanesociety-funny-dog-3o7527pa7qs9kCG78A"></a></p>

So any help would be appreciated, DitDit :).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.