├── Documentation.md ├── Examples ├── Game.meta ├── Game │ ├── FPS Score.meta │ ├── FPS Score │ │ ├── FPSDisplayer.cs │ │ ├── FPSDisplayer.cs.meta │ │ ├── FPSScore.prefab │ │ └── FPSScore.prefab.meta │ ├── Game.meta │ ├── Game.unity │ ├── Game.unity.meta │ ├── Game │ │ ├── Game.lighting │ │ ├── Game.lighting.meta │ │ ├── GrassLayer.terrainlayer │ │ ├── GrassLayer.terrainlayer.meta │ │ ├── LightingData.asset │ │ ├── LightingData.asset.meta │ │ ├── New Terrain.asset │ │ ├── New Terrain.asset.meta │ │ ├── Player.mat │ │ ├── Player.mat.meta │ │ ├── ReflectionProbe-0.exr │ │ ├── ReflectionProbe-0.exr.meta │ │ ├── grass.png │ │ └── grass.png.meta │ ├── Player.prefab │ ├── Player.prefab.meta │ ├── Scripts.meta │ └── Scripts │ │ ├── NetworkClient.cs │ │ ├── NetworkClient.cs.meta │ │ ├── NetworkManager.cs │ │ ├── NetworkManager.cs.meta │ │ ├── NetworkPlayer.cs │ │ ├── NetworkPlayer.cs.meta │ │ ├── NetworkServer.cs │ │ ├── NetworkServer.cs.meta │ │ ├── UIMenager.cs │ │ └── UIMenager.cs.meta ├── Test.meta ├── Test │ ├── Scripts.meta │ ├── Scripts │ │ ├── TestClient.cs │ │ ├── TestClient.cs.meta │ │ ├── TestServer.cs │ │ └── TestServer.cs.meta │ ├── TestScene.unity │ └── TestScene.unity.meta ├── TestingСonnections.meta └── TestingСonnections │ ├── Scripts.meta │ ├── Scripts │ ├── TestingClient.cs │ ├── TestingClient.cs.meta │ ├── TestingServer.cs │ └── TestingServer.cs.meta │ ├── TestingСonnections.unity │ └── TestingСonnections.unity.meta ├── LICENSE ├── README.md ├── SimpleUDP.asmdef └── SimpleUDP ├── Core ├── UdpChannel.cs ├── UdpHeader.cs ├── UdpListener.cs ├── UdpPending.cs └── Utils │ ├── UdpActive.cs │ ├── UdpBuffer.cs │ ├── UdpConverter.cs │ └── UdpLog.cs ├── Extensions ├── Extensions.cs └── Packet.cs ├── UdpClient.cs ├── UdpPeer.cs └── UdpServer.cs /Documentation.md: -------------------------------------------------------------------------------- 1 | # SimpleUDP Documentation 2 | 3 | ## Overview 4 | SimpleUDP is a lightweight and easy-to-use UDP networking library suitable for both .NET applications and Unity projects. It supports both server and client functionality, with features for reliable and unreliable messaging. 5 | 6 | ### The project at this stage is already suitable for use in small commercial applications and games. 7 | - I don't plan to make any major changes to the functionality in the future, mostly bug fixes and performance improvements. 8 | 9 | **Version:** 0.7.0 10 | 11 | [General](#General) 12 | 13 | [Log](#Log) 14 | 15 | [Server](#Server) 16 | 17 | [Client](#Client) 18 | 19 | [Peer](#Peer) 20 | 21 | [Packet](#Packet) 22 | 23 | [License](#License) 24 | 25 | ## General 26 | 27 | The following properties and methods are available in both the `Server` and `Client` classes: 28 | 29 | ### Properties 30 | 31 | - `bool IsRunning` `Read only` 32 | - Indicates whether the listening is active. 33 | 34 | - `ushort LocalPort` `Read only` 35 | - The local port being listened to. 36 | 37 | - `IPEndPoint LocalEndPoint` `Read only` 38 | - The local IP address and port. 39 | 40 | - `bool EnableBroadcast` `Read only` 41 | - Indicates if broadcast messages can be sent. 42 | - Enabling the broadcast is available in the parameters of the "Start()" method. 43 | 44 | - `LimitedSizePackage = true` 45 | - Limits the maximum packet size for receiving to 1432 bytes. 46 | 47 | - `ushort MaxSizePacket = 1432` 48 | - The maximum packet size is 1432 bytes.. 49 | 50 | - `ushort ReceiveBufferSize` 51 | - The buffer size in bytes for receiving packets. 52 | - The default value is 2048. 53 | 54 | - `int AvailablePackages` 55 | - The number of packets waiting to be processed by the `Receive` method. 56 | 57 | - `bool SocketPoll` 58 | - Polling status of the socket. More info: [Socket.Poll](https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.poll?view=net-8.0) reference. 59 | - Important: use this option only to process Receive in a dedicated thread. 60 | 61 | ### Callbacks 62 | 63 | - `Action OnStarted` 64 | - Invoked when the server starts. 65 | 66 | - `Action OnStopped` 67 | - Invoked when the server stops. 68 | 69 | - `Action OnReceiveBroadcast` 70 | - Invoked when a broadcast message is received. 71 | 72 | - `Action OnReceiveUnconnected` 73 | - Invoked when a message is received from an unconnected host. 74 | - It is not necessary for the client to specify the port as it will be assigned by the system. 75 | 76 | ### Methods 77 | 78 | - `void Start(ushort port, bool enableBroadcast = false)` 79 | - Important: Must be called before any other operation. Starts listening on the specified port with optional broadcast capability. 80 | 81 | - `void Stop()` 82 | - Stops listening and closes the socket. 83 | 84 | - `void Receive()` 85 | - Receives and processes all pending packets. 86 | - Important: must be called in an infinite loop or in updates to constantly receive notifications if they have been received. 87 | 88 | - `void TickUpdate()` 89 | - Update the status of connections. 90 | - Important: must be called in an infinite loop or in updates to process received messages or status. 91 | - Important: this method is a combination of methods for the UpdateTimer, UpdateDisconnecting server, so do not call them as conflicts will occur. 92 | 93 | - `void SendBroadcast(ushort port, byte[] packet, int length)` 94 | - Sends a broadcast message if `EnableBroadcast` is `true`. 95 | 96 | - `void SendUnconnected(EndPoint endPoint, byte[] packet, int length)` 97 | - Sends a message without establishing a connection. 98 | 99 | - `void SendBroadcast(ushort port, Packet packet)` `Extensions` 100 | - Sends a broadcast message if `EnableBroadcast` is `true`. 101 | 102 | - `void SendUnconnected(EndPoint endPoint, Packet packet)` `Extensions` 103 | - Sends a message without establishing a connection. 104 | 105 | ## Log 106 | 107 | The `UdpLog` class has additional properties, methods, and events specific to server functionality: 108 | 109 | ### Methods 110 | 111 | - `void Initialize(Log logInfo, Log logWarning = null, Log logError = null)` 112 | - Initializing logging for Server and Client. 113 | 114 | ## Server 115 | 116 | The `Server` class has additional properties, methods, and events specific to server functionality: 117 | 118 | ### Properties 119 | 120 | - `uint TimeOut = 5000` 121 | - The time in milliseconds after which you can assume that the client has disconnected from the network and can be disconnected. 122 | 123 | - `uint MaxConnections = 256` 124 | - Maximum number of possible simultaneous connections. 125 | 126 | - `string KeyConnection = ""` 127 | - Server key, when clients try to establish a connection to the server, they must have the correct key for the server to allow the connection. 128 | 129 | - `uint MaxNumberId = ushort.MaxValue` 130 | - Limits the maximum number of identifiers that can be issued. 131 | - Note: It is desirable that the number of identifiers exceed the maximum number of connections by 2 times to reduce the likelihood of identifier collisions; in the event of a collision, a new available one is searched for. 132 | 133 | - `uint ConnectionsCount` `Read only` 134 | - The current number of connections to the server. 135 | 136 | ### Callbacks 137 | 138 | - `Action OnConnected` 139 | - Called when someone joins the server. 140 | 141 | - `Action OnDisconnected` 142 | - Called when someone leaves the server. 143 | 144 | - `Action OnReceiveReliable` 145 | - Invoked when a reliable message is received. 146 | 147 | - `Action OnReceiveUnreliable` 148 | - Invoked when an unreliable message is received. 149 | 150 | ### Methods 151 | 152 | - `void Disconnect(uint peerId)` 153 | - Forcibly disconnect from the client's server. 154 | 155 | - `void SendReliable(uint peerId, Packet packet)` `Extensions` 156 | - Sends a reliable message to peer. 157 | 158 | - `void SendUnreliable(uint peerId, Packet packet)` `Extensions` 159 | - Sends an unreliable message to peer. 160 | 161 | - `void SendReliable(uint peerId, byte[] packet)` 162 | - Sends a reliable message to peer. 163 | 164 | - `void SendUnreliable(uint peerId, byte[] packet)` 165 | - Sends an unreliable message to peer. 166 | 167 | - `void SendReliable(uint peerId, byte[] packet, int length, int offset = 0)` 168 | - Sends a reliable message to peer. 169 | 170 | - `void SendUnreliable(uint peerId, byte[] packet, int length, int offset = 0)` 171 | - Sends an unreliable message to peer. 172 | 173 | - `void SendAllReliable(Packet packet, uint ignoreId = 0)` `Extensions` 174 | - Sends a reliable message to all peers, optionally ignoring one. 175 | 176 | - `void SendAllUnreliable(Packet packet, uint ignoreId = 0)` `Extensions` 177 | - Sends an unreliable message to all peers, optionally ignoring one. 178 | 179 | - `void SendAllReliable(byte[] packet, uint ignoreId = 0)` 180 | - Sends a reliable message to all peers, optionally ignoring one. 181 | 182 | - `void SendAllUnreliable(byte[] packet, uint ignoreId = 0)` 183 | - Sends an unreliable message to all peers, optionally ignoring one. 184 | 185 | - `void SendAllReliable(byte[] packet, int length, uint ignoreId = 0)` 186 | - Sends a reliable message to all peers, optionally ignoring one. 187 | 188 | - `void SendAllUnreliable(byte[] packet, int length, uint ignoreId = 0)` 189 | - Sends an unreliable message to all peers, optionally ignoring one. 190 | 191 | - `void SendAllReliable(byte[] packet, int length, int offset, uint ignoreId = 0)` 192 | - Sends a reliable message to all peers, optionally ignoring one. 193 | 194 | - `void SendAllUnreliable(byte[] packet, int length, int offset, uint ignoreId = 0)` 195 | - Sends an unreliable message to all peers, optionally ignoring one. 196 | 197 | - `void UpdateTimer(uint deltaTime)` 198 | - Updates only the timers. 199 | 200 | - `void UpdateDisconnecting()` 201 | - Updates only the disconnecting peers. 202 | 203 | ## Client 204 | 205 | The `Client` class has properties, methods, and events specific to client functionality: 206 | 207 | - `uint TimeOut = 5000` 208 | - The time in milliseconds after which the connection to the server can be considered disconnected. 209 | 210 | - `string KeyConnection = ""` 211 | - The client's key must be correct as on the server to attempt to establish a connection. 212 | 213 | - `uint Id` `Read only` 214 | - The client's ID is synchronized with the ID that was issued on the server. 215 | 216 | - `uint Rtt` `Read only` 217 | - The round-trip time, measured every 1000 milliseconds to check for connection status. 218 | 219 | - `State State` `Read only` 220 | - The connection state of the client. 221 | 222 | - `EndPoint EndPoint` `Read only` 223 | - IP address of the server 224 | 225 | - `Reason ReasonDisconnection` `Read only` 226 | - The reason why the client was disconnected from the server. 227 | 228 | ### Callbacks 229 | 230 | - `Action OnConnected` 231 | - Called when the connection is successful. 232 | 233 | - `Action OnDisconnected` 234 | - Called in case of disconnection. 235 | 236 | - `Action OnReceiveReliable` 237 | - Invoked when a reliable message is received. 238 | 239 | - `Action OnReceiveUnreliable` 240 | - Invoked when an unreliable message is received. 241 | 242 | ### Methods 243 | 244 | - `void Connect(string ipAddress, ushort port)` 245 | - Important: `Start()` must be called before `Connect()` for a correct connection attempt. 246 | 247 | - `void Disconnect()` 248 | - Notifies the server about the disconnection and then disconnects the connection. 249 | 250 | - `void QuietDisconnect()` 251 | - Immediately disconnects without notifying the server or stops any ongoing connection/disconnection actions. 252 | 253 | - `void SendReliable(Packet packet)` `Extensions` 254 | - Sends a reliable message to the host. 255 | 256 | - `void SendUnreliable(Packet packet)` `Extensions` 257 | - Sends an unreliable message to the host. 258 | 259 | - `void SendReliable(byte[] packet)` 260 | - Sends a reliable message to the server. 261 | 262 | - `void SendUnreliable(byte[] packet)` 263 | - Sends an unreliable message to the server. 264 | 265 | - `void SendReliable(byte[] packet, int length, int offset = 0)` 266 | - Sends a reliable message to the server. 267 | 268 | - `void SendUnreliable(byte[] packet, int length, int offset = 0)` 269 | - Sends an unreliable message to the server. 270 | 271 | ## Peer 272 | 273 | The `Peer` class is used in conjunction with the `Server` class for managing connected clients: 274 | 275 | ### Properties 276 | 277 | - `uint Id` `Read only` 278 | - The peer's ID. 279 | 280 | - `uint Rtt` `Read only` 281 | - The round-trip time, measured every 1000 milliseconds to check for connection status. 282 | 283 | - `State State` `Read only` 284 | - The connection state of the peer's. 285 | 286 | - `EndPoint EndPoint` `Read only` 287 | - IP address of the host 288 | 289 | - `Reason ReasonDisconnection` `Read only` 290 | - The reason why the peer was disconnected from the host. 291 | 292 | ### Methods 293 | 294 | - `void Disconnect()` 295 | - Notifies the host of the disconnection and then disconnects the connection. 296 | 297 | - `void QuietDisconnect()` 298 | - Immediately disconnects without notifying the server or stops any ongoing connection/disconnection actions. 299 | 300 | - `void UpdateTimer(uint deltaTime)` 301 | - Updates the timers for this peer. 302 | - Important: call it when you need to split peer updates across threads and do not call it when TickUpdate or UpdateTimer is called because there will be conflicts. 303 | 304 | - `void SendReliable(Packet packet)` `Extensions` 305 | - Sends a reliable message to the host. 306 | 307 | - `void SendUnreliable(Packet packet)` `Extensions` 308 | - Sends an unreliable message to the host. 309 | 310 | - `void SendReliable(byte[] packet)` 311 | - Sends a reliable message to the host. 312 | 313 | - `void SendUnreliable(byte[] packet)` 314 | - Sends an unreliable message to the host. 315 | 316 | - `void SendReliable(byte[] packet, int length, int offset = 0)` 317 | - Sends a reliable message to the host. 318 | 319 | - `void SendUnreliable(byte[] packet, int length, int offset = 0)` 320 | - Sends an unreliable message to the host. 321 | 322 | # Packet 323 | 324 | The `Packet` class in the SimpleUDP library provides functionality to create, write, read, and manage packets of data. It supports various data types and ensures efficient handling of network data. 325 | 326 | ## Properties 327 | 328 | - `int Offset` 329 | - Gets the current read offset in the packet. 330 | - `int Length` 331 | - Gets the current write offset in the packet. 332 | - `const ushort MaxSizeData = 1432` 333 | - The maximum size of data in the packet. 334 | - `byte[] Data` 335 | - The byte array that holds the packet data. 336 | 337 | ## Methods 338 | 339 | - `static Packet.Write(ushort maxSizeData = MaxSizeData)` 340 | - Creates a new writable packet with the specified maximum data size. 341 | - ```csharp 342 | Packet packet = Packet.Write(); 343 | ``` 344 | 345 | - `static Packet.Read(byte[] packet)` 346 | - Create a package to read. 347 | - ```csharp 348 | void Handler(byte[] packet) 349 | { 350 | Packet packet = Packet.Read(packet); 351 | } 352 | ``` 353 | 354 | - `static Packet.Read(byte[] packet, int length, int offset)` 355 | - Create a package to read. 356 | - ```csharp 357 | void Handler(byte[] packet) 358 | { 359 | Packet packet = Packet.Read(packet, packet.Length, 0); 360 | } 361 | ``` 362 | 363 | ### Bool 364 | 365 | - `static Packet Bool(bool value, ushort maxSizeData = 256)` 366 | - Creates a new packet and writes a bool value. 367 | - ```csharp 368 | Packet packet = Packet.Bool(true); 369 | ``` 370 | - `Packet Bool(bool value)` 371 | - Writes a bool value to the packet. 372 | - ```csharp 373 | packet.Bool(true); 374 | ``` 375 | - `bool Bool()` 376 | - Reads a bool value from the packet. 377 | - ```csharp 378 | bool value = packet.Bool(); 379 | ``` 380 | 381 | ### Byte 382 | 383 | - `static Packet Byte(byte value, ushort maxSizeData = 256)` 384 | - Creates a new packet and writes a byte value. 385 | - ```csharp 386 | Packet packet = Packet.Byte(1); 387 | ``` 388 | - `Packet Byte(byte value)` 389 | - Writes a byte value to the packet. 390 | - ```csharp 391 | packet.Byte(1); 392 | ``` 393 | - `byte Byte()` 394 | - Reads a byte value from the packet. 395 | - ```csharp 396 | byte value = packet.Byte(); 397 | ``` 398 | 399 | ### SByte 400 | 401 | - `static Packet SByte(sbyte value, ushort maxSizeData = 256)` 402 | - Creates a new packet and writes an sbyte value. 403 | - ```csharp 404 | Packet packet = Packet.SByte(-1); 405 | ``` 406 | - `Packet SByte(sbyte value)` 407 | - Writes an sbyte value to the packet. 408 | - ```csharp 409 | packet.SByte(-1); 410 | ``` 411 | - `sbyte SByte()` 412 | - Reads an sbyte value from the packet. 413 | - ```csharp 414 | sbyte value = packet.SByte(); 415 | ``` 416 | 417 | ### Short 418 | 419 | - `static Packet Short(short value, ushort maxSizeData = 256)` 420 | - Creates a new packet and writes a short value. 421 | - ```csharp 422 | Packet packet = Packet.Short(123); 423 | ``` 424 | - `Packet Short(short value)` 425 | - Writes a short value to the packet. 426 | - ```csharp 427 | packet.Short(123); 428 | ``` 429 | - `short Short()` 430 | - Reads a short value from the packet. 431 | - ```csharp 432 | short value = packet.Short(); 433 | ``` 434 | 435 | ### UShort 436 | 437 | - `static Packet UShort(ushort value, ushort maxSizeData = 256)` 438 | - Creates a new packet and writes a ushort value. 439 | - ```csharp 440 | Packet packet = Packet.UShort(123); 441 | ``` 442 | - `Packet UShort(ushort value)` 443 | - Writes a ushort value to the packet. 444 | - ```csharp 445 | packet.UShort(123); 446 | ``` 447 | - `ushort UShort()` 448 | - Reads a ushort value from the packet. 449 | - ```csharp 450 | ushort value = packet.UShort(); 451 | ``` 452 | 453 | ### Int 454 | 455 | - `static Packet Int(int value, ushort maxSizeData = 256)` 456 | - Creates a new packet and writes an int value. 457 | - ```csharp 458 | Packet packet = Packet.Int(123); 459 | ``` 460 | - `Packet Int(int value)` 461 | - Writes an int value to the packet. 462 | - ```csharp 463 | packet.Int(123); 464 | ``` 465 | - `int Int()` 466 | - Reads an int value from the packet. 467 | - ```csharp 468 | int value = packet.Int(); 469 | ``` 470 | 471 | ### UInt 472 | 473 | - `static Packet UInt(uint value, ushort maxSizeData = 256)` 474 | - Creates a new packet and writes a uint value. 475 | - ```csharp 476 | Packet packet = Packet.UInt(123U); 477 | ``` 478 | - `Packet UInt(uint value)` 479 | - Writes a uint value to the packet. 480 | - ```csharp 481 | packet.UInt(123U); 482 | ``` 483 | - `uint UInt()` 484 | - Reads a uint value from the packet. 485 | - ```csharp 486 | uint value = packet.UInt(); 487 | ``` 488 | 489 | ### Long 490 | 491 | - `static Packet Long(long value, ushort maxSizeData = 256)` 492 | - Creates a new packet and writes a long value. 493 | - ```csharp 494 | Packet packet = Packet.Long(123L); 495 | ``` 496 | - `Packet Long(long value)` 497 | - Writes a long value to the packet. 498 | - ```csharp 499 | packet.Long(123L); 500 | ``` 501 | - `long Long()` 502 | - Reads a long value from the packet. 503 | - ```csharp 504 | long value = packet.Long(); 505 | ``` 506 | 507 | ### ULong 508 | 509 | - `static Packet ULong(ulong value, ushort maxSizeData = 256)` 510 | - Creates a new packet and writes a ulong value. 511 | - ```csharp 512 | Packet packet = Packet.ULong(123UL); 513 | ``` 514 | - `Packet ULong(ulong value)` 515 | - Writes a ulong value to the packet. 516 | - ```csharp 517 | packet.ULong(123UL); 518 | ``` 519 | - `ulong ULong()` 520 | - Reads a ulong value from the packet. 521 | - ```csharp 522 | ulong value = packet.ULong(); 523 | ``` 524 | 525 | ### Float 526 | 527 | - `static Packet Float(float value, ushort maxSizeData = 256)` 528 | - Creates a new packet and writes a float value. 529 | - ```csharp 530 | Packet packet = Packet.Float(123.45f); 531 | ``` 532 | - `Packet Float(float value)` 533 | - Writes a float value to the packet. 534 | - ```csharp 535 | packet.Float(123.45f); 536 | ``` 537 | - `float Float()` 538 | - Reads a float value from the packet. 539 | - ```csharp 540 | float value = packet.Float(); 541 | ``` 542 | 543 | ### Double 544 | 545 | - `static Packet Double(double value, ushort maxSizeData = 256)` 546 | - Creates a new packet and writes a double value. 547 | - ```csharp 548 | Packet packet = Packet.Double(123.45); 549 | ``` 550 | - `Packet Double(double value)` 551 | - Writes a double value to the packet. 552 | - ```csharp 553 | packet.Double(123.45); 554 | ``` 555 | - `double Double()` 556 | - Reads a double value from the packet. 557 | - ```csharp 558 | double value = packet.Double(); 559 | ``` 560 | 561 | ### Char 562 | 563 | - `static Packet Char(char value, ushort maxSizeData = MaxSizeData)` 564 | - Creates a new packet and writes a char value. 565 | - ```csharp 566 | Packet packet = Packet.Char('C'); 567 | ``` 568 | - `Packet Char(char value)` 569 | - Writes a char value to the packet. 570 | - ```csharp 571 | packet.Char('C'); 572 | ``` 573 | - `string Char()` 574 | - Reads a char value from the packet. 575 | - ```csharp 576 | char value = packet.Char(); 577 | ``` 578 | 579 | ### String 580 | 581 | - `static Packet String(string value, ushort maxSizeData = MaxSizeData)` 582 | - Creates a new packet and writes a string value. 583 | - ```csharp 584 | Packet packet = Packet.String("Hello, world!"); 585 | ``` 586 | - `Packet String(string value)` 587 | - Writes a string value to the packet. 588 | - ```csharp 589 | packet.String("Hello, world!"); 590 | ``` 591 | - `string String()` 592 | - Reads a string value from the packet. 593 | - ```csharp 594 | string value = packet.String(); 595 | ``` 596 | 597 | ## License 598 | 599 | This library is available under the MIT License. See the [LICENSE](LICENSE) file for more information. 600 | 601 | --- 602 | 603 | For more details, examples, and updates, visit the [GitHub repository](https://github.com/StrumDev/SimpleUDP) and the [Unity Asset Store page](https://assetstore.unity.com). 604 | -------------------------------------------------------------------------------- /Examples/Game.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 26a864201a659aa47ab39b12dc990d24 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/FPS Score.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71a1f4d0540bdff4084986f6cafac9b6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/FPS Score/FPSDisplayer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace SimpleUDP.Examples 6 | { 7 | [RequireComponent(typeof (Text))] 8 | public class FPSDisplayer : MonoBehaviour 9 | { 10 | public ushort MaxFPS = 360; 11 | 12 | const float FpsMeasurePeriod = 0.5f; 13 | const string Display = "FPS: {0}"; 14 | 15 | private Text m_Text; 16 | private int m_FpsAccumulator = 0; 17 | private float m_FpsNextPeriod = 0; 18 | private int m_CurrentFps; 19 | 20 | 21 | private void Start() 22 | { 23 | Application.targetFrameRate = MaxFPS; 24 | 25 | m_FpsNextPeriod = Time.realtimeSinceStartup + FpsMeasurePeriod; 26 | m_Text = GetComponent(); 27 | } 28 | 29 | private void Update() 30 | { 31 | m_FpsAccumulator++; 32 | 33 | if (Time.realtimeSinceStartup > m_FpsNextPeriod) 34 | { 35 | m_CurrentFps = (int) (m_FpsAccumulator / FpsMeasurePeriod); 36 | m_FpsAccumulator = 0; 37 | m_FpsNextPeriod += FpsMeasurePeriod; 38 | m_Text.text = string.Format(Display, m_CurrentFps); 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /Examples/Game/FPS Score/FPSDisplayer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5c8d6427090f3c14889d7de331705780 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Game/FPS Score/FPSScore.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &165316175010213851 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 165316175010213852} 12 | - component: {fileID: 165316175010213824} 13 | - component: {fileID: 9159898965712566242} 14 | - component: {fileID: 4077751500556843882} 15 | m_Layer: 5 16 | m_Name: FPSScore 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!224 &165316175010213852 23 | RectTransform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 165316175010213851} 29 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 30 | m_LocalPosition: {x: 0, y: 0, z: 0} 31 | m_LocalScale: {x: 1, y: 1, z: 1} 32 | m_Children: [] 33 | m_Father: {fileID: 0} 34 | m_RootOrder: 0 35 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 36 | m_AnchorMin: {x: 1, y: 1} 37 | m_AnchorMax: {x: 1, y: 1} 38 | m_AnchoredPosition: {x: -73.49719, y: -17.22229} 39 | m_SizeDelta: {x: 146.99426, y: 34.44452} 40 | m_Pivot: {x: 0.5, y: 0.5} 41 | --- !u!222 &165316175010213824 42 | CanvasRenderer: 43 | m_ObjectHideFlags: 0 44 | m_CorrespondingSourceObject: {fileID: 0} 45 | m_PrefabInstance: {fileID: 0} 46 | m_PrefabAsset: {fileID: 0} 47 | m_GameObject: {fileID: 165316175010213851} 48 | m_CullTransparentMesh: 1 49 | --- !u!114 &9159898965712566242 50 | MonoBehaviour: 51 | m_ObjectHideFlags: 0 52 | m_CorrespondingSourceObject: {fileID: 0} 53 | m_PrefabInstance: {fileID: 0} 54 | m_PrefabAsset: {fileID: 0} 55 | m_GameObject: {fileID: 165316175010213851} 56 | m_Enabled: 1 57 | m_EditorHideFlags: 0 58 | m_Script: {fileID: 11500000, guid: 5c8d6427090f3c14889d7de331705780, type: 3} 59 | m_Name: 60 | m_EditorClassIdentifier: 61 | MaxFPS: 360 62 | --- !u!114 &4077751500556843882 63 | MonoBehaviour: 64 | m_ObjectHideFlags: 0 65 | m_CorrespondingSourceObject: {fileID: 0} 66 | m_PrefabInstance: {fileID: 0} 67 | m_PrefabAsset: {fileID: 0} 68 | m_GameObject: {fileID: 165316175010213851} 69 | m_Enabled: 1 70 | m_EditorHideFlags: 0 71 | m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} 72 | m_Name: 73 | m_EditorClassIdentifier: 74 | m_Material: {fileID: 0} 75 | m_Color: {r: 1, g: 1, b: 1, a: 1} 76 | m_RaycastTarget: 1 77 | m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} 78 | m_Maskable: 1 79 | m_OnCullStateChanged: 80 | m_PersistentCalls: 81 | m_Calls: [] 82 | m_FontData: 83 | m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} 84 | m_FontSize: 25 85 | m_FontStyle: 1 86 | m_BestFit: 0 87 | m_MinSize: 10 88 | m_MaxSize: 40 89 | m_Alignment: 4 90 | m_AlignByGeometry: 0 91 | m_RichText: 1 92 | m_HorizontalOverflow: 0 93 | m_VerticalOverflow: 0 94 | m_LineSpacing: 1 95 | m_Text: 'FPS: 60' 96 | -------------------------------------------------------------------------------- /Examples/Game/FPS Score/FPSScore.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3200de1ec52b3164388e047230fa61d8 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Examples/Game/Game.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9221cfd2389a78f4d825c57c2753a142 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9bba2a2967269aa47b30f2ce934764de 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Examples/Game/Game/Game.lighting: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!850595691 &4890085278179872738 4 | LightingSettings: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: Game 10 | serializedVersion: 3 11 | m_GIWorkflowMode: 1 12 | m_EnableBakedLightmaps: 0 13 | m_EnableRealtimeLightmaps: 0 14 | m_RealtimeEnvironmentLighting: 1 15 | m_BounceScale: 1 16 | m_AlbedoBoost: 1 17 | m_IndirectOutputScale: 1 18 | m_UsingShadowmask: 1 19 | m_BakeBackend: 1 20 | m_LightmapMaxSize: 1024 21 | m_BakeResolution: 40 22 | m_Padding: 2 23 | m_TextureCompression: 1 24 | m_AO: 0 25 | m_AOMaxDistance: 1 26 | m_CompAOExponent: 1 27 | m_CompAOExponentDirect: 0 28 | m_ExtractAO: 0 29 | m_MixedBakeMode: 2 30 | m_LightmapsBakeMode: 1 31 | m_FilterMode: 1 32 | m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} 33 | m_ExportTrainingData: 0 34 | m_TrainingDataDestination: TrainingData 35 | m_RealtimeResolution: 2 36 | m_ForceWhiteAlbedo: 0 37 | m_ForceUpdates: 0 38 | m_FinalGather: 0 39 | m_FinalGatherRayCount: 256 40 | m_FinalGatherFiltering: 1 41 | m_PVRCulling: 0 42 | m_PVRSampling: 1 43 | m_PVRDirectSampleCount: 32 44 | m_PVRSampleCount: 512 45 | m_PVREnvironmentSampleCount: 256 46 | m_PVREnvironmentReferencePointCount: 2048 47 | m_LightProbeSampleCountMultiplier: 4 48 | m_PVRBounces: 2 49 | m_PVRMinBounces: 1 50 | m_PVREnvironmentMIS: 0 51 | m_PVRFilteringMode: 1 52 | m_PVRDenoiserTypeDirect: 1 53 | m_PVRDenoiserTypeIndirect: 1 54 | m_PVRDenoiserTypeAO: 1 55 | m_PVRFilterTypeDirect: 0 56 | m_PVRFilterTypeIndirect: 0 57 | m_PVRFilterTypeAO: 0 58 | m_PVRFilteringGaussRadiusDirect: 1 59 | m_PVRFilteringGaussRadiusIndirect: 5 60 | m_PVRFilteringGaussRadiusAO: 2 61 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 62 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 63 | m_PVRFilteringAtrousPositionSigmaAO: 1 64 | -------------------------------------------------------------------------------- /Examples/Game/Game/Game.lighting.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b786329d24a4bc4b94eb1e7011ac737 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 4890085278179872738 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game/GrassLayer.terrainlayer: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1953259897 &8574412962073106934 4 | TerrainLayer: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_Name: GrassLayer 10 | m_DiffuseTexture: {fileID: 2800000, guid: 3e66a31e563fe084b9f3d7ef8aa44966, type: 3} 11 | m_NormalMapTexture: {fileID: 0} 12 | m_MaskMapTexture: {fileID: 0} 13 | m_TileSize: {x: 10, y: 10} 14 | m_TileOffset: {x: 0, y: 0} 15 | m_Specular: {r: 0, g: 0, b: 0, a: 0} 16 | m_Metallic: 0.35 17 | m_Smoothness: 0 18 | m_NormalScale: 1 19 | m_DiffuseRemapMin: {x: 0, y: 0, z: 0, w: 0} 20 | m_DiffuseRemapMax: {x: 1, y: 1, z: 1, w: 1} 21 | m_MaskMapRemapMin: {x: 0, y: 0, z: 0, w: 0} 22 | m_MaskMapRemapMax: {x: 1, y: 1, z: 1, w: 1} 23 | -------------------------------------------------------------------------------- /Examples/Game/Game/GrassLayer.terrainlayer.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4c767cb1a3925a54eb146f79ec825d14 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 8574412962073106934 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game/LightingData.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrumDev/SimpleUDP/9c8d9bcae5f3b2867a24db2d3818e012a9fbb7af/Examples/Game/Game/LightingData.asset -------------------------------------------------------------------------------- /Examples/Game/Game/LightingData.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7487bbdcb63487e4d8736c8837589a2e 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 112000000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game/New Terrain.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrumDev/SimpleUDP/9c8d9bcae5f3b2867a24db2d3818e012a9fbb7af/Examples/Game/Game/New Terrain.asset -------------------------------------------------------------------------------- /Examples/Game/Game/New Terrain.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 069b91532e5bdcf45a72c5769d35223f 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 15600000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game/Player.mat: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!21 &2100000 4 | Material: 5 | serializedVersion: 6 6 | m_ObjectHideFlags: 0 7 | m_CorrespondingSourceObject: {fileID: 0} 8 | m_PrefabInstance: {fileID: 0} 9 | m_PrefabAsset: {fileID: 0} 10 | m_Name: Player 11 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} 12 | m_ShaderKeywords: 13 | m_LightmapFlags: 4 14 | m_EnableInstancingVariants: 0 15 | m_DoubleSidedGI: 0 16 | m_CustomRenderQueue: -1 17 | stringTagMap: {} 18 | disabledShaderPasses: [] 19 | m_SavedProperties: 20 | serializedVersion: 3 21 | m_TexEnvs: 22 | - _BumpMap: 23 | m_Texture: {fileID: 0} 24 | m_Scale: {x: 1, y: 1} 25 | m_Offset: {x: 0, y: 0} 26 | - _DetailAlbedoMap: 27 | m_Texture: {fileID: 0} 28 | m_Scale: {x: 1, y: 1} 29 | m_Offset: {x: 0, y: 0} 30 | - _DetailMask: 31 | m_Texture: {fileID: 0} 32 | m_Scale: {x: 1, y: 1} 33 | m_Offset: {x: 0, y: 0} 34 | - _DetailNormalMap: 35 | m_Texture: {fileID: 0} 36 | m_Scale: {x: 1, y: 1} 37 | m_Offset: {x: 0, y: 0} 38 | - _EmissionMap: 39 | m_Texture: {fileID: 0} 40 | m_Scale: {x: 1, y: 1} 41 | m_Offset: {x: 0, y: 0} 42 | - _MainTex: 43 | m_Texture: {fileID: 0} 44 | m_Scale: {x: 1, y: 1} 45 | m_Offset: {x: 0, y: 0} 46 | - _MetallicGlossMap: 47 | m_Texture: {fileID: 0} 48 | m_Scale: {x: 1, y: 1} 49 | m_Offset: {x: 0, y: 0} 50 | - _OcclusionMap: 51 | m_Texture: {fileID: 0} 52 | m_Scale: {x: 1, y: 1} 53 | m_Offset: {x: 0, y: 0} 54 | - _ParallaxMap: 55 | m_Texture: {fileID: 0} 56 | m_Scale: {x: 1, y: 1} 57 | m_Offset: {x: 0, y: 0} 58 | m_Floats: 59 | - _BumpScale: 1 60 | - _Cutoff: 0.5 61 | - _DetailNormalMapScale: 1 62 | - _DstBlend: 0 63 | - _GlossMapScale: 1 64 | - _Glossiness: 0 65 | - _GlossyReflections: 1 66 | - _Metallic: 0 67 | - _Mode: 0 68 | - _OcclusionStrength: 1 69 | - _Parallax: 0.02 70 | - _SmoothnessTextureChannel: 0 71 | - _SpecularHighlights: 1 72 | - _SrcBlend: 1 73 | - _UVSec: 0 74 | - _ZWrite: 1 75 | m_Colors: 76 | - _Color: {r: 0.08466991, g: 0.5471698, b: 0.05420078, a: 1} 77 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} 78 | m_BuildTextureStacks: [] 79 | -------------------------------------------------------------------------------- /Examples/Game/Game/Player.mat.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 52892272eb86b6c4eb85285f3b7a0b30 3 | NativeFormatImporter: 4 | externalObjects: {} 5 | mainObjectFileID: 2100000 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Game/ReflectionProbe-0.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrumDev/SimpleUDP/9c8d9bcae5f3b2867a24db2d3818e012a9fbb7af/Examples/Game/Game/ReflectionProbe-0.exr -------------------------------------------------------------------------------- /Examples/Game/Game/ReflectionProbe-0.exr.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3772dfa074dd12649803d50d1af29c3d 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 12 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | isReadable: 0 24 | streamingMipmaps: 0 25 | streamingMipmapsPriority: 0 26 | vTOnly: 0 27 | grayScaleToAlpha: 0 28 | generateCubemap: 6 29 | cubemapConvolution: 1 30 | seamlessCubemap: 1 31 | textureFormat: 1 32 | maxTextureSize: 2048 33 | textureSettings: 34 | serializedVersion: 2 35 | filterMode: 2 36 | aniso: 0 37 | mipBias: 0 38 | wrapU: 1 39 | wrapV: 1 40 | wrapW: 1 41 | nPOTScale: 1 42 | lightmap: 0 43 | compressionQuality: 50 44 | spriteMode: 0 45 | spriteExtrude: 1 46 | spriteMeshType: 1 47 | alignment: 0 48 | spritePivot: {x: 0.5, y: 0.5} 49 | spritePixelsToUnits: 100 50 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 51 | spriteGenerateFallbackPhysicsShape: 1 52 | alphaUsage: 1 53 | alphaIsTransparency: 0 54 | spriteTessellationDetail: -1 55 | textureType: 0 56 | textureShape: 2 57 | singleChannelComponent: 0 58 | flipbookRows: 1 59 | flipbookColumns: 1 60 | maxTextureSizeSet: 0 61 | compressionQualitySet: 0 62 | textureFormatSet: 0 63 | ignorePngGamma: 0 64 | applyGammaDecoding: 0 65 | cookieLightType: 0 66 | platformSettings: 67 | - serializedVersion: 3 68 | buildTarget: DefaultTexturePlatform 69 | maxTextureSize: 2048 70 | resizeAlgorithm: 0 71 | textureFormat: -1 72 | textureCompression: 1 73 | compressionQuality: 100 74 | crunchedCompression: 0 75 | allowsAlphaSplitting: 0 76 | overridden: 0 77 | androidETC2FallbackOverride: 0 78 | forceMaximumCompressionQuality_BC6H_BC7: 0 79 | - serializedVersion: 3 80 | buildTarget: Standalone 81 | maxTextureSize: 2048 82 | resizeAlgorithm: 0 83 | textureFormat: -1 84 | textureCompression: 1 85 | compressionQuality: 50 86 | crunchedCompression: 0 87 | allowsAlphaSplitting: 0 88 | overridden: 0 89 | androidETC2FallbackOverride: 0 90 | forceMaximumCompressionQuality_BC6H_BC7: 0 91 | - serializedVersion: 3 92 | buildTarget: Android 93 | maxTextureSize: 2048 94 | resizeAlgorithm: 0 95 | textureFormat: -1 96 | textureCompression: 1 97 | compressionQuality: 50 98 | crunchedCompression: 0 99 | allowsAlphaSplitting: 0 100 | overridden: 0 101 | androidETC2FallbackOverride: 0 102 | forceMaximumCompressionQuality_BC6H_BC7: 0 103 | spriteSheet: 104 | serializedVersion: 2 105 | sprites: [] 106 | outline: [] 107 | physicsShape: [] 108 | bones: [] 109 | spriteID: 110 | internalID: 0 111 | vertices: [] 112 | indices: 113 | edges: [] 114 | weights: [] 115 | secondaryTextures: [] 116 | spritePackingTag: 117 | pSDRemoveMatte: 0 118 | pSDShowRemoveMatteOption: 0 119 | userData: 120 | assetBundleName: 121 | assetBundleVariant: 122 | -------------------------------------------------------------------------------- /Examples/Game/Game/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StrumDev/SimpleUDP/9c8d9bcae5f3b2867a24db2d3818e012a9fbb7af/Examples/Game/Game/grass.png -------------------------------------------------------------------------------- /Examples/Game/Game/grass.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e66a31e563fe084b9f3d7ef8aa44966 3 | TextureImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 13 7 | mipmaps: 8 | mipMapMode: 0 9 | enableMipMap: 1 10 | sRGBTexture: 1 11 | linearTexture: 0 12 | fadeOut: 0 13 | borderMipMap: 0 14 | mipMapsPreserveCoverage: 0 15 | alphaTestReferenceValue: 0.5 16 | mipMapFadeDistanceStart: 1 17 | mipMapFadeDistanceEnd: 3 18 | bumpmap: 19 | convertToNormalMap: 0 20 | externalNormalMap: 0 21 | heightScale: 0.25 22 | normalMapFilter: 0 23 | flipGreenChannel: 0 24 | isReadable: 0 25 | streamingMipmaps: 0 26 | streamingMipmapsPriority: 0 27 | vTOnly: 0 28 | ignoreMipmapLimit: 0 29 | grayScaleToAlpha: 0 30 | generateCubemap: 6 31 | cubemapConvolution: 0 32 | seamlessCubemap: 0 33 | textureFormat: 1 34 | maxTextureSize: 2048 35 | textureSettings: 36 | serializedVersion: 2 37 | filterMode: 1 38 | aniso: 1 39 | mipBias: 0 40 | wrapU: 0 41 | wrapV: 0 42 | wrapW: 0 43 | nPOTScale: 1 44 | lightmap: 0 45 | compressionQuality: 50 46 | spriteMode: 0 47 | spriteExtrude: 1 48 | spriteMeshType: 1 49 | alignment: 0 50 | spritePivot: {x: 0.5, y: 0.5} 51 | spritePixelsToUnits: 100 52 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 53 | spriteGenerateFallbackPhysicsShape: 1 54 | alphaUsage: 1 55 | alphaIsTransparency: 0 56 | spriteTessellationDetail: -1 57 | textureType: 0 58 | textureShape: 1 59 | singleChannelComponent: 0 60 | flipbookRows: 1 61 | flipbookColumns: 1 62 | maxTextureSizeSet: 0 63 | compressionQualitySet: 0 64 | textureFormatSet: 0 65 | ignorePngGamma: 0 66 | applyGammaDecoding: 0 67 | swizzle: 50462976 68 | cookieLightType: 0 69 | platformSettings: 70 | - serializedVersion: 3 71 | buildTarget: DefaultTexturePlatform 72 | maxTextureSize: 2048 73 | resizeAlgorithm: 0 74 | textureFormat: -1 75 | textureCompression: 1 76 | compressionQuality: 50 77 | crunchedCompression: 0 78 | allowsAlphaSplitting: 0 79 | overridden: 0 80 | ignorePlatformSupport: 0 81 | androidETC2FallbackOverride: 0 82 | forceMaximumCompressionQuality_BC6H_BC7: 0 83 | - serializedVersion: 3 84 | buildTarget: Standalone 85 | maxTextureSize: 2048 86 | resizeAlgorithm: 0 87 | textureFormat: -1 88 | textureCompression: 1 89 | compressionQuality: 50 90 | crunchedCompression: 0 91 | allowsAlphaSplitting: 0 92 | overridden: 0 93 | ignorePlatformSupport: 0 94 | androidETC2FallbackOverride: 0 95 | forceMaximumCompressionQuality_BC6H_BC7: 0 96 | - serializedVersion: 3 97 | buildTarget: Android 98 | maxTextureSize: 2048 99 | resizeAlgorithm: 0 100 | textureFormat: -1 101 | textureCompression: 1 102 | compressionQuality: 50 103 | crunchedCompression: 0 104 | allowsAlphaSplitting: 0 105 | overridden: 0 106 | ignorePlatformSupport: 0 107 | androidETC2FallbackOverride: 0 108 | forceMaximumCompressionQuality_BC6H_BC7: 0 109 | spriteSheet: 110 | serializedVersion: 2 111 | sprites: [] 112 | outline: [] 113 | physicsShape: [] 114 | bones: [] 115 | spriteID: 116 | internalID: 0 117 | vertices: [] 118 | indices: 119 | edges: [] 120 | weights: [] 121 | secondaryTextures: [] 122 | nameFileIdTable: {} 123 | mipmapLimitGroupName: 124 | pSDRemoveMatte: 0 125 | userData: 126 | assetBundleName: 127 | assetBundleVariant: 128 | -------------------------------------------------------------------------------- /Examples/Game/Player.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &2169285722883299368 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 2169285722883299369} 12 | - component: {fileID: 2169285722883299372} 13 | - component: {fileID: 2169285722883299375} 14 | - component: {fileID: 2169285722883299374} 15 | m_Layer: 0 16 | m_Name: Cube 17 | m_TagString: Untagged 18 | m_Icon: {fileID: 0} 19 | m_NavMeshLayer: 0 20 | m_StaticEditorFlags: 0 21 | m_IsActive: 1 22 | --- !u!4 &2169285722883299369 23 | Transform: 24 | m_ObjectHideFlags: 0 25 | m_CorrespondingSourceObject: {fileID: 0} 26 | m_PrefabInstance: {fileID: 0} 27 | m_PrefabAsset: {fileID: 0} 28 | m_GameObject: {fileID: 2169285722883299368} 29 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 30 | m_LocalPosition: {x: 0, y: 0.5, z: 0} 31 | m_LocalScale: {x: 1, y: 1, z: 1} 32 | m_Children: 33 | - {fileID: 5637592383150267855} 34 | m_Father: {fileID: 2169285723540056514} 35 | m_RootOrder: 0 36 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 37 | --- !u!33 &2169285722883299372 38 | MeshFilter: 39 | m_ObjectHideFlags: 0 40 | m_CorrespondingSourceObject: {fileID: 0} 41 | m_PrefabInstance: {fileID: 0} 42 | m_PrefabAsset: {fileID: 0} 43 | m_GameObject: {fileID: 2169285722883299368} 44 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 45 | --- !u!23 &2169285722883299375 46 | MeshRenderer: 47 | m_ObjectHideFlags: 0 48 | m_CorrespondingSourceObject: {fileID: 0} 49 | m_PrefabInstance: {fileID: 0} 50 | m_PrefabAsset: {fileID: 0} 51 | m_GameObject: {fileID: 2169285722883299368} 52 | m_Enabled: 1 53 | m_CastShadows: 1 54 | m_ReceiveShadows: 1 55 | m_DynamicOccludee: 1 56 | m_MotionVectors: 1 57 | m_LightProbeUsage: 1 58 | m_ReflectionProbeUsage: 1 59 | m_RayTracingMode: 2 60 | m_RayTraceProcedural: 0 61 | m_RenderingLayerMask: 1 62 | m_RendererPriority: 0 63 | m_Materials: 64 | - {fileID: 2100000, guid: 52892272eb86b6c4eb85285f3b7a0b30, type: 2} 65 | m_StaticBatchInfo: 66 | firstSubMesh: 0 67 | subMeshCount: 0 68 | m_StaticBatchRoot: {fileID: 0} 69 | m_ProbeAnchor: {fileID: 0} 70 | m_LightProbeVolumeOverride: {fileID: 0} 71 | m_ScaleInLightmap: 1 72 | m_ReceiveGI: 1 73 | m_PreserveUVs: 0 74 | m_IgnoreNormalsForChartDetection: 0 75 | m_ImportantGI: 0 76 | m_StitchLightmapSeams: 1 77 | m_SelectedEditorRenderState: 3 78 | m_MinimumChartSize: 4 79 | m_AutoUVMaxDistance: 0.5 80 | m_AutoUVMaxAngle: 89 81 | m_LightmapParameters: {fileID: 0} 82 | m_SortingLayerID: 0 83 | m_SortingLayer: 0 84 | m_SortingOrder: 0 85 | m_AdditionalVertexStreams: {fileID: 0} 86 | --- !u!65 &2169285722883299374 87 | BoxCollider: 88 | m_ObjectHideFlags: 0 89 | m_CorrespondingSourceObject: {fileID: 0} 90 | m_PrefabInstance: {fileID: 0} 91 | m_PrefabAsset: {fileID: 0} 92 | m_GameObject: {fileID: 2169285722883299368} 93 | m_Material: {fileID: 0} 94 | m_IsTrigger: 0 95 | m_Enabled: 1 96 | serializedVersion: 2 97 | m_Size: {x: 1, y: 1, z: 1} 98 | m_Center: {x: 0, y: 0, z: 0} 99 | --- !u!1 &2169285723540056541 100 | GameObject: 101 | m_ObjectHideFlags: 0 102 | m_CorrespondingSourceObject: {fileID: 0} 103 | m_PrefabInstance: {fileID: 0} 104 | m_PrefabAsset: {fileID: 0} 105 | serializedVersion: 6 106 | m_Component: 107 | - component: {fileID: 2169285723540056514} 108 | - component: {fileID: 2169285723540056515} 109 | m_Layer: 0 110 | m_Name: Player 111 | m_TagString: Untagged 112 | m_Icon: {fileID: 0} 113 | m_NavMeshLayer: 0 114 | m_StaticEditorFlags: 0 115 | m_IsActive: 1 116 | --- !u!4 &2169285723540056514 117 | Transform: 118 | m_ObjectHideFlags: 0 119 | m_CorrespondingSourceObject: {fileID: 0} 120 | m_PrefabInstance: {fileID: 0} 121 | m_PrefabAsset: {fileID: 0} 122 | m_GameObject: {fileID: 2169285723540056541} 123 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 124 | m_LocalPosition: {x: 0, y: 0, z: 0} 125 | m_LocalScale: {x: 1, y: 1, z: 1} 126 | m_Children: 127 | - {fileID: 2169285722883299369} 128 | m_Father: {fileID: 0} 129 | m_RootOrder: 0 130 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 131 | --- !u!114 &2169285723540056515 132 | MonoBehaviour: 133 | m_ObjectHideFlags: 0 134 | m_CorrespondingSourceObject: {fileID: 0} 135 | m_PrefabInstance: {fileID: 0} 136 | m_PrefabAsset: {fileID: 0} 137 | m_GameObject: {fileID: 2169285723540056541} 138 | m_Enabled: 1 139 | m_EditorHideFlags: 0 140 | m_Script: {fileID: 11500000, guid: 658e95baeeb377d438d19745a324b3ec, type: 3} 141 | m_Name: 142 | m_EditorClassIdentifier: 143 | Speed: 8 144 | RotateSpeed: 180 145 | ClientId: 0 146 | IsLocal: 0 147 | --- !u!1 &8137002170797993707 148 | GameObject: 149 | m_ObjectHideFlags: 0 150 | m_CorrespondingSourceObject: {fileID: 0} 151 | m_PrefabInstance: {fileID: 0} 152 | m_PrefabAsset: {fileID: 0} 153 | serializedVersion: 6 154 | m_Component: 155 | - component: {fileID: 5637592383150267855} 156 | - component: {fileID: 3018366403561689057} 157 | - component: {fileID: 3374138088311572550} 158 | - component: {fileID: 5953461289220523850} 159 | m_Layer: 0 160 | m_Name: Cube 161 | m_TagString: Untagged 162 | m_Icon: {fileID: 0} 163 | m_NavMeshLayer: 0 164 | m_StaticEditorFlags: 0 165 | m_IsActive: 1 166 | --- !u!4 &5637592383150267855 167 | Transform: 168 | m_ObjectHideFlags: 0 169 | m_CorrespondingSourceObject: {fileID: 0} 170 | m_PrefabInstance: {fileID: 0} 171 | m_PrefabAsset: {fileID: 0} 172 | m_GameObject: {fileID: 8137002170797993707} 173 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 174 | m_LocalPosition: {x: 0, y: 0, z: 0.2} 175 | m_LocalScale: {x: 0.8, y: 0.8, z: 0.8} 176 | m_Children: [] 177 | m_Father: {fileID: 2169285722883299369} 178 | m_RootOrder: 0 179 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 180 | --- !u!33 &3018366403561689057 181 | MeshFilter: 182 | m_ObjectHideFlags: 0 183 | m_CorrespondingSourceObject: {fileID: 0} 184 | m_PrefabInstance: {fileID: 0} 185 | m_PrefabAsset: {fileID: 0} 186 | m_GameObject: {fileID: 8137002170797993707} 187 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 188 | --- !u!23 &3374138088311572550 189 | MeshRenderer: 190 | m_ObjectHideFlags: 0 191 | m_CorrespondingSourceObject: {fileID: 0} 192 | m_PrefabInstance: {fileID: 0} 193 | m_PrefabAsset: {fileID: 0} 194 | m_GameObject: {fileID: 8137002170797993707} 195 | m_Enabled: 1 196 | m_CastShadows: 1 197 | m_ReceiveShadows: 1 198 | m_DynamicOccludee: 1 199 | m_MotionVectors: 1 200 | m_LightProbeUsage: 1 201 | m_ReflectionProbeUsage: 1 202 | m_RayTracingMode: 2 203 | m_RayTraceProcedural: 0 204 | m_RenderingLayerMask: 1 205 | m_RendererPriority: 0 206 | m_Materials: 207 | - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} 208 | m_StaticBatchInfo: 209 | firstSubMesh: 0 210 | subMeshCount: 0 211 | m_StaticBatchRoot: {fileID: 0} 212 | m_ProbeAnchor: {fileID: 0} 213 | m_LightProbeVolumeOverride: {fileID: 0} 214 | m_ScaleInLightmap: 1 215 | m_ReceiveGI: 1 216 | m_PreserveUVs: 0 217 | m_IgnoreNormalsForChartDetection: 0 218 | m_ImportantGI: 0 219 | m_StitchLightmapSeams: 1 220 | m_SelectedEditorRenderState: 3 221 | m_MinimumChartSize: 4 222 | m_AutoUVMaxDistance: 0.5 223 | m_AutoUVMaxAngle: 89 224 | m_LightmapParameters: {fileID: 0} 225 | m_SortingLayerID: 0 226 | m_SortingLayer: 0 227 | m_SortingOrder: 0 228 | m_AdditionalVertexStreams: {fileID: 0} 229 | --- !u!65 &5953461289220523850 230 | BoxCollider: 231 | m_ObjectHideFlags: 0 232 | m_CorrespondingSourceObject: {fileID: 0} 233 | m_PrefabInstance: {fileID: 0} 234 | m_PrefabAsset: {fileID: 0} 235 | m_GameObject: {fileID: 8137002170797993707} 236 | m_Material: {fileID: 0} 237 | m_IsTrigger: 0 238 | m_Enabled: 1 239 | serializedVersion: 2 240 | m_Size: {x: 1, y: 1, z: 1} 241 | m_Center: {x: 0, y: 0, z: 0} 242 | -------------------------------------------------------------------------------- /Examples/Game/Player.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d29ae2cd8e72a0249871664a913e7a4b 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Examples/Game/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8f878e708aa24e547a4259bd1193e692 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkClient.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using UnityEngine; 3 | using System.Collections.Generic; 4 | 5 | namespace SimpleUDP.Examples 6 | { 7 | public class NetworkClient : MonoBehaviour 8 | { 9 | public GameObject PlayerPrefab; 10 | 11 | private NetworkPlayer localPlayer; 12 | private Dictionary players = new Dictionary(); 13 | 14 | private UdpClient Client => NetworkManager.Client; 15 | 16 | 17 | private void Awake() 18 | { 19 | Client.OnStarted += OnStarted; 20 | Client.OnStopped += OnStopped; 21 | 22 | Client.OnConnected += OnConnected; 23 | Client.OnDisconnected += OnDisconnected; 24 | 25 | Client.OnReceiveReliable += ReceiveHandler; 26 | Client.OnReceiveUnreliable += ReceiveHandler; 27 | } 28 | 29 | private void OnStarted() 30 | { 31 | players.Clear(); 32 | Debug.Log($"[Client] Started: {Client.LocalPort}"); 33 | } 34 | 35 | private void OnConnected() 36 | { 37 | localPlayer = Instantiate(PlayerPrefab).GetComponent(); 38 | 39 | localPlayer.IsLocal = true; 40 | localPlayer.ClientId = Client.Id; 41 | localPlayer.SendAsyncPosition(); 42 | 43 | Debug.Log($"[Client] Connected to: {Client.EndPoint}"); 44 | } 45 | 46 | private void OnDisconnected() 47 | { 48 | lock (players) 49 | { 50 | localPlayer?.Destroy(); 51 | 52 | foreach (NetworkPlayer player in players.Values) 53 | player.Destroy(); 54 | 55 | players.Clear(); 56 | 57 | Debug.Log($"[Client] Disconnected from: {Client.EndPoint}"); 58 | } 59 | } 60 | 61 | private void ReceiveHandler(byte[] packet) 62 | { 63 | Packet read = Packet.Read(packet); 64 | 65 | switch ((Header)read.Byte()) 66 | { 67 | case Header.Movement: 68 | Movement(read); 69 | return; 70 | case Header.ClientConnected: 71 | ClientConnected(read); 72 | return; 73 | case Header.ClientDisconnected: 74 | ClientDisconnected(read); 75 | return; 76 | } 77 | } 78 | 79 | private void Movement(Packet packet) 80 | { 81 | if (players.TryGetValue(packet.UInt(), out NetworkPlayer player)) 82 | player.SetTransform(packet); 83 | } 84 | 85 | private void ClientConnected(Packet packet) 86 | { 87 | lock (players) 88 | { 89 | NetworkPlayer player = Instantiate(PlayerPrefab).GetComponent(); 90 | 91 | player.IsLocal = false; 92 | player.ClientId = packet.UInt(); 93 | 94 | players.Add(player.ClientId, player); 95 | } 96 | } 97 | 98 | private void ClientDisconnected(Packet packet) 99 | { 100 | lock (players) 101 | { 102 | NetworkPlayer player = players[packet.UInt()]; 103 | 104 | player.Destroy(); 105 | players.Remove(player.ClientId); 106 | } 107 | } 108 | 109 | private void OnStopped() 110 | { 111 | Debug.Log($"[Client] Stopped!"); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkClient.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd27a1b1d4eb997488b324db45f7c0d1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkManager.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using UnityEngine; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Stopwatch = System.Diagnostics.Stopwatch; 6 | 7 | namespace SimpleUDP.Examples 8 | { 9 | public enum Header : byte 10 | { 11 | Movement, 12 | ClientConnected, 13 | ClientDisconnected, 14 | } 15 | public class NetworkManager : MonoBehaviour 16 | { 17 | public string IpAddress = "127.0.0.1"; 18 | public ushort Port = 12700; 19 | 20 | public static UdpServer Server; 21 | public static UdpClient Client; 22 | 23 | [Header("Diagnostics Server")] 24 | public uint ElapsedTickServer; 25 | public int AvailablePackageServer; 26 | 27 | [Header("Diagnostics Client")] 28 | public uint ElapsedTickClient; 29 | public int AvailablePackageClient; 30 | 31 | private bool isActive; 32 | private UIMenager uiMenager; 33 | 34 | private void Awake() 35 | { 36 | Server = new UdpServer(); 37 | Client = new UdpClient(); 38 | 39 | Client.OnConnected += OnConnected; 40 | Client.OnDisconnected += OnDisconnected; 41 | } 42 | 43 | private void Start() 44 | { 45 | isActive = true; 46 | 47 | StartUpdateAsyncClient(); 48 | 49 | uiMenager = GetComponent(); 50 | 51 | uiMenager.SetActionConnect("Connect", Connect); 52 | uiMenager.SetActionDisconnect("Disconnect", Disconnect); 53 | 54 | Client.Start(); 55 | } 56 | 57 | private void OnConnected() 58 | { 59 | uiMenager.Connected(); 60 | 61 | if (Server.IsRunning) 62 | { 63 | uiMenager.LocalPortText.gameObject.SetActive(true); 64 | uiMenager.LocalPortText.text = $"LocalPort: {Server.LocalPort}"; 65 | } 66 | } 67 | 68 | private void OnDisconnected() 69 | { 70 | uiMenager.Disconnected(); 71 | 72 | uiMenager.SetActionConnect("Connect", Connect); 73 | uiMenager.SetActionDisconnect("Disconnect", Disconnect); 74 | 75 | if (Server.IsRunning) 76 | Server.Stop(); 77 | 78 | uiMenager.LocalPortText.gameObject.SetActive(false); 79 | } 80 | 81 | public void CreateGame() 82 | { 83 | if (Server.IsAvailablePort(12700)) 84 | Server.Start(12700); 85 | else 86 | Server.Start(); 87 | 88 | StartUpdateThreadServer(); 89 | Client.Connect("127.0.0.1", Server.LocalPort); 90 | } 91 | 92 | public void Connect() 93 | { 94 | if (!string.IsNullOrEmpty(uiMenager.InputAddress.text)) 95 | { 96 | string[] str = uiMenager.InputAddress.text.Split(':', System.StringSplitOptions.RemoveEmptyEntries); 97 | 98 | if (str != null && str.Length != 0) 99 | { 100 | IpAddress = str[0]; 101 | 102 | if (str.Length >= 2) 103 | ushort.TryParse(str[1], out Port); 104 | else 105 | Port = 12700; 106 | } 107 | } 108 | 109 | uiMenager.SetActionConnect("Stop Connecting", Client.QuietDisconnect); 110 | Client.Connect(IpAddress, Port); 111 | } 112 | 113 | public void Disconnect() 114 | { 115 | uiMenager.SetActionDisconnect("Stop Disconnecting", Client.QuietDisconnect); 116 | Client.Disconnect(); 117 | } 118 | 119 | private void FixedUpdate() 120 | { 121 | if (Client.State == State.Connected) 122 | uiMenager.SetRttText(Client.Rtt); 123 | 124 | AvailablePackageServer = Server.AvailablePackages; 125 | AvailablePackageClient = Client.AvailablePackages; 126 | } 127 | 128 | private void StartUpdateThreadServer() 129 | { 130 | Stopwatch swServer = new Stopwatch(); 131 | 132 | new Thread(() => 133 | { 134 | while (isActive) 135 | { 136 | if (!Server.IsRunning) 137 | return; 138 | 139 | Smoothing(ref ElapsedTickServer, (uint)swServer.ElapsedMilliseconds, 0.2f); 140 | swServer.Restart(); 141 | 142 | if (Server.SocketPoll) 143 | Server.Receive(); 144 | 145 | Server.TickUpdate(); 146 | 147 | // 1000ms / 10 = TickRate: 100 148 | Thread.Sleep(10); 149 | } 150 | 151 | }).Start(); 152 | } 153 | 154 | private async void StartUpdateAsyncClient() 155 | { 156 | Stopwatch swClient = new Stopwatch(); 157 | 158 | while (isActive) 159 | { 160 | Smoothing(ref ElapsedTickClient, (uint)swClient.ElapsedMilliseconds, 0.2f); 161 | swClient.Restart(); 162 | 163 | // 1000ms / 20 = TickRate: 50 164 | Client.Receive(); 165 | Client.TickUpdate(); 166 | 167 | await Task.Delay(20); 168 | } 169 | } 170 | 171 | private void Smoothing(ref uint value, uint measuredValue, float smooth) 172 | { 173 | value = (uint)((1 - smooth) * value + smooth * measuredValue); 174 | } 175 | 176 | private void OnApplicationQuit() 177 | { 178 | isActive = false; 179 | 180 | Server.Stop(); 181 | Client.Stop(); 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b24fb2c036d33b847b618535525bff4e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkPlayer.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using UnityEngine; 3 | using System.Threading.Tasks; 4 | using Unity.Collections; 5 | 6 | namespace SimpleUDP.Examples 7 | { 8 | public class NetworkPlayer : MonoBehaviour 9 | { 10 | public float Speed = 8f; 11 | public float RotateSpeed = 180f; 12 | 13 | public uint ClientId { get; set; } 14 | public bool IsLocal { get; set; } 15 | 16 | private bool isDestroy; 17 | 18 | private bool moveToZero; 19 | private Vector3 newPosition; 20 | private Quaternion newQuaternion; 21 | private Packet packet = Packet.Write(32); 22 | 23 | private void Update() 24 | { 25 | float lerpSpeed = Speed * 1.3f; 26 | 27 | if (IsLocal) 28 | { 29 | if (!moveToZero) 30 | { 31 | newPosition = transform.position; 32 | 33 | if (Input.GetKeyDown(KeyCode.R)) 34 | { 35 | newPosition = Vector3.zero; 36 | moveToZero = true; 37 | } 38 | } 39 | else 40 | { 41 | if (Vector3.Distance(transform.position, Vector3.zero) <= 0.1f) 42 | moveToZero = false; 43 | 44 | transform.position = Vector3.Lerp(transform.position, Vector3.zero, lerpSpeed * Time.deltaTime); 45 | } 46 | 47 | return; 48 | } 49 | 50 | if (newPosition != transform.position) 51 | transform.position = Vector3.Lerp(transform.position, newPosition, lerpSpeed * Time.deltaTime); 52 | 53 | if (newQuaternion != transform.rotation) 54 | transform.rotation = Quaternion.Lerp(transform.rotation, newQuaternion, lerpSpeed * Time.deltaTime); 55 | } 56 | 57 | private void FixedUpdate() 58 | { 59 | if (IsLocal && !isDestroy && !moveToZero) 60 | { 61 | Vector2 input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")); 62 | 63 | transform.Translate(0, 0, input.y * Speed * Time.fixedDeltaTime); 64 | transform.Rotate(0, input.x * RotateSpeed * Time.fixedDeltaTime, 0); 65 | } 66 | 67 | if (isDestroy) 68 | Destroy(gameObject); 69 | } 70 | 71 | public async void SendAsyncPosition() 72 | { 73 | while (IsLocal) 74 | { 75 | if (isDestroy || this == null) 76 | return; 77 | 78 | // 1000ms / 40 = TickRate: 25 79 | NetworkManager.Client.SendReliable(GetTransform()); 80 | 81 | packet.Reset(); 82 | await Task.Delay(40); 83 | } 84 | } 85 | 86 | public void SetTransform(Packet packet) 87 | { 88 | newPosition = new Vector3(packet.Float(), packet.Float(), packet.Float()); 89 | newQuaternion = Quaternion.Euler(packet.Float(), packet.Float(), packet.Float()); 90 | } 91 | 92 | private Packet GetTransform() 93 | { 94 | packet.Byte((byte)Header.Movement); 95 | packet.UInt(ClientId); 96 | 97 | packet.Float(transform.position.x); 98 | packet.Float(transform.position.y); 99 | packet.Float(transform.position.z); 100 | 101 | packet.Float(transform.rotation.eulerAngles.x); 102 | packet.Float(transform.rotation.eulerAngles.y); 103 | packet.Float(transform.rotation.eulerAngles.z); 104 | 105 | return packet; 106 | } 107 | 108 | public void Destroy() 109 | { 110 | isDestroy = true; 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkPlayer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 658e95baeeb377d438d19745a324b3ec 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkServer.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using UnityEngine; 3 | using System.Collections.Generic; 4 | 5 | namespace SimpleUDP.Examples 6 | { 7 | public class NetworkServer : MonoBehaviour 8 | { 9 | public uint OnlinePlayers; 10 | 11 | private List players = new List(); 12 | 13 | private UdpServer Server => NetworkManager.Server; 14 | 15 | private void Awake() 16 | { 17 | Server.OnStarted += OnStarted; 18 | Server.OnStopped += OnStopped; 19 | 20 | Server.OnConnected += OnConnected; 21 | Server.OnDisconnected += OnDisconnected; 22 | 23 | Server.OnReceiveReliable += ReceiveHandler; 24 | Server.OnReceiveUnreliable += ReceiveHandler; 25 | } 26 | 27 | private void FixedUpdate() 28 | { 29 | OnlinePlayers = Server.ConnectionsCount; 30 | } 31 | 32 | private void OnStarted() 33 | { 34 | players.Clear(); 35 | Debug.Log($"[Server] Started: {Server.LocalPort}"); 36 | } 37 | 38 | private void OnConnected(UdpPeer peer) 39 | { 40 | lock (players) 41 | { 42 | players.Add(peer); 43 | 44 | SendConnectedClients(peer); 45 | 46 | Server.SendAllReliable(Packet.Byte((byte)Header.ClientConnected).UInt(peer.Id), peer.Id); 47 | 48 | Debug.Log($"[Server] Client Connected: {peer.Id}"); 49 | } 50 | } 51 | 52 | private void OnDisconnected(UdpPeer peer) 53 | { 54 | lock (players) 55 | { 56 | players.Remove(peer); 57 | 58 | Server.SendAllReliable(Packet.Byte((byte)Header.ClientDisconnected).UInt(peer.Id), peer.Id); 59 | 60 | Debug.Log($"[Server] Client Disconnected: {peer.Id}"); 61 | } 62 | } 63 | 64 | private void SendConnectedClients(UdpPeer peer) 65 | { 66 | lock (players) 67 | { 68 | foreach (UdpPeer player in players) 69 | { 70 | if (player.Id != peer.Id) 71 | peer.SendReliable(Packet.Byte((byte)Header.ClientConnected).UInt(player.Id)); 72 | } 73 | } 74 | } 75 | 76 | private void ReceiveHandler(UdpPeer peer, byte[] packet) 77 | { 78 | Packet read = Packet.Read(packet); 79 | 80 | switch ((Header)read.Byte()) 81 | { 82 | case Header.Movement: 83 | Movement(read, peer); 84 | return; 85 | } 86 | } 87 | 88 | private void Movement(Packet packet, UdpPeer peer) 89 | { 90 | Server.SendAllReliable(packet, peer.Id); 91 | } 92 | 93 | private void OnStopped() 94 | { 95 | players.Clear(); 96 | Debug.Log($"[Server] Stopped!"); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /Examples/Game/Scripts/NetworkServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d5384894dbfe9c4ea94d50011c97032 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/UIMenager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | 5 | namespace SimpleUDP 6 | { 7 | public class UIMenager : MonoBehaviour 8 | { 9 | [Header("UI Panels")] 10 | public GameObject MainPanel; 11 | public GameObject GamePanel; 12 | 13 | [Header("Game Panel")] 14 | public GameObject Pause; 15 | 16 | [Header("UI Elements")] 17 | public InputField InputAddress; 18 | 19 | [Header("UI Buttons")] 20 | public Text TextConnect; 21 | public Button ButtonConnect; 22 | public Text TextDisconnect; 23 | public Button ButtonDisconnect; 24 | 25 | [Header("UI Text")] 26 | public Text RttScore; 27 | public Text LocalPortText; 28 | 29 | public void Connected() 30 | { 31 | MainPanel.SetActive(false); 32 | GamePanel.SetActive(true); 33 | 34 | Pause.SetActive(false); 35 | } 36 | 37 | public void Disconnected() 38 | { 39 | MainPanel.SetActive(true); 40 | GamePanel.SetActive(false); 41 | 42 | Pause.SetActive(false); 43 | } 44 | 45 | public void SetRttText(uint rtt) 46 | { 47 | RttScore.text = $"Rtt: {rtt}ms"; 48 | } 49 | 50 | public void SetActionConnect(string text, Action action) 51 | { 52 | TextConnect.text = text; 53 | ButtonConnect.onClick.RemoveAllListeners(); 54 | ButtonConnect.onClick.AddListener(() => action()); 55 | } 56 | 57 | public void SetActionDisconnect(string text, Action action) 58 | { 59 | TextDisconnect.text = text; 60 | ButtonDisconnect.onClick.RemoveAllListeners(); 61 | ButtonDisconnect.onClick.AddListener(() => action()); 62 | } 63 | 64 | private void Update() 65 | { 66 | if (GamePanel.activeSelf) 67 | { 68 | if (Input.GetKeyDown(KeyCode.Escape)) 69 | Pause.SetActive(!Pause.activeSelf); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Examples/Game/Scripts/UIMenager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9f9d643024d8db64d899a99f68e27660 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Test.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ef36d8714f401dc42a5f0f81c6e6a8cc 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Test/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fa7e425b354db454c8c56d57f80b25a4 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/Test/Scripts/TestClient.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using System.Net; 3 | using UnityEngine; 4 | 5 | namespace SimpleUDP.Examples 6 | { 7 | public class TestClient : MonoBehaviour 8 | { 9 | public string IpAddress = "127.0.0.1"; 10 | public ushort Port = 12700; 11 | 12 | public string Key = "TestKey"; 13 | 14 | public ushort SendBroadcastPort = 12700; 15 | 16 | public uint TimeOut = 5000; 17 | public bool Broadcast = true; 18 | 19 | public UdpClient client; 20 | 21 | private void Start() 22 | { 23 | client = new UdpClient(); 24 | client.KeyConnection = Key; 25 | 26 | client.OnStarted = OnStarted; 27 | client.OnStopped = OnStopped; 28 | 29 | client.OnConnected = OnConnected; 30 | client.OnDisconnected = OnDisconnected; 31 | 32 | client.OnReceiveReliable = OnReceiveReliable; 33 | client.OnReceiveUnreliable = OnReceiveUnreliable; 34 | 35 | client.TimeOut = TimeOut; 36 | } 37 | 38 | private void OnGUI() 39 | { 40 | GUILayout.BeginArea(new Rect(270, 40, 250, 999)); 41 | 42 | if (client.IsRunning) 43 | { 44 | if (GUILayout.Button("Stop Client")) 45 | client.Stop(); 46 | 47 | if (client.State == State.NoConnect) 48 | { 49 | if (GUILayout.Button("Connect")) 50 | client.Connect(IpAddress, Port); 51 | } 52 | else if (client.State == State.Connecting) 53 | { 54 | if (GUILayout.Button("Stop Connecting...")) 55 | client.QuietDisconnect(); 56 | } 57 | else if (client.State == State.Connected) 58 | { 59 | if (GUILayout.Button("Disconnect")) 60 | client.Disconnect(); 61 | 62 | if (GUILayout.Button("Quiet Disconnect")) 63 | client.QuietDisconnect(); 64 | 65 | if (GUILayout.Button("Send Reliable")) 66 | SendReliable(); 67 | 68 | if (GUILayout.Button("Send Unreliable")) 69 | SendUnreliable(); 70 | 71 | if (GUILayout.Button("Send Broadcast")) 72 | SendBroadcast(); 73 | 74 | if (GUILayout.Button("Send Unconnected")) 75 | SendUnconnected(); 76 | } 77 | else if (client.State == State.Disconnecting) 78 | { 79 | if (GUILayout.Button("Stop Disconnecting...")) 80 | client.QuietDisconnect(); 81 | } 82 | } 83 | else 84 | { // If the port is zero, the available listening port will be given to the client. 85 | if (GUILayout.Button("Start Client")) 86 | client.Start(0, Broadcast); 87 | } 88 | 89 | GUILayout.EndArea(); 90 | } 91 | 92 | public void SendReliable() 93 | { 94 | Packet packet = Packet.Write(); 95 | packet.String("Hello, Reliable from client!"); 96 | 97 | client.SendReliable(packet); 98 | } 99 | 100 | public void SendUnreliable() 101 | { 102 | Packet packet = Packet.Write(); 103 | packet.String("Hello, Unreliable from client!"); 104 | 105 | client.SendUnreliable(packet); 106 | } 107 | 108 | public void SendBroadcast() 109 | { 110 | Packet packet = Packet.Write(); 111 | packet.String("Hello, Broadcast from client!"); 112 | 113 | client.SendBroadcast(SendBroadcastPort, packet); 114 | } 115 | 116 | public void SendUnconnected() 117 | { 118 | Packet packet = Packet.Write(); 119 | packet.String("Hello, Unconnected from client!"); 120 | 121 | client.SendUnconnected(client.EndPoint, packet); 122 | } 123 | 124 | private void OnStarted() 125 | { 126 | Debug.Log($"[Client] OnStarted on port: {client.LocalPort}"); 127 | } 128 | 129 | private void OnStopped() 130 | { 131 | Debug.Log($"[Client] OnStopped"); 132 | } 133 | 134 | private void OnConnected() 135 | { 136 | Debug.Log($"[Client] OnConnected to: {client.EndPoint}, Id: {client.Id}"); 137 | } 138 | 139 | private void OnDisconnected() 140 | { 141 | Debug.Log($"[Client] OnDisconnected from: {client.EndPoint}, Id: {client.Id}, Reason: {client.ReasonDisconnection}"); 142 | } 143 | 144 | private void OnReceiveReliable(byte[] packet) 145 | { 146 | Packet read = Packet.Read(packet); 147 | 148 | Debug.Log($"[Client] OnReceiveReliable: {read.String()}"); 149 | } 150 | 151 | private void OnReceiveUnreliable(byte[] packet) 152 | { 153 | Packet read = Packet.Read(packet); 154 | 155 | Debug.Log($"[Client] OnReceiveUnreliable: {read.String()}"); 156 | } 157 | 158 | private void FixedUpdate() 159 | { 160 | client.Receive(); 161 | client.TickUpdate(); 162 | } 163 | 164 | private void OnApplicationQuit() 165 | { 166 | client.Stop(); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Examples/Test/Scripts/TestClient.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db9647ef94bee2e4dae7cf46cf565008 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Test/Scripts/TestServer.cs: -------------------------------------------------------------------------------- 1 | using SimpleUDP; 2 | using System.Net; 3 | using UnityEngine; 4 | 5 | namespace SimpleUDP.Examples 6 | { 7 | public class TestServer : MonoBehaviour 8 | { 9 | public ushort Port = 12700; 10 | public string Key = "TestKey"; 11 | public ushort MaxConnections = 256; 12 | 13 | public uint TimeOut = 5000; 14 | public bool Broadcast = true; 15 | 16 | public uint Online; 17 | 18 | public UdpServer server; 19 | private UdpPeer udpPeer; 20 | 21 | private void Start() 22 | { 23 | UdpLog.Initialize(Debug.Log); 24 | 25 | server = new UdpServer(); 26 | server.KeyConnection = Key; 27 | 28 | server.OnStarted = OnStarted; 29 | server.OnStopped = OnStopped; 30 | 31 | server.OnConnected = OnConnected; 32 | server.OnDisconnected = OnDisconnected; 33 | 34 | server.OnReceiveReliable = OnReceiveReliable; 35 | server.OnReceiveUnreliable = OnReceiveUnreliable; 36 | 37 | server.OnReceiveBroadcast = OnReceiveBroadcast; 38 | server.OnReceiveUnconnected = OnReceiveUnconnected; 39 | 40 | server.TimeOut = TimeOut; 41 | server.MaxConnections = MaxConnections; 42 | } 43 | 44 | private void OnGUI() 45 | { 46 | GUILayout.BeginArea(new Rect(10, 40, 250, 999)); 47 | 48 | if (server.IsRunning) 49 | { 50 | if (GUILayout.Button("Stop Server")) 51 | server.Stop(); 52 | 53 | if (udpPeer != null && GUILayout.Button("Disconnect")) 54 | server.Disconnect(udpPeer.Id); 55 | 56 | if (GUILayout.Button("Send All Reliable")) 57 | SendAllReliable(); 58 | 59 | if (GUILayout.Button("Send All Unreliable")) 60 | SendAllUnreliable(); 61 | } 62 | else 63 | { 64 | if (GUILayout.Button("Start Server")) 65 | { 66 | if (server.IsAvailablePort(Port)) 67 | server.Start(Port, Broadcast); 68 | else 69 | server.Start(0, Broadcast); 70 | } 71 | } 72 | 73 | GUILayout.EndArea(); 74 | } 75 | 76 | public void SendAllReliable() 77 | { 78 | Packet packet = Packet.Write(); 79 | packet.String("Hello, Reliable from server!"); 80 | 81 | server.SendAllReliable(packet); 82 | } 83 | 84 | public void SendAllUnreliable() 85 | { 86 | Packet packet = Packet.Write(); 87 | packet.String("Hello, Unreliable from server!"); 88 | 89 | server.SendAllUnreliable(packet); 90 | } 91 | 92 | private void OnStarted() 93 | { 94 | Debug.Log($"[Server] OnStarted on port: {server.LocalPort}"); 95 | } 96 | 97 | private void OnStopped() 98 | { 99 | Debug.Log($"[Server] OnStopped"); 100 | } 101 | 102 | private void OnConnected(UdpPeer peer) 103 | { 104 | udpPeer = peer; 105 | Debug.Log($"[Server] OnConnected Id: {peer.Id}"); 106 | } 107 | 108 | private void OnDisconnected(UdpPeer peer) 109 | { 110 | udpPeer = null; 111 | Debug.Log($"[Server] OnDisconnected Id: {peer.Id}, Reason: {peer.ReasonDisconnection}"); 112 | } 113 | 114 | private void OnReceiveReliable(UdpPeer peer, byte[] packet) 115 | { 116 | Packet read = Packet.Read(packet); 117 | 118 | Debug.Log($"[Server] OnReceiveReliable: {read.String()}"); 119 | } 120 | 121 | private void OnReceiveUnreliable(UdpPeer peer, byte[] packet) 122 | { 123 | Packet read = Packet.Read(packet); 124 | 125 | Debug.Log($"[Server] OnReceiveUnreliable: {read.String()}"); 126 | } 127 | 128 | private void OnReceiveBroadcast(EndPoint endPoint, byte[] packet) 129 | { 130 | Packet read = Packet.Read(packet); 131 | 132 | Debug.Log($"[Server] OnReceiveBroadcast: {read.String()}"); 133 | } 134 | 135 | private void OnReceiveUnconnected(EndPoint endPoint, byte[] packet) 136 | { 137 | Packet read = Packet.Read(packet); 138 | 139 | Debug.Log($"[Server] OnReceiveUnconnected: {read.String()}"); 140 | } 141 | 142 | private void FixedUpdate() 143 | { 144 | server.Receive(); 145 | server.TickUpdate(); 146 | 147 | Online = server.ConnectionsCount; 148 | } 149 | 150 | private void OnApplicationQuit() 151 | { 152 | server.Stop(); 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /Examples/Test/Scripts/TestServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c4e6f3d55d7543b4d82507157d764e4f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/Test/TestScene.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_UseRadianceAmbientProbe: 0 42 | --- !u!157 &3 43 | LightmapSettings: 44 | m_ObjectHideFlags: 0 45 | serializedVersion: 12 46 | m_GIWorkflowMode: 1 47 | m_GISettings: 48 | serializedVersion: 2 49 | m_BounceScale: 1 50 | m_IndirectOutputScale: 1 51 | m_AlbedoBoost: 1 52 | m_EnvironmentLightingMode: 0 53 | m_EnableBakedLightmaps: 1 54 | m_EnableRealtimeLightmaps: 0 55 | m_LightmapEditorSettings: 56 | serializedVersion: 12 57 | m_Resolution: 2 58 | m_BakeResolution: 40 59 | m_AtlasSize: 1024 60 | m_AO: 0 61 | m_AOMaxDistance: 1 62 | m_CompAOExponent: 1 63 | m_CompAOExponentDirect: 0 64 | m_ExtractAmbientOcclusion: 0 65 | m_Padding: 2 66 | m_LightmapParameters: {fileID: 0} 67 | m_LightmapsBakeMode: 1 68 | m_TextureCompression: 1 69 | m_FinalGather: 0 70 | m_FinalGatherFiltering: 1 71 | m_FinalGatherRayCount: 256 72 | m_ReflectionCompression: 2 73 | m_MixedBakeMode: 2 74 | m_BakeBackend: 1 75 | m_PVRSampling: 1 76 | m_PVRDirectSampleCount: 32 77 | m_PVRSampleCount: 512 78 | m_PVRBounces: 2 79 | m_PVREnvironmentSampleCount: 256 80 | m_PVREnvironmentReferencePointCount: 2048 81 | m_PVRFilteringMode: 1 82 | m_PVRDenoiserTypeDirect: 1 83 | m_PVRDenoiserTypeIndirect: 1 84 | m_PVRDenoiserTypeAO: 1 85 | m_PVRFilterTypeDirect: 0 86 | m_PVRFilterTypeIndirect: 0 87 | m_PVRFilterTypeAO: 0 88 | m_PVREnvironmentMIS: 1 89 | m_PVRCulling: 1 90 | m_PVRFilteringGaussRadiusDirect: 1 91 | m_PVRFilteringGaussRadiusIndirect: 5 92 | m_PVRFilteringGaussRadiusAO: 2 93 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 94 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 95 | m_PVRFilteringAtrousPositionSigmaAO: 1 96 | m_ExportTrainingData: 0 97 | m_TrainingDataDestination: TrainingData 98 | m_LightProbeSampleCountMultiplier: 4 99 | m_LightingDataAsset: {fileID: 0} 100 | m_LightingSettings: {fileID: 0} 101 | --- !u!196 &4 102 | NavMeshSettings: 103 | serializedVersion: 2 104 | m_ObjectHideFlags: 0 105 | m_BuildSettings: 106 | serializedVersion: 3 107 | agentTypeID: 0 108 | agentRadius: 0.5 109 | agentHeight: 2 110 | agentSlope: 45 111 | agentClimb: 0.4 112 | ledgeDropHeight: 0 113 | maxJumpAcrossDistance: 0 114 | minRegionArea: 2 115 | manualCellSize: 0 116 | cellSize: 0.16666667 117 | manualTileSize: 0 118 | tileSize: 256 119 | buildHeightMesh: 0 120 | maxJobWorkers: 0 121 | preserveTilesOutsideBounds: 0 122 | debug: 123 | m_Flags: 0 124 | m_NavMeshData: {fileID: 0} 125 | --- !u!1 &774522096 126 | GameObject: 127 | m_ObjectHideFlags: 0 128 | m_CorrespondingSourceObject: {fileID: 0} 129 | m_PrefabInstance: {fileID: 0} 130 | m_PrefabAsset: {fileID: 0} 131 | serializedVersion: 6 132 | m_Component: 133 | - component: {fileID: 774522099} 134 | - component: {fileID: 774522098} 135 | m_Layer: 0 136 | m_Name: Main Camera 137 | m_TagString: MainCamera 138 | m_Icon: {fileID: 0} 139 | m_NavMeshLayer: 0 140 | m_StaticEditorFlags: 0 141 | m_IsActive: 1 142 | --- !u!20 &774522098 143 | Camera: 144 | m_ObjectHideFlags: 0 145 | m_CorrespondingSourceObject: {fileID: 0} 146 | m_PrefabInstance: {fileID: 0} 147 | m_PrefabAsset: {fileID: 0} 148 | m_GameObject: {fileID: 774522096} 149 | m_Enabled: 1 150 | serializedVersion: 2 151 | m_ClearFlags: 2 152 | m_BackGroundColor: {r: 0.114542544, g: 0.42347938, b: 0.735849, a: 0} 153 | m_projectionMatrixMode: 1 154 | m_GateFitMode: 2 155 | m_FOVAxisMode: 0 156 | m_Iso: 200 157 | m_ShutterSpeed: 0.005 158 | m_Aperture: 16 159 | m_FocusDistance: 10 160 | m_FocalLength: 50 161 | m_BladeCount: 5 162 | m_Curvature: {x: 2, y: 11} 163 | m_BarrelClipping: 0.25 164 | m_Anamorphism: 0 165 | m_SensorSize: {x: 36, y: 24} 166 | m_LensShift: {x: 0, y: 0} 167 | m_NormalizedViewPortRect: 168 | serializedVersion: 2 169 | x: 0 170 | y: 0 171 | width: 1 172 | height: 1 173 | near clip plane: 0.3 174 | far clip plane: 1 175 | field of view: 60 176 | orthographic: 0 177 | orthographic size: 5 178 | m_Depth: -1 179 | m_CullingMask: 180 | serializedVersion: 2 181 | m_Bits: 4294967295 182 | m_RenderingPath: -1 183 | m_TargetTexture: {fileID: 0} 184 | m_TargetDisplay: 0 185 | m_TargetEye: 3 186 | m_HDR: 1 187 | m_AllowMSAA: 1 188 | m_AllowDynamicResolution: 0 189 | m_ForceIntoRT: 0 190 | m_OcclusionCulling: 1 191 | m_StereoConvergence: 10 192 | m_StereoSeparation: 0.022 193 | --- !u!4 &774522099 194 | Transform: 195 | m_ObjectHideFlags: 0 196 | m_CorrespondingSourceObject: {fileID: 0} 197 | m_PrefabInstance: {fileID: 0} 198 | m_PrefabAsset: {fileID: 0} 199 | m_GameObject: {fileID: 774522096} 200 | serializedVersion: 2 201 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 202 | m_LocalPosition: {x: 0, y: 1, z: -10} 203 | m_LocalScale: {x: 1, y: 1, z: 1} 204 | m_ConstrainProportionsScale: 0 205 | m_Children: [] 206 | m_Father: {fileID: 0} 207 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 208 | --- !u!1 &1644417068 209 | GameObject: 210 | m_ObjectHideFlags: 0 211 | m_CorrespondingSourceObject: {fileID: 0} 212 | m_PrefabInstance: {fileID: 0} 213 | m_PrefabAsset: {fileID: 0} 214 | serializedVersion: 6 215 | m_Component: 216 | - component: {fileID: 1644417069} 217 | - component: {fileID: 1644417071} 218 | - component: {fileID: 1644417070} 219 | m_Layer: 0 220 | m_Name: Network 221 | m_TagString: Untagged 222 | m_Icon: {fileID: 0} 223 | m_NavMeshLayer: 0 224 | m_StaticEditorFlags: 0 225 | m_IsActive: 1 226 | --- !u!4 &1644417069 227 | Transform: 228 | m_ObjectHideFlags: 0 229 | m_CorrespondingSourceObject: {fileID: 0} 230 | m_PrefabInstance: {fileID: 0} 231 | m_PrefabAsset: {fileID: 0} 232 | m_GameObject: {fileID: 1644417068} 233 | serializedVersion: 2 234 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 235 | m_LocalPosition: {x: 0, y: 0, z: 0} 236 | m_LocalScale: {x: 1, y: 1, z: 1} 237 | m_ConstrainProportionsScale: 0 238 | m_Children: [] 239 | m_Father: {fileID: 0} 240 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 241 | --- !u!114 &1644417070 242 | MonoBehaviour: 243 | m_ObjectHideFlags: 0 244 | m_CorrespondingSourceObject: {fileID: 0} 245 | m_PrefabInstance: {fileID: 0} 246 | m_PrefabAsset: {fileID: 0} 247 | m_GameObject: {fileID: 1644417068} 248 | m_Enabled: 1 249 | m_EditorHideFlags: 0 250 | m_Script: {fileID: 11500000, guid: db9647ef94bee2e4dae7cf46cf565008, type: 3} 251 | m_Name: 252 | m_EditorClassIdentifier: 253 | IpAddress: 127.0.0.1 254 | Port: 12700 255 | Key: key 256 | SendBroadcastPort: 12700 257 | TimeOut: 5000 258 | Broadcast: 1 259 | --- !u!114 &1644417071 260 | MonoBehaviour: 261 | m_ObjectHideFlags: 0 262 | m_CorrespondingSourceObject: {fileID: 0} 263 | m_PrefabInstance: {fileID: 0} 264 | m_PrefabAsset: {fileID: 0} 265 | m_GameObject: {fileID: 1644417068} 266 | m_Enabled: 1 267 | m_EditorHideFlags: 0 268 | m_Script: {fileID: 11500000, guid: c4e6f3d55d7543b4d82507157d764e4f, type: 3} 269 | m_Name: 270 | m_EditorClassIdentifier: 271 | Port: 12700 272 | Key: key 273 | MaxConnections: 256 274 | TimeOut: 5000 275 | Broadcast: 1 276 | Online: 0 277 | --- !u!1660057539 &9223372036854775807 278 | SceneRoots: 279 | m_ObjectHideFlags: 0 280 | m_Roots: 281 | - {fileID: 774522099} 282 | - {fileID: 1644417069} 283 | -------------------------------------------------------------------------------- /Examples/Test/TestScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 650509f07b3c99d44a8a2a2a0efae084 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Examples/TestingСonnections.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d977f9e76024a3848994b827446320dd 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/TestingСonnections/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0dfb1626c8b1db6439a91d0881bf0b33 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Examples/TestingСonnections/Scripts/TestingClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleUDP; 3 | using UnityEngine; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | 8 | namespace SimpleUDP.Examples 9 | { 10 | public class TestingClient : MonoBehaviour 11 | { 12 | public string IpAddress = "127.0.0.1"; 13 | public ushort Port = 12700; 14 | 15 | public ushort MaxСonnectionСreation = 1024; 16 | public ushort DelayСreationСonnection = 100; 17 | 18 | [Header("List Clients")] 19 | public List Clients = new List(); 20 | 21 | private int currentList; 22 | 23 | private async void Start() 24 | { 25 | currentList = 0; 26 | Clients.Add(new ThreadClients()); 27 | 28 | for (int i = 0; i < MaxСonnectionСreation; i++) 29 | { 30 | lock (Clients) 31 | { 32 | Client client = new Client(IpAddress, Port); 33 | AddClientToList(client); 34 | } 35 | await Task.Delay(DelayСreationСonnection); 36 | } 37 | } 38 | 39 | private void AddClientToList(Client client) 40 | { 41 | if (!Clients[currentList].TryAddClient(client)) 42 | { 43 | currentList++; 44 | 45 | Clients.Add(new ThreadClients()); 46 | Clients[currentList].TryAddClient(client); 47 | } 48 | } 49 | 50 | private void OnApplicationQuit() 51 | { 52 | lock (Clients) 53 | { 54 | foreach (ThreadClients client in Clients) 55 | client.Stop(); 56 | } 57 | } 58 | 59 | [Serializable] public class Client 60 | { 61 | public uint Id; 62 | public uint Rtt; 63 | public bool IsConnected; 64 | 65 | private UdpClient udpClient; 66 | 67 | public Client(string address, ushort port) 68 | { 69 | IsConnected = false; 70 | udpClient = new UdpClient(); 71 | 72 | udpClient.OnConnected = () => IsConnected = true; 73 | udpClient.OnDisconnected = () => IsConnected = false; 74 | 75 | udpClient.Start(); 76 | udpClient.Connect(address, port); 77 | } 78 | 79 | public void TickUpdate() 80 | { 81 | udpClient.Receive(); 82 | udpClient.TickUpdate(); 83 | 84 | Id = udpClient.Id; 85 | Rtt = udpClient.Rtt; 86 | } 87 | 88 | public void Stop() 89 | { 90 | udpClient.Stop(); 91 | } 92 | } 93 | 94 | [Serializable] public class ThreadClients 95 | { 96 | public List Clients; 97 | 98 | private bool isActive; 99 | private ushort maxClients = 128; 100 | 101 | public ThreadClients() 102 | { 103 | Clients = new List(); 104 | isActive = true; 105 | 106 | new Thread(TickUpdateThread).Start(); 107 | } 108 | 109 | public bool TryAddClient(Client client) 110 | { 111 | lock (Clients) 112 | { 113 | if (Clients.Count < maxClients) 114 | { 115 | Clients.Add(client); 116 | return true; 117 | } 118 | } 119 | 120 | return false; 121 | } 122 | 123 | private void TickUpdateThread() 124 | { 125 | while (isActive) 126 | { 127 | lock (Clients) 128 | { 129 | foreach (Client client in Clients) 130 | client?.TickUpdate(); 131 | } 132 | 133 | Thread.Sleep(10); 134 | } 135 | } 136 | 137 | public void Stop() 138 | { 139 | isActive = false; 140 | 141 | lock (Clients) 142 | { 143 | foreach (Client client in Clients) 144 | client.Stop(); 145 | } 146 | } 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /Examples/TestingСonnections/Scripts/TestingClient.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2cc46c41eee469145bb76aab88cf88fe 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/TestingСonnections/Scripts/TestingServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleUDP; 3 | using UnityEngine; 4 | using System.Threading; 5 | 6 | namespace SimpleUDP.Examples 7 | { 8 | public class TestingServer : MonoBehaviour 9 | { 10 | public ushort Port = 12700; 11 | public uint MaxConnections = 1024; 12 | 13 | [Header("Statistic")] 14 | public uint Connections; 15 | 16 | private UdpServer udpServer; 17 | 18 | private void Start() 19 | { 20 | udpServer = new UdpServer(); 21 | udpServer.MaxConnections = MaxConnections; 22 | 23 | udpServer.Start(Port); 24 | 25 | new Thread(ReceiveThread).Start(); 26 | new Thread(TickUpdateThread).Start(); 27 | } 28 | 29 | private void ReceiveThread() 30 | { 31 | while (udpServer.IsRunning) 32 | { 33 | if (udpServer.SocketPoll) 34 | udpServer.Receive(); 35 | 36 | Thread.Sleep(10); 37 | } 38 | } 39 | 40 | private void TickUpdateThread() 41 | { 42 | while (udpServer.IsRunning) 43 | { 44 | udpServer.TickUpdate(); 45 | Thread.Sleep(10); 46 | } 47 | } 48 | 49 | private void FixedUpdate() 50 | { 51 | Connections = udpServer.ConnectionsCount; 52 | } 53 | 54 | private void OnApplicationQuit() 55 | { 56 | udpServer.Stop(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Examples/TestingСonnections/Scripts/TestingServer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5aaf412373ab4e46b38444ce1e0f004 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Examples/TestingСonnections/TestingСonnections.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | OcclusionCullingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_OcclusionBakeSettings: 8 | smallestOccluder: 5 9 | smallestHole: 0.25 10 | backfaceThreshold: 100 11 | m_SceneGUID: 00000000000000000000000000000000 12 | m_OcclusionCullingData: {fileID: 0} 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_ObjectHideFlags: 0 16 | serializedVersion: 9 17 | m_Fog: 0 18 | m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} 19 | m_FogMode: 3 20 | m_FogDensity: 0.01 21 | m_LinearFogStart: 0 22 | m_LinearFogEnd: 300 23 | m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} 24 | m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} 25 | m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} 26 | m_AmbientIntensity: 1 27 | m_AmbientMode: 0 28 | m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} 29 | m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} 30 | m_HaloStrength: 0.5 31 | m_FlareStrength: 1 32 | m_FlareFadeSpeed: 3 33 | m_HaloTexture: {fileID: 0} 34 | m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} 35 | m_DefaultReflectionMode: 0 36 | m_DefaultReflectionResolution: 128 37 | m_ReflectionBounces: 1 38 | m_ReflectionIntensity: 1 39 | m_CustomReflection: {fileID: 0} 40 | m_Sun: {fileID: 0} 41 | m_UseRadianceAmbientProbe: 0 42 | --- !u!157 &3 43 | LightmapSettings: 44 | m_ObjectHideFlags: 0 45 | serializedVersion: 12 46 | m_GIWorkflowMode: 1 47 | m_GISettings: 48 | serializedVersion: 2 49 | m_BounceScale: 1 50 | m_IndirectOutputScale: 1 51 | m_AlbedoBoost: 1 52 | m_EnvironmentLightingMode: 0 53 | m_EnableBakedLightmaps: 1 54 | m_EnableRealtimeLightmaps: 0 55 | m_LightmapEditorSettings: 56 | serializedVersion: 12 57 | m_Resolution: 2 58 | m_BakeResolution: 40 59 | m_AtlasSize: 1024 60 | m_AO: 0 61 | m_AOMaxDistance: 1 62 | m_CompAOExponent: 1 63 | m_CompAOExponentDirect: 0 64 | m_ExtractAmbientOcclusion: 0 65 | m_Padding: 2 66 | m_LightmapParameters: {fileID: 0} 67 | m_LightmapsBakeMode: 1 68 | m_TextureCompression: 1 69 | m_FinalGather: 0 70 | m_FinalGatherFiltering: 1 71 | m_FinalGatherRayCount: 256 72 | m_ReflectionCompression: 2 73 | m_MixedBakeMode: 2 74 | m_BakeBackend: 1 75 | m_PVRSampling: 1 76 | m_PVRDirectSampleCount: 32 77 | m_PVRSampleCount: 512 78 | m_PVRBounces: 2 79 | m_PVREnvironmentSampleCount: 256 80 | m_PVREnvironmentReferencePointCount: 2048 81 | m_PVRFilteringMode: 1 82 | m_PVRDenoiserTypeDirect: 1 83 | m_PVRDenoiserTypeIndirect: 1 84 | m_PVRDenoiserTypeAO: 1 85 | m_PVRFilterTypeDirect: 0 86 | m_PVRFilterTypeIndirect: 0 87 | m_PVRFilterTypeAO: 0 88 | m_PVREnvironmentMIS: 1 89 | m_PVRCulling: 1 90 | m_PVRFilteringGaussRadiusDirect: 1 91 | m_PVRFilteringGaussRadiusIndirect: 5 92 | m_PVRFilteringGaussRadiusAO: 2 93 | m_PVRFilteringAtrousPositionSigmaDirect: 0.5 94 | m_PVRFilteringAtrousPositionSigmaIndirect: 2 95 | m_PVRFilteringAtrousPositionSigmaAO: 1 96 | m_ExportTrainingData: 0 97 | m_TrainingDataDestination: TrainingData 98 | m_LightProbeSampleCountMultiplier: 4 99 | m_LightingDataAsset: {fileID: 0} 100 | m_LightingSettings: {fileID: 0} 101 | --- !u!196 &4 102 | NavMeshSettings: 103 | serializedVersion: 2 104 | m_ObjectHideFlags: 0 105 | m_BuildSettings: 106 | serializedVersion: 3 107 | agentTypeID: 0 108 | agentRadius: 0.5 109 | agentHeight: 2 110 | agentSlope: 45 111 | agentClimb: 0.4 112 | ledgeDropHeight: 0 113 | maxJumpAcrossDistance: 0 114 | minRegionArea: 2 115 | manualCellSize: 0 116 | cellSize: 0.16666667 117 | manualTileSize: 0 118 | tileSize: 256 119 | buildHeightMesh: 0 120 | maxJobWorkers: 0 121 | preserveTilesOutsideBounds: 0 122 | debug: 123 | m_Flags: 0 124 | m_NavMeshData: {fileID: 0} 125 | --- !u!1 &99353082 126 | GameObject: 127 | m_ObjectHideFlags: 0 128 | m_CorrespondingSourceObject: {fileID: 0} 129 | m_PrefabInstance: {fileID: 0} 130 | m_PrefabAsset: {fileID: 0} 131 | serializedVersion: 6 132 | m_Component: 133 | - component: {fileID: 99353085} 134 | - component: {fileID: 99353084} 135 | - component: {fileID: 99353083} 136 | m_Layer: 0 137 | m_Name: Camera 138 | m_TagString: MainCamera 139 | m_Icon: {fileID: 0} 140 | m_NavMeshLayer: 0 141 | m_StaticEditorFlags: 0 142 | m_IsActive: 1 143 | --- !u!81 &99353083 144 | AudioListener: 145 | m_ObjectHideFlags: 0 146 | m_CorrespondingSourceObject: {fileID: 0} 147 | m_PrefabInstance: {fileID: 0} 148 | m_PrefabAsset: {fileID: 0} 149 | m_GameObject: {fileID: 99353082} 150 | m_Enabled: 1 151 | --- !u!20 &99353084 152 | Camera: 153 | m_ObjectHideFlags: 0 154 | m_CorrespondingSourceObject: {fileID: 0} 155 | m_PrefabInstance: {fileID: 0} 156 | m_PrefabAsset: {fileID: 0} 157 | m_GameObject: {fileID: 99353082} 158 | m_Enabled: 1 159 | serializedVersion: 2 160 | m_ClearFlags: 2 161 | m_BackGroundColor: {r: 0.11372549, g: 0.42352942, b: 0.735849, a: 0} 162 | m_projectionMatrixMode: 1 163 | m_GateFitMode: 2 164 | m_FOVAxisMode: 0 165 | m_Iso: 200 166 | m_ShutterSpeed: 0.005 167 | m_Aperture: 16 168 | m_FocusDistance: 10 169 | m_FocalLength: 50 170 | m_BladeCount: 5 171 | m_Curvature: {x: 2, y: 11} 172 | m_BarrelClipping: 0.25 173 | m_Anamorphism: 0 174 | m_SensorSize: {x: 36, y: 24} 175 | m_LensShift: {x: 0, y: 0} 176 | m_NormalizedViewPortRect: 177 | serializedVersion: 2 178 | x: 0 179 | y: 0 180 | width: 1 181 | height: 1 182 | near clip plane: 0.3 183 | far clip plane: 1000 184 | field of view: 60 185 | orthographic: 0 186 | orthographic size: 5 187 | m_Depth: -1 188 | m_CullingMask: 189 | serializedVersion: 2 190 | m_Bits: 4294967295 191 | m_RenderingPath: -1 192 | m_TargetTexture: {fileID: 0} 193 | m_TargetDisplay: 0 194 | m_TargetEye: 3 195 | m_HDR: 1 196 | m_AllowMSAA: 1 197 | m_AllowDynamicResolution: 0 198 | m_ForceIntoRT: 0 199 | m_OcclusionCulling: 1 200 | m_StereoConvergence: 10 201 | m_StereoSeparation: 0.022 202 | --- !u!4 &99353085 203 | Transform: 204 | m_ObjectHideFlags: 0 205 | m_CorrespondingSourceObject: {fileID: 0} 206 | m_PrefabInstance: {fileID: 0} 207 | m_PrefabAsset: {fileID: 0} 208 | m_GameObject: {fileID: 99353082} 209 | serializedVersion: 2 210 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 211 | m_LocalPosition: {x: 0, y: 1, z: -10} 212 | m_LocalScale: {x: 1, y: 1, z: 1} 213 | m_ConstrainProportionsScale: 0 214 | m_Children: [] 215 | m_Father: {fileID: 0} 216 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 217 | --- !u!1 &1067805704 218 | GameObject: 219 | m_ObjectHideFlags: 0 220 | m_CorrespondingSourceObject: {fileID: 0} 221 | m_PrefabInstance: {fileID: 0} 222 | m_PrefabAsset: {fileID: 0} 223 | serializedVersion: 6 224 | m_Component: 225 | - component: {fileID: 1067805705} 226 | - component: {fileID: 1067805707} 227 | - component: {fileID: 1067805706} 228 | m_Layer: 0 229 | m_Name: Network 230 | m_TagString: Untagged 231 | m_Icon: {fileID: 0} 232 | m_NavMeshLayer: 0 233 | m_StaticEditorFlags: 0 234 | m_IsActive: 1 235 | --- !u!4 &1067805705 236 | Transform: 237 | m_ObjectHideFlags: 0 238 | m_CorrespondingSourceObject: {fileID: 0} 239 | m_PrefabInstance: {fileID: 0} 240 | m_PrefabAsset: {fileID: 0} 241 | m_GameObject: {fileID: 1067805704} 242 | serializedVersion: 2 243 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 244 | m_LocalPosition: {x: 0, y: 0, z: 0} 245 | m_LocalScale: {x: 1, y: 1, z: 1} 246 | m_ConstrainProportionsScale: 0 247 | m_Children: [] 248 | m_Father: {fileID: 0} 249 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 250 | --- !u!114 &1067805706 251 | MonoBehaviour: 252 | m_ObjectHideFlags: 0 253 | m_CorrespondingSourceObject: {fileID: 0} 254 | m_PrefabInstance: {fileID: 0} 255 | m_PrefabAsset: {fileID: 0} 256 | m_GameObject: {fileID: 1067805704} 257 | m_Enabled: 1 258 | m_EditorHideFlags: 0 259 | m_Script: {fileID: 11500000, guid: 2cc46c41eee469145bb76aab88cf88fe, type: 3} 260 | m_Name: 261 | m_EditorClassIdentifier: 262 | IpAddress: 127.0.0.1 263 | Port: 12700 264 | "Max\u0421onnection\u0421reation": 1024 265 | "Delay\u0421reation\u0421onnection": 25 266 | Clients: [] 267 | --- !u!114 &1067805707 268 | MonoBehaviour: 269 | m_ObjectHideFlags: 0 270 | m_CorrespondingSourceObject: {fileID: 0} 271 | m_PrefabInstance: {fileID: 0} 272 | m_PrefabAsset: {fileID: 0} 273 | m_GameObject: {fileID: 1067805704} 274 | m_Enabled: 1 275 | m_EditorHideFlags: 0 276 | m_Script: {fileID: 11500000, guid: e5aaf412373ab4e46b38444ce1e0f004, type: 3} 277 | m_Name: 278 | m_EditorClassIdentifier: 279 | Port: 12700 280 | MaxConnections: 1024 281 | Connections: 0 282 | --- !u!1660057539 &9223372036854775807 283 | SceneRoots: 284 | m_ObjectHideFlags: 0 285 | m_Roots: 286 | - {fileID: 99353085} 287 | - {fileID: 1067805705} 288 | -------------------------------------------------------------------------------- /Examples/TestingСonnections/TestingСonnections.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3cb450a8c93174d4bba91e468e2c94b4 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 StrumDev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleUDP v0.7.0 2 | 3 | ### SimpleUDP is a simple network library that provides reliable and unreliable messages. 4 | 5 | - This is my first project on which I spent a considerable amount of time to understand how the network works. This network library is primarily designed to create virtual connections and transmit reliable data over the UDP protocol. The project is not yet fully completed, so I do not know how frequently updates will be released. This project is not something powerful or efficient; I made it to learn how to finish projects rather than start and not complete them. 6 | 7 | I developed this project myself, starting without knowledge of C# and how the network works, so please be understanding. 8 | 9 | ### The project at this stage is already suitable for use in small commercial applications and games. 10 | - I don't plan to make any major changes to the functionality in the future, mostly bug fixes and performance improvements. 11 | 12 | ### Where it can be used. 13 | - It's up to you to decide, it's a fairly small library that may be suitable for small projects. 14 | - I don't try to be better than existing similar off-the-shelf solutions, because I do it to learn. 15 | 16 | Documentation: [here](https://github.com/StrumDev/SimpleUDP/blob/main/Documentation.md) 17 | 18 | [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua) 19 | 20 | * Only UDP. 21 | * CCU: No limit. 22 | * Small packet header size overhead: 1 byte for unreliable, 2 bytes for reliable. 23 | * Broadcast message. 24 | * Client-server connections. 25 | * Can be used in dotnet and Unity applications. 26 | * License: MIT License. 27 | * The library is under development. 28 | 29 | Discord server: [here](https://discord.gg/x2yUKGmfgY) 30 | 31 | ### I will be very grateful for your support. 32 | 33 | [![Donate](https://github.com/user-attachments/assets/67c6c744-4ab4-411f-ac4c-3b32e1be0895)](https://www.paypal.com/donate/?hosted_button_id=7LPMECG4E9EH4) 34 | 35 | # SimpleUDP - Unity demo game. 36 | 37 | https://github.com/StrumDev/SimpleUDP/assets/114677727/d5517072-a254-4171-a6ff-986ff0f4704a 38 | -------------------------------------------------------------------------------- /SimpleUDP.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleUDP by StrumDev", 3 | "rootNamespace": "SimpleUDP", 4 | "references": [], 5 | "includePlatforms": [], 6 | "excludePlatforms": [], 7 | "allowUnsafeCode": false, 8 | "overrideReferences": false, 9 | "precompiledReferences": [], 10 | "autoReferenced": true, 11 | "defineConstraints": [], 12 | "versionDefines": [], 13 | "noEngineReferences": false 14 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/UdpChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SimpleUDP.Utils; 3 | 4 | namespace SimpleUDP.Core 5 | { 6 | public class UdpChannel 7 | { 8 | internal Action RawSend; 9 | 10 | internal uint Interval = 50; 11 | internal byte MaxPending = 64; 12 | 13 | internal ushort MaxQueue = 4; 14 | internal ushort CapacityQueue = 16; 15 | 16 | internal const byte MaximumCount = 128; 17 | 18 | private byte[] sendAck; 19 | private byte[] receiveAck; 20 | 21 | private UdpBuffer udpBuffer; 22 | private UdpActive pendings; 23 | 24 | private object locker = new object(); 25 | private object lockerAck = new object(); 26 | 27 | public UdpChannel(Action rawSend) 28 | { 29 | RawSend = rawSend; 30 | } 31 | 32 | internal void Initialize() 33 | { 34 | sendAck = new byte[MaxPending]; 35 | receiveAck = new byte[MaximumCount]; 36 | 37 | pendings = new UdpActive(MaxPending); 38 | 39 | for (int index = 0; index < MaxPending; index++) 40 | pendings.AddElement(new UdpPending(RawSend)); 41 | 42 | udpBuffer = new UdpBuffer(MaxQueue, CapacityQueue); 43 | } 44 | 45 | internal void SendUnreliable(byte[] packet, int length, int offset) 46 | { 47 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 48 | 49 | buffer[UdpIndex.Header] = UdpHeader.Unreliable; 50 | Buffer.BlockCopy(packet, offset, buffer, UdpIndex.Unreliable, length); 51 | 52 | RawSend(buffer); 53 | } 54 | 55 | internal void SendReliable(byte[] packet, int length, int offset) 56 | { 57 | lock (locker) 58 | { 59 | byte[] buffer = new byte[length + UdpIndex.Reliable]; 60 | 61 | buffer[UdpIndex.Header] = UdpHeader.Reliable; 62 | Buffer.BlockCopy(packet, offset, buffer, UdpIndex.Reliable, length); 63 | 64 | if (pendings.TrySetActive(out byte index)) 65 | SendPending(index, buffer); 66 | else 67 | udpBuffer.AddElement(buffer); 68 | } 69 | } 70 | 71 | private void SendPending(byte index, byte[] buffer) 72 | { 73 | NextAck(index, ref sendAck[index], out buffer[UdpIndex.Ack]); 74 | pendings[index].SendPacket(buffer); 75 | } 76 | 77 | internal void UpdateTimer(uint deltaTime) 78 | { 79 | lock (locker) 80 | { 81 | for (int index = 0; index < pendings.ActiveCount; index++) 82 | pendings.GetActive(index)?.UpdateTimer(deltaTime, Interval); 83 | } 84 | } 85 | 86 | internal bool IsNewAck(byte[] data) 87 | { 88 | lock (lockerAck) 89 | { 90 | byte index = GetIndex(data[UdpIndex.Ack]); 91 | RawSend(new byte[]{UdpHeader.ReliableAck, data[UdpIndex.Ack]}); 92 | 93 | if (receiveAck[index] != data[UdpIndex.Ack]) 94 | { 95 | receiveAck[index] = data[UdpIndex.Ack]; 96 | return true; 97 | } 98 | else return false; 99 | } 100 | } 101 | 102 | internal void ClearAck(byte[] data) 103 | { 104 | lock (locker) 105 | { 106 | byte index = GetIndex(data[UdpIndex.Ack]); 107 | 108 | if (pendings[index].buffer == null) 109 | return; 110 | 111 | if (pendings[index].buffer[UdpIndex.Ack] == data[UdpIndex.Ack]) 112 | { 113 | pendings[index].ClearPacket(); 114 | 115 | if (udpBuffer.Count != 0) 116 | SendPending(index, udpBuffer.GetElement()); 117 | else 118 | pendings.RemoveActive(index); 119 | } 120 | } 121 | } 122 | 123 | private void NextAck(byte index, ref byte ack, out byte newAck) 124 | { 125 | newAck = (byte)((ack < 128 ? ack += 128 : ack -= 128) + index); 126 | } 127 | 128 | private byte GetIndex(byte header) 129 | { 130 | return (byte)(header < 128 ? header : header - 128); 131 | } 132 | 133 | internal void ClearChannel() 134 | { 135 | if (pendings != null) 136 | { 137 | for (int i = 0; i < MaxPending; i++) 138 | pendings[i].ClearPacket(); 139 | 140 | udpBuffer.Clear(); 141 | } 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/UdpHeader.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleUDP.Core 2 | { 3 | internal static class UdpHeader 4 | { 5 | public const byte Connect = 1; 6 | public const byte Connected = 2; 7 | 8 | public const byte Disconnect = 3; 9 | public const byte Disconnected = 4; 10 | 11 | public const byte Reliable = 5; 12 | public const byte Unreliable = 6; 13 | public const byte ReliableAck = 7; 14 | 15 | public const byte Ping = 8; 16 | public const byte Pong = 9; 17 | 18 | public const byte Broadcast = 10; 19 | public const byte Unconnected = 11; 20 | } 21 | internal static class UdpIndex 22 | { 23 | internal const byte Ack = 1; 24 | internal const byte Header = 0; 25 | 26 | internal const byte Reliable = 2; 27 | internal const byte Unreliable = 1; 28 | 29 | internal const byte Reason = 1; 30 | } 31 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/UdpListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Diagnostics; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace SimpleUDP.Core 8 | { 9 | using UdpClient = System.Net.Sockets.UdpClient; 10 | public class UdpListener 11 | { 12 | public Action OnStarted; 13 | public Action OnStopped; 14 | 15 | public bool IsRunning { get; private set; } 16 | public ushort LocalPort { get; private set; } 17 | public EndPoint LocalEndPoint { get; private set; } 18 | 19 | public bool LimitedSizePackage = true; 20 | public ushort ReceiveBufferSize = 2048; 21 | 22 | public const ushort MaxSizePacket = 1432; 23 | 24 | public bool EnableBroadcast => socket.EnableBroadcast; 25 | 26 | public int AvailablePackages => IsRunning ? socket.Available : 0; 27 | public bool SocketPoll => IsRunning ? socket.Poll(500000, SelectMode.SelectRead) : false; //500000 => 0.5s 28 | 29 | private Socket socket; 30 | private EndPoint sender; 31 | private Stopwatch watch; 32 | 33 | private const int OneKilobyte = 1024; 34 | 35 | private byte[] buffer; 36 | private object locker = new object(); 37 | 38 | // SioUdpConnreset = IOC_IN | IOC_VENDOR | 12 39 | private const int SioUdpConnreset = -1744830452; 40 | 41 | public UdpListener() 42 | { 43 | watch = new Stopwatch(); 44 | sender = new IPEndPoint(IPAddress.Any, 0); 45 | } 46 | 47 | public void Start(ushort port = 0, bool enableBroadcast = false) 48 | { 49 | lock (locker) 50 | { 51 | if (!IsRunning) 52 | { 53 | socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 54 | 55 | socket.Blocking = true; 56 | socket.EnableBroadcast = enableBroadcast; 57 | 58 | BindSocket(new IPEndPoint(IPAddress.Any, port)); 59 | 60 | OnListenerStarted(); 61 | } 62 | } 63 | } 64 | 65 | protected void AdjustBufferSizes(int kilobytes) 66 | { 67 | socket.SendBufferSize = OneKilobyte * kilobytes; 68 | socket.ReceiveBufferSize = OneKilobyte * kilobytes; 69 | } 70 | 71 | private void BindSocket(IPEndPoint endPoint) 72 | { 73 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 74 | socket.IOControl(SioUdpConnreset, new byte[] {0}, null); 75 | 76 | socket.Bind(endPoint); 77 | 78 | buffer = new byte[ReceiveBufferSize]; 79 | 80 | LocalPort = (ushort)((IPEndPoint)socket.LocalEndPoint).Port; 81 | LocalEndPoint = (IPEndPoint)socket.LocalEndPoint; 82 | 83 | IsRunning = true; 84 | } 85 | 86 | protected void SendTo(EndPoint endPoint, params byte[] data) 87 | { 88 | SendTo(data, data.Length, endPoint); 89 | } 90 | 91 | protected void SendTo(byte[] data, int size, EndPoint endPoint) 92 | { 93 | if (IsRunning) 94 | socket?.SendTo(data, size, SocketFlags.None, endPoint); 95 | } 96 | 97 | public void Receive() 98 | { 99 | if (IsRunning) 100 | { 101 | try 102 | { 103 | while (socket.Available > 0) 104 | { 105 | int size = socket.ReceiveFrom(buffer, ref sender); 106 | 107 | if (LimitedSizePackage && size >= MaxSizePacket) 108 | continue; 109 | 110 | OnRawHandler(buffer, size, sender); 111 | } 112 | } 113 | catch (Exception) { return; } 114 | } 115 | } 116 | 117 | public void TickUpdate() 118 | { 119 | uint elapsed = (uint)watch.ElapsedMilliseconds; 120 | 121 | watch.Restart(); 122 | OnTickUpdate(elapsed); 123 | } 124 | 125 | public bool IsAvailablePort(ushort port) 126 | { 127 | try 128 | { 129 | using (UdpClient udpPort = new UdpClient(port)) 130 | udpPort.Close(); 131 | 132 | return true; 133 | } 134 | catch (SocketException) { return false; } 135 | } 136 | 137 | public void Stop() 138 | { 139 | lock (locker) 140 | { 141 | if (IsRunning) 142 | { 143 | IsRunning = false; 144 | 145 | watch.Stop(); 146 | socket.Close(); 147 | 148 | OnListenerStopped(); 149 | } 150 | } 151 | } 152 | 153 | protected virtual void OnTickUpdate(uint deltaTime) { } 154 | 155 | protected virtual void OnListenerStarted() { OnStarted?.Invoke(); } 156 | protected virtual void OnListenerStopped() { OnStopped?.Invoke(); } 157 | 158 | protected virtual void OnRawHandler(byte[] data, int length, EndPoint endPoint) { } 159 | } 160 | 161 | public delegate void SendTo(byte[] data, int size, EndPoint endPoint); 162 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/UdpPending.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleUDP.Core 4 | { 5 | public class UdpPending 6 | { 7 | private Action rawSend; 8 | 9 | internal uint timer { get; private set; } 10 | internal byte[] buffer { get; private set; } 11 | 12 | internal UdpPending(Action rawSend) 13 | { 14 | this.rawSend = rawSend; 15 | } 16 | 17 | internal void SendPacket(byte[] packet) 18 | { 19 | rawSend(buffer = packet); 20 | } 21 | 22 | internal void UpdateTimer(uint deltaTime, uint interval) 23 | { 24 | if (buffer != null) 25 | { 26 | if ((timer += deltaTime) >= interval) 27 | { 28 | timer = 0; 29 | rawSend(buffer); 30 | } 31 | } 32 | } 33 | 34 | internal void ClearPacket() 35 | { 36 | timer = 0; 37 | buffer = null; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/Utils/UdpActive.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleUDP 4 | { 5 | public class UdpActive 6 | { 7 | private struct Element 8 | { 9 | internal T value; 10 | internal byte active; 11 | } 12 | 13 | private byte[] actives; 14 | private Element[] elements; 15 | 16 | public byte ActiveCount { get; private set; } 17 | public byte ElementCount { get; private set; } 18 | 19 | public UdpActive(byte capacity) 20 | { 21 | actives = new byte[capacity]; 22 | elements = new Element[capacity]; 23 | } 24 | 25 | public void AddElement(T value) 26 | { 27 | if (ElementCount < elements.Length) 28 | { 29 | actives[ElementCount] = ElementCount; 30 | elements[ElementCount].value = value; 31 | 32 | ElementCount++; 33 | } 34 | } 35 | 36 | public bool TrySetActive(out byte index) 37 | { 38 | index = 0; 39 | 40 | if (ActiveCount < ElementCount) 41 | { 42 | index = actives[ActiveCount]; 43 | elements[index].active = ActiveCount; 44 | 45 | ActiveCount++; 46 | return true; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | public void RemoveActive(byte index) 53 | { 54 | if (ActiveCount > 0) 55 | { 56 | byte elementActive = elements[index].active; 57 | byte lastActiveIndex = actives[ActiveCount - 1]; 58 | 59 | actives[elementActive] = lastActiveIndex; 60 | elements[lastActiveIndex].active = elementActive; 61 | 62 | elements[index].active = 0; 63 | actives[ActiveCount - 1] = index; 64 | 65 | ActiveCount--; 66 | } 67 | } 68 | 69 | public T GetActive(int nextActive) 70 | { 71 | if (nextActive >= ActiveCount) 72 | return default; 73 | 74 | return elements[actives[nextActive]].value; 75 | } 76 | 77 | public T this[int index] 78 | { 79 | get 80 | { 81 | if (index >= ElementCount) 82 | return default; 83 | 84 | return elements[index].value; 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/Utils/UdpBuffer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SimpleUDP.Utils 4 | { 5 | public class UdpBuffer 6 | { 7 | public int Count { get; private set; } 8 | 9 | internal readonly ushort MaxQueue = 16; 10 | internal readonly ushort Capacity = 256; 11 | 12 | private Queue[] queues; 13 | private int queueWrite, queueRead; 14 | 15 | private object locker = new object(); 16 | 17 | public UdpBuffer(ushort maxQueue = 16, ushort capacity = 256) 18 | { 19 | queues = new Queue[MaxQueue = maxQueue]; 20 | 21 | for (int i = 0; i < MaxQueue; i++) 22 | queues[i] = new Queue(Capacity = capacity); 23 | } 24 | 25 | public void AddElement(T value) 26 | { 27 | lock (locker) 28 | { 29 | if (queueWrite >= MaxQueue) 30 | queueWrite = 0; 31 | 32 | Count++; 33 | queues[queueWrite++].Enqueue(value); 34 | } 35 | } 36 | 37 | public T GetElement() 38 | { 39 | lock (locker) 40 | { 41 | if (Count != 0) 42 | { 43 | if (queueRead >= MaxQueue) 44 | queueRead = 0; 45 | 46 | Count--; 47 | return queues[queueRead++].Dequeue(); 48 | } 49 | else return default; 50 | } 51 | } 52 | 53 | public void Clear() 54 | { 55 | lock (locker) 56 | { 57 | Count = 0; 58 | queueRead = 0; 59 | queueWrite = 0; 60 | 61 | foreach (Queue queue in queues) 62 | queue.Clear(); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/Utils/UdpConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleUDP.Utils 4 | { 5 | public static class UdpConverter 6 | { 7 | public const byte SizeBool = 1; 8 | public const byte SizeChar = 2; 9 | 10 | public const byte SizeByte = 1; 11 | public const byte SizeSByte = 1; 12 | 13 | public const byte SizeShort = 2; 14 | public const byte SizeUShort = 2; 15 | 16 | public const byte SizeInt = 4; 17 | public const byte SizeUInt = 4; 18 | 19 | public const byte SizeLong = 8; 20 | public const byte SizeULong = 8; 21 | 22 | public const byte SizeFloat = 4; 23 | public const byte SizeDouble = 8; 24 | 25 | public static bool IsLittleEndian { get; private set; } 26 | 27 | static UdpConverter() 28 | { 29 | IsLittleEndian = BitConverter.IsLittleEndian; 30 | } 31 | 32 | #region ToBytes 33 | 34 | public static byte SetBool(bool value, byte[] array, int startIndex) 35 | { 36 | array[startIndex] = (byte)(value ? 1 : 0); 37 | 38 | return SizeByte; 39 | } 40 | 41 | public static byte SetByte(byte value, byte[] array, int startIndex) 42 | { 43 | array[startIndex] = value; 44 | 45 | return SizeByte; 46 | } 47 | 48 | public static byte SetSByte(sbyte value, byte[] array, int startIndex) 49 | { 50 | array[startIndex] = (byte)value; 51 | 52 | return SizeByte; 53 | } 54 | 55 | public static byte SetShort(short value, byte[] array, int startIndex) 56 | { 57 | if (IsLittleEndian) 58 | { 59 | array[startIndex ] = (byte)value; 60 | array[startIndex + 1] = (byte)(value >> 8); 61 | } 62 | else 63 | { 64 | array[startIndex + 1] = (byte)value; 65 | array[startIndex ] = (byte)(value >> 8); 66 | } 67 | 68 | return SizeShort; 69 | } 70 | 71 | public static byte SetUShort(ushort value, byte[] array, int startIndex) 72 | { 73 | if (IsLittleEndian) 74 | { 75 | array[startIndex ] = (byte)value; 76 | array[startIndex + 1] = (byte)(value >> 8); 77 | } 78 | else 79 | { 80 | array[startIndex + 1] = (byte)value; 81 | array[startIndex ] = (byte)(value >> 8); 82 | } 83 | 84 | return SizeShort; 85 | } 86 | 87 | public static byte SetInt(int value, byte[] array, int startIndex) 88 | { 89 | if (IsLittleEndian) 90 | { 91 | array[startIndex ] = (byte)value; 92 | array[startIndex + 1] = (byte)(value >> 8); 93 | array[startIndex + 2] = (byte)(value >> 16); 94 | array[startIndex + 3] = (byte)(value >> 24); 95 | } 96 | else 97 | { 98 | array[startIndex + 3] = (byte)value; 99 | array[startIndex + 2] = (byte)(value >> 8); 100 | array[startIndex + 1] = (byte)(value >> 16); 101 | array[startIndex ] = (byte)(value >> 24); 102 | } 103 | 104 | return SizeInt; 105 | } 106 | 107 | public static byte SetUInt(uint value, byte[] array, int startIndex) 108 | { 109 | if (IsLittleEndian) 110 | { 111 | array[startIndex ] = (byte)value; 112 | array[startIndex + 1] = (byte)(value >> 8); 113 | array[startIndex + 2] = (byte)(value >> 16); 114 | array[startIndex + 3] = (byte)(value >> 24); 115 | } 116 | else 117 | { 118 | array[startIndex + 3] = (byte)value; 119 | array[startIndex + 2] = (byte)(value >> 8); 120 | array[startIndex + 1] = (byte)(value >> 16); 121 | array[startIndex ] = (byte)(value >> 24); 122 | } 123 | 124 | return SizeInt; 125 | } 126 | 127 | public static byte SetLong(long value, byte[] array, int startIndex) 128 | { 129 | if (IsLittleEndian) 130 | { 131 | array[startIndex ] = (byte)value; 132 | array[startIndex + 1] = (byte)(value >> 8); 133 | array[startIndex + 2] = (byte)(value >> 16); 134 | array[startIndex + 3] = (byte)(value >> 24); 135 | array[startIndex + 4] = (byte)(value >> 32); 136 | array[startIndex + 5] = (byte)(value >> 40); 137 | array[startIndex + 6] = (byte)(value >> 48); 138 | array[startIndex + 7] = (byte)(value >> 56); 139 | } 140 | else 141 | { 142 | array[startIndex + 7] = (byte)value; 143 | array[startIndex + 6] = (byte)(value >> 8); 144 | array[startIndex + 5] = (byte)(value >> 16); 145 | array[startIndex + 4] = (byte)(value >> 24); 146 | array[startIndex + 3] = (byte)(value >> 32); 147 | array[startIndex + 2] = (byte)(value >> 40); 148 | array[startIndex + 1] = (byte)(value >> 48); 149 | array[startIndex ] = (byte)(value >> 56); 150 | } 151 | 152 | return SizeLong; 153 | } 154 | 155 | public static byte SetULong(ulong value, byte[] array, int startIndex) 156 | { 157 | if (IsLittleEndian) 158 | { 159 | array[startIndex ] = (byte)value; 160 | array[startIndex + 1] = (byte)(value >> 8); 161 | array[startIndex + 2] = (byte)(value >> 16); 162 | array[startIndex + 3] = (byte)(value >> 24); 163 | array[startIndex + 4] = (byte)(value >> 32); 164 | array[startIndex + 5] = (byte)(value >> 40); 165 | array[startIndex + 6] = (byte)(value >> 48); 166 | array[startIndex + 7] = (byte)(value >> 56); 167 | } 168 | else 169 | { 170 | array[startIndex + 7] = (byte)value; 171 | array[startIndex + 6] = (byte)(value >> 8); 172 | array[startIndex + 5] = (byte)(value >> 16); 173 | array[startIndex + 4] = (byte)(value >> 24); 174 | array[startIndex + 3] = (byte)(value >> 32); 175 | array[startIndex + 2] = (byte)(value >> 40); 176 | array[startIndex + 1] = (byte)(value >> 48); 177 | array[startIndex ] = (byte)(value >> 56); 178 | } 179 | 180 | return SizeLong; 181 | } 182 | 183 | public static byte SetFloat(float value, byte[] array, int startIndex) 184 | { 185 | int intValue = BitConverter.SingleToInt32Bits(value); 186 | 187 | if (IsLittleEndian) 188 | { 189 | array[startIndex ] = (byte)(intValue & 0xFF); 190 | array[startIndex + 1] = (byte)((intValue >> 8) & 0xFF); 191 | array[startIndex + 2] = (byte)((intValue >> 16) & 0xFF); 192 | array[startIndex + 3] = (byte)((intValue >> 24) & 0xFF); 193 | } 194 | else 195 | { 196 | array[startIndex + 3] = (byte)(intValue & 0xFF); 197 | array[startIndex + 2] = (byte)((intValue >> 8) & 0xFF); 198 | array[startIndex + 1] = (byte)((intValue >> 16) & 0xFF); 199 | array[startIndex ] = (byte)((intValue >> 24) & 0xFF); 200 | } 201 | 202 | return SizeFloat; 203 | } 204 | 205 | public static byte SetDouble(double value, byte[] array, int startIndex) 206 | { 207 | long longValue = BitConverter.DoubleToInt64Bits(value); 208 | 209 | if (IsLittleEndian) 210 | { 211 | array[startIndex ] = (byte)(longValue & 0xFF); 212 | array[startIndex + 1] = (byte)((longValue >> 8) & 0xFF); 213 | array[startIndex + 2] = (byte)((longValue >> 16) & 0xFF); 214 | array[startIndex + 3] = (byte)((longValue >> 24) & 0xFF); 215 | array[startIndex + 4] = (byte)((longValue >> 32) & 0xFF); 216 | array[startIndex + 5] = (byte)((longValue >> 40) & 0xFF); 217 | array[startIndex + 6] = (byte)((longValue >> 48) & 0xFF); 218 | array[startIndex + 7] = (byte)((longValue >> 56) & 0xFF); 219 | } 220 | else 221 | { 222 | array[startIndex + 7] = (byte)(longValue & 0xFF); 223 | array[startIndex + 6] = (byte)((longValue >> 8) & 0xFF); 224 | array[startIndex + 5] = (byte)((longValue >> 16) & 0xFF); 225 | array[startIndex + 4] = (byte)((longValue >> 24) & 0xFF); 226 | array[startIndex + 3] = (byte)((longValue >> 32) & 0xFF); 227 | array[startIndex + 2] = (byte)((longValue >> 40) & 0xFF); 228 | array[startIndex + 1] = (byte)((longValue >> 48) & 0xFF); 229 | array[startIndex ] = (byte)((longValue >> 56) & 0xFF); 230 | } 231 | 232 | return SizeDouble; 233 | } 234 | 235 | #endregion 236 | 237 | #region GetValue 238 | 239 | public static bool GetBool(byte[] array, int startIndex) 240 | { 241 | return array[startIndex] == 1; 242 | } 243 | 244 | public static byte GetByte(byte[] array, int startIndex) 245 | { 246 | return array[startIndex]; 247 | } 248 | 249 | public static sbyte GetSByte(byte[] array, int startIndex) 250 | { 251 | return (sbyte)array[startIndex]; 252 | } 253 | 254 | public static short GetShort(byte[] array, int startIndex) 255 | { 256 | if (IsLittleEndian) 257 | { 258 | return (short)(array[startIndex] | 259 | ((short)array[startIndex + 1] << 8)); 260 | } 261 | else 262 | { 263 | return (short)(array[startIndex + 1] | 264 | ((short)array[startIndex] << 8)); 265 | } 266 | } 267 | 268 | public static ushort GetUShort(byte[] array, int startIndex) 269 | { 270 | if (IsLittleEndian) 271 | { 272 | return (ushort)(array[startIndex] | 273 | ((ushort)array[startIndex + 1] << 8)); 274 | } 275 | else 276 | { 277 | return (ushort)(array[startIndex + 1] | 278 | ((ushort)array[startIndex] << 8)); 279 | } 280 | } 281 | 282 | public static int GetInt(byte[] array, int startIndex) 283 | { 284 | if (IsLittleEndian) 285 | { 286 | return (int)array[startIndex] | 287 | ((int)array[startIndex + 1] << 8 ) | 288 | ((int)array[startIndex + 2] << 16) | 289 | ((int)array[startIndex + 3] << 24); 290 | } 291 | else 292 | { 293 | return (int)array[startIndex + 3] | 294 | ((int)array[startIndex + 2] << 8 ) | 295 | ((int)array[startIndex + 1] << 16) | 296 | ((int)array[startIndex ] << 24); 297 | } 298 | } 299 | 300 | public static uint GetUInt(byte[] array, int startIndex) 301 | { 302 | if (IsLittleEndian) 303 | { 304 | return (uint)array[startIndex] | 305 | ((uint)array[startIndex + 1] << 8 ) | 306 | ((uint)array[startIndex + 2] << 16) | 307 | ((uint)array[startIndex + 3] << 24); 308 | } 309 | else 310 | { 311 | return (uint)array[startIndex + 3] | 312 | ((uint)array[startIndex + 2] << 8 ) | 313 | ((uint)array[startIndex + 1] << 16) | 314 | ((uint)array[startIndex ] << 24); 315 | } 316 | } 317 | 318 | public static long GetLong(byte[] array, int startIndex) 319 | { 320 | if (IsLittleEndian) 321 | { 322 | return (long)array[startIndex] | 323 | ((long)array[startIndex + 1] << 8 ) | 324 | ((long)array[startIndex + 2] << 16) | 325 | ((long)array[startIndex + 3] << 24) | 326 | ((long)array[startIndex + 4] << 32) | 327 | ((long)array[startIndex + 5] << 40) | 328 | ((long)array[startIndex + 6] << 48) | 329 | ((long)array[startIndex + 7] << 56); 330 | } 331 | else 332 | { 333 | return (long)array[startIndex + 7] | 334 | ((long)array[startIndex + 6] << 8 ) | 335 | ((long)array[startIndex + 5] << 16) | 336 | ((long)array[startIndex + 4] << 24) | 337 | ((long)array[startIndex + 3] << 32) | 338 | ((long)array[startIndex + 2] << 40) | 339 | ((long)array[startIndex + 1] << 48) | 340 | ((long)array[startIndex ] << 56); 341 | } 342 | } 343 | 344 | public static ulong GetULong(byte[] array, int startIndex) 345 | { 346 | if (IsLittleEndian) 347 | { 348 | return (ulong)array[startIndex] | 349 | ((ulong)array[startIndex + 1] << 8 ) | 350 | ((ulong)array[startIndex + 2] << 16) | 351 | ((ulong)array[startIndex + 3] << 24) | 352 | ((ulong)array[startIndex + 4] << 32) | 353 | ((ulong)array[startIndex + 5] << 40) | 354 | ((ulong)array[startIndex + 6] << 48) | 355 | ((ulong)array[startIndex + 7] << 56); 356 | } 357 | else 358 | { 359 | return (ulong)array[startIndex + 7] | 360 | ((ulong)array[startIndex + 6] << 8 ) | 361 | ((ulong)array[startIndex + 5] << 16) | 362 | ((ulong)array[startIndex + 4] << 24) | 363 | ((ulong)array[startIndex + 3] << 32) | 364 | ((ulong)array[startIndex + 2] << 40) | 365 | ((ulong)array[startIndex + 1] << 48) | 366 | ((ulong)array[startIndex ] << 56); 367 | } 368 | } 369 | 370 | public static float GetFloat(byte[] array, int startIndex) 371 | { 372 | int intValue; 373 | 374 | if (IsLittleEndian) 375 | { 376 | intValue = (int)array[startIndex] | 377 | ((int)array[startIndex + 1] << 8 ) | 378 | ((int)array[startIndex + 2] << 16) | 379 | ((int)array[startIndex + 3] << 24); 380 | } 381 | else 382 | { 383 | intValue = (int)array[startIndex + 3] | 384 | ((int)array[startIndex + 2] << 8 ) | 385 | ((int)array[startIndex + 1] << 16) | 386 | ((int)array[startIndex ] << 24); 387 | } 388 | 389 | return BitConverter.Int32BitsToSingle(intValue); 390 | } 391 | 392 | public static double GetDouble(byte[] array, int startIndex) 393 | { 394 | long longValue; 395 | 396 | if (IsLittleEndian) 397 | { 398 | longValue = (long)array[startIndex] | 399 | ((long)array[startIndex + 1] << 8 ) | 400 | ((long)array[startIndex + 2] << 16) | 401 | ((long)array[startIndex + 3] << 24) | 402 | ((long)array[startIndex + 4] << 32) | 403 | ((long)array[startIndex + 5] << 40) | 404 | ((long)array[startIndex + 6] << 48) | 405 | ((long)array[startIndex + 7] << 56); 406 | } 407 | else 408 | { 409 | longValue = (long)array[startIndex + 7] | 410 | ((long)array[startIndex + 6] << 8 ) | 411 | ((long)array[startIndex + 5] << 16) | 412 | ((long)array[startIndex + 4] << 24) | 413 | ((long)array[startIndex + 3] << 32) | 414 | ((long)array[startIndex + 2] << 40) | 415 | ((long)array[startIndex + 1] << 48) | 416 | ((long)array[startIndex ] << 56); 417 | } 418 | 419 | return BitConverter.Int64BitsToDouble(longValue); 420 | } 421 | 422 | #endregion 423 | } 424 | } -------------------------------------------------------------------------------- /SimpleUDP/Core/Utils/UdpLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SimpleUDP 4 | { 5 | public static class UdpLog 6 | { 7 | public delegate void Log(object mes); 8 | 9 | public static Log Info = Console.Write; 10 | public static Log Warning = Console.Write; 11 | public static Log Error = Console.Write; 12 | 13 | public static void Initialize(Log logInfo, Log logWarning = null, Log logError = null) 14 | { 15 | Info = logInfo; 16 | Warning = logWarning; 17 | Error = logError; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /SimpleUDP/Extensions/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using SimpleUDP.Core; 4 | 5 | namespace SimpleUDP 6 | { 7 | public static class Extensions 8 | { 9 | 10 | #region UdpServer 11 | 12 | public static void SendReliable(this UdpServer server, uint peerId, Packet packet) 13 | { 14 | server.SendReliable(peerId, packet.Data, packet.Length, 0); 15 | } 16 | 17 | public static void SendUnreliable(this UdpServer server, uint peerId, Packet packet) 18 | { 19 | server.SendUnreliable(peerId, packet.Data, packet.Length, 0); 20 | } 21 | 22 | public static void SendAllReliable(this UdpServer server, Packet packet, uint ignoreId = 0) 23 | { 24 | server.SendAllReliable(packet.Data, packet.Length, 0, ignoreId); 25 | } 26 | 27 | public static void SendAllUnreliable(this UdpServer server, Packet packet, uint ignoreId = 0) 28 | { 29 | server.SendAllUnreliable(packet.Data, packet.Length, 0, ignoreId); 30 | } 31 | 32 | public static void SendBroadcast(this UdpServer server, ushort port, Packet packet) 33 | { 34 | server.SendBroadcast(port, packet.Data, packet.Length); 35 | } 36 | 37 | public static void SendUnconnected(this UdpServer server, EndPoint endPoint, Packet packet) 38 | { 39 | server.SendUnconnected(endPoint, packet.Data, packet.Length); 40 | } 41 | 42 | #endregion 43 | 44 | #region UdpClient 45 | 46 | public static void SendReliable(this UdpClient client, Packet packet) 47 | { 48 | client.SendReliable(packet.Data, packet.Length, 0); 49 | } 50 | 51 | public static void SendUnreliable(this UdpClient client, Packet packet) 52 | { 53 | client.SendUnreliable(packet.Data, packet.Length, 0); 54 | } 55 | 56 | public static void SendBroadcast(this UdpClient client, ushort port, Packet packet) 57 | { 58 | client.SendBroadcast(port, packet.Data, packet.Length); 59 | } 60 | 61 | public static void SendUnconnected(this UdpClient client, EndPoint endPoint, Packet packet) 62 | { 63 | client.SendUnconnected(endPoint, packet.Data, packet.Length); 64 | } 65 | 66 | #endregion 67 | 68 | #region UdpPeer 69 | 70 | public static void SendReliable(this UdpPeer peer, Packet packet) 71 | { 72 | peer.SendReliable(packet.Data, packet.Length, 0); 73 | } 74 | 75 | public static void SendUnreliable(this UdpPeer peer, Packet packet) 76 | { 77 | peer.SendUnreliable(packet.Data, packet.Length, 0); 78 | } 79 | 80 | #endregion 81 | 82 | } 83 | } -------------------------------------------------------------------------------- /SimpleUDP/Extensions/Packet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using SimpleUDP.Utils; 4 | 5 | namespace SimpleUDP 6 | { 7 | public class Packet 8 | { 9 | public int Offset => read; 10 | public int Length => write; 11 | 12 | public byte[] Data { get; private set; } 13 | 14 | public const ushort MaxSizePacket = 1432; 15 | 16 | private int write, read; 17 | 18 | public static Packet Write(ushort maxSizeData = MaxSizePacket) => 19 | new Packet(new byte[maxSizeData]); 20 | 21 | public static Packet Read(byte[] packet) => 22 | new Packet(packet, packet.Length, 0); 23 | 24 | public static Packet Read(byte[] packet, int length, int offset) => 25 | new Packet(packet, length, offset); 26 | 27 | internal Packet(byte[] data, int length = 0, int offset = 0) 28 | { 29 | Data = data; 30 | read = offset; 31 | write = length; 32 | } 33 | 34 | public Packet Reset() 35 | { 36 | read = 0; 37 | write = 0; 38 | 39 | return this; 40 | } 41 | 42 | public byte[] GetArray(int offset = 0) 43 | { 44 | byte[] buffer = new byte[Length]; 45 | Buffer.BlockCopy(Data, offset, buffer, 0, Length); 46 | 47 | return buffer; 48 | } 49 | 50 | private bool CanWrite(int size) 51 | { 52 | return write + size <= Data.Length; 53 | } 54 | 55 | private bool CanRead(int size) 56 | { 57 | return read + size <= Length; 58 | } 59 | 60 | #region Bool 61 | 62 | public static Packet Bool(bool value, ushort maxSizeData = 256) => 63 | new Packet(new byte[maxSizeData]).Bool(value); 64 | 65 | public Packet Bool(bool value) 66 | { 67 | if (!CanWrite(UdpConverter.SizeBool)) 68 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 69 | 70 | write += UdpConverter.SetBool(value, Data, write); 71 | return this; 72 | } 73 | 74 | public bool Bool() 75 | { 76 | if (!CanRead(UdpConverter.SizeBool)) 77 | return false; 78 | 79 | bool value = UdpConverter.GetBool(Data, read); 80 | read += sizeof(bool); 81 | 82 | return value; 83 | } 84 | 85 | #endregion 86 | 87 | #region Byte 88 | 89 | public static Packet Byte(byte value, ushort maxSizeData = 256) => 90 | new Packet(new byte[maxSizeData]).Byte(value); 91 | 92 | public Packet Byte(byte value) 93 | { 94 | if (!CanWrite(UdpConverter.SizeByte)) 95 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 96 | 97 | write += UdpConverter.SetByte(value, Data, write); 98 | return this; 99 | } 100 | 101 | public byte Byte() 102 | { 103 | if (!CanRead(UdpConverter.SizeByte)) 104 | return 0; 105 | 106 | byte value = UdpConverter.GetByte(Data, read); 107 | read += sizeof(byte); 108 | 109 | return value; 110 | } 111 | 112 | #endregion 113 | 114 | #region SByte 115 | 116 | public static Packet SByte(sbyte value, ushort maxSizeData = 256) => 117 | new Packet(new byte[maxSizeData]).SByte(value); 118 | 119 | public Packet SByte(sbyte value) 120 | { 121 | if (!CanWrite(UdpConverter.SizeSByte)) 122 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 123 | 124 | write += UdpConverter.SetSByte(value, Data, write); 125 | return this; 126 | } 127 | 128 | public sbyte SByte() 129 | { 130 | if (!CanRead(UdpConverter.SizeSByte)) 131 | return 0; 132 | 133 | sbyte value = UdpConverter.GetSByte(Data, read); 134 | read += sizeof(sbyte); 135 | 136 | return value; 137 | } 138 | 139 | #endregion 140 | 141 | #region Short 142 | 143 | public static Packet Short(short value, ushort maxSizeData = 256) => 144 | new Packet(new byte[maxSizeData]).Short(value); 145 | 146 | public Packet Short(short value) 147 | { 148 | if (!CanWrite(UdpConverter.SizeShort)) 149 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 150 | 151 | write += UdpConverter.SetShort(value, Data, write); 152 | return this; 153 | } 154 | 155 | public short Short() 156 | { 157 | if (!CanRead(UdpConverter.SizeShort)) 158 | return 0; 159 | 160 | short value = UdpConverter.GetShort(Data, read); 161 | read += sizeof(short); 162 | 163 | return value; 164 | } 165 | 166 | #endregion 167 | 168 | #region UShort 169 | 170 | public static Packet UShort(ushort value, ushort maxSizeData = 256) => 171 | new Packet(new byte[maxSizeData]).UShort(value); 172 | 173 | public Packet UShort(ushort value) 174 | { 175 | if (!CanWrite(UdpConverter.SizeUShort)) 176 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 177 | 178 | write += UdpConverter.SetUShort(value, Data, write); 179 | return this; 180 | } 181 | 182 | public ushort UShort() 183 | { 184 | if (!CanRead(UdpConverter.SizeUShort)) 185 | return 0; 186 | 187 | ushort value = UdpConverter.GetUShort(Data, read); 188 | read += sizeof(ushort); 189 | 190 | return value; 191 | } 192 | 193 | #endregion 194 | 195 | #region Int 196 | 197 | public static Packet Int(int value, ushort maxSizeData = 256) => 198 | new Packet(new byte[maxSizeData]).Int(value); 199 | 200 | public Packet Int(int value) 201 | { 202 | if (!CanWrite(UdpConverter.SizeInt)) 203 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 204 | 205 | write += UdpConverter.SetInt(value, Data, write); 206 | return this; 207 | } 208 | 209 | public int Int() 210 | { 211 | if (!CanRead(UdpConverter.SizeInt)) 212 | return 0; 213 | 214 | int value = UdpConverter.GetInt(Data, read); 215 | read += sizeof(int); 216 | 217 | return value; 218 | } 219 | 220 | #endregion 221 | 222 | #region UInt 223 | 224 | public static Packet UInt(uint value, ushort maxSizeData = 256) => 225 | new Packet(new byte[maxSizeData]).UInt(value); 226 | 227 | public Packet UInt(uint value) 228 | { 229 | if (!CanWrite(UdpConverter.SizeUInt)) 230 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 231 | 232 | write += UdpConverter.SetUInt(value, Data, write); 233 | return this; 234 | } 235 | 236 | public uint UInt() 237 | { 238 | if (!CanRead(UdpConverter.SizeUInt)) 239 | return 0; 240 | 241 | uint value = UdpConverter.GetUInt(Data, read); 242 | read += sizeof(uint); 243 | 244 | return value; 245 | } 246 | 247 | #endregion 248 | 249 | #region Long 250 | 251 | public static Packet Long(long value, ushort maxSizeData = 256) => 252 | new Packet(new byte[maxSizeData]).Long(value); 253 | 254 | public Packet Long(long value) 255 | { 256 | if (!CanWrite(UdpConverter.SizeLong)) 257 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 258 | 259 | write += UdpConverter.SetLong(value, Data, write); 260 | return this; 261 | } 262 | 263 | public long Long() 264 | { 265 | if (!CanRead(UdpConverter.SizeLong)) 266 | return 0; 267 | 268 | long value = UdpConverter.GetLong(Data, read); 269 | read += sizeof(long); 270 | 271 | return value; 272 | } 273 | 274 | #endregion 275 | 276 | #region ULong 277 | 278 | public static Packet ULong(ulong value, ushort maxSizeData = 256) => 279 | new Packet(new byte[maxSizeData]).ULong(value); 280 | 281 | public Packet ULong(ulong value) 282 | { 283 | if (!CanWrite(UdpConverter.SizeULong)) 284 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 285 | 286 | write += UdpConverter.SetULong(value, Data, write); 287 | return this; 288 | } 289 | 290 | public ulong ULong() 291 | { 292 | if (!CanRead(UdpConverter.SizeULong)) 293 | return 0; 294 | 295 | ulong value = UdpConverter.GetULong(Data, read); 296 | read += sizeof(ulong); 297 | 298 | return value; 299 | } 300 | 301 | #endregion 302 | 303 | #region Float 304 | 305 | public static Packet Float(float value, ushort maxSizeData = 256) => 306 | new Packet(new byte[maxSizeData]).Float(value); 307 | 308 | public Packet Float(float value) 309 | { 310 | if (!CanWrite(UdpConverter.SizeFloat)) 311 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 312 | 313 | write += UdpConverter.SetFloat(value, Data, write); 314 | return this; 315 | } 316 | 317 | public float Float() 318 | { 319 | if (!CanRead(UdpConverter.SizeFloat)) 320 | return 0; 321 | 322 | float value = UdpConverter.GetFloat(Data, read); 323 | read += sizeof(float); 324 | 325 | return value; 326 | } 327 | 328 | #endregion 329 | 330 | #region Double 331 | 332 | public static Packet Double(double value, ushort maxSizeData = 256) => 333 | new Packet(new byte[maxSizeData]).Double(value); 334 | 335 | public Packet Double(double value) 336 | { 337 | if (!CanWrite(UdpConverter.SizeDouble)) 338 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 339 | 340 | write += UdpConverter.SetDouble(value, Data, write); 341 | return this; 342 | } 343 | 344 | public double Double() 345 | { 346 | if (!CanRead(UdpConverter.SizeDouble)) 347 | return 0; 348 | 349 | double value = UdpConverter.GetDouble(Data, read); 350 | read += sizeof(double); 351 | 352 | return value; 353 | } 354 | 355 | #endregion 356 | 357 | #region Char 358 | 359 | public static Packet Char(char value, ushort maxSizeData = 512) => 360 | new Packet(new byte[maxSizeData]).Char(value); 361 | 362 | public Packet Char(char value) 363 | { 364 | if (!CanWrite(UdpConverter.SizeChar)) 365 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 366 | 367 | byte[] buffer = BitConverter.GetBytes(value); 368 | 369 | Data[write] = buffer[0]; 370 | Data[write + 1] = buffer[1]; 371 | 372 | write += 2; 373 | return this; 374 | } 375 | 376 | public char Char() 377 | { 378 | if (!CanRead(UdpConverter.SizeChar)) 379 | return ' '; 380 | 381 | char value = BitConverter.ToChar(Data, read); 382 | 383 | read += 2; 384 | return value; 385 | } 386 | 387 | #endregion 388 | 389 | #region String 390 | 391 | public static Packet String(string value, ushort maxSizeData = MaxSizePacket) => 392 | new Packet(new byte[maxSizeData]).String(value); 393 | 394 | public Packet String(string value) 395 | { 396 | if (!CanWrite(value.Length * UdpConverter.SizeChar)) 397 | throw new IndexOutOfRangeException($"The maximum length should not exceed {Data.Length} bytes"); 398 | 399 | byte[] buffer = Encoding.UTF8.GetBytes(value); 400 | 401 | UShort((ushort)buffer.Length); 402 | Buffer.BlockCopy(buffer, 0 , Data, write, buffer.Length); 403 | 404 | write += buffer.Length; 405 | return this; 406 | } 407 | 408 | public string String() 409 | { 410 | ushort count = UShort(); 411 | 412 | if (count == 0 || !CanRead(count)) 413 | return ""; 414 | 415 | string value = Encoding.UTF8.GetString(Data, read, count); 416 | 417 | read += count; 418 | return value; 419 | } 420 | 421 | #endregion 422 | } 423 | } -------------------------------------------------------------------------------- /SimpleUDP/UdpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using SimpleUDP.Core; 4 | using SimpleUDP.Utils; 5 | 6 | namespace SimpleUDP 7 | { 8 | public class UdpClient : UdpListener 9 | { 10 | public Action OnConnected; 11 | public Action OnDisconnected; 12 | 13 | public Action OnReceiveReliable; 14 | public Action OnReceiveUnreliable; 15 | 16 | public Action OnReceiveBroadcast; 17 | public Action OnReceiveUnconnected; 18 | 19 | public uint TimeOut = 5000; 20 | public string KeyConnection = ""; 21 | 22 | public uint Id => udpPeer.Id; 23 | public uint Rtt => udpPeer.Rtt; 24 | public State State => udpPeer.State; 25 | public EndPoint EndPoint => udpPeer.EndPoint; 26 | 27 | public Reason ReasonDisconnection => udpPeer.ReasonDisconnection; 28 | 29 | private UdpPeer udpPeer; 30 | private EndPoint remoteEndPoint; 31 | 32 | private const int ClientKilobytes = 128; 33 | 34 | public UdpClient() 35 | { 36 | udpPeer = new UdpPeer(SendTo) 37 | { 38 | TimeOut = TimeOut, 39 | OnLostConnection = LostConnection, 40 | }; 41 | } 42 | 43 | protected override void OnListenerStarted() 44 | { 45 | AdjustBufferSizes(ClientKilobytes); 46 | 47 | UdpLog.Info($"[Client] Started on port: {LocalPort}"); 48 | base.OnListenerStarted(); 49 | } 50 | 51 | public void Connect(string address, ushort port) 52 | { 53 | if (udpPeer.State != State.NoConnect) 54 | return; 55 | 56 | SetIPEndPoint(address, port); 57 | udpPeer.SendConnect(remoteEndPoint, Id, KeyConnection); 58 | 59 | UdpLog.Info($"[Client] Connection to: {EndPoint}"); 60 | } 61 | 62 | public void Disconnect() 63 | { 64 | udpPeer.Disconnect(); 65 | } 66 | 67 | public void QuietDisconnect() 68 | { 69 | udpPeer.QuietDisconnect(); 70 | } 71 | 72 | public void SendReliable(byte[] packet) 73 | { 74 | udpPeer.SendReliable(packet, packet.Length); 75 | } 76 | 77 | public void SendUnreliable(byte[] packet) 78 | { 79 | udpPeer.SendUnreliable(packet, packet.Length); 80 | } 81 | 82 | public void SendReliable(byte[] packet, int length, int offset = 0) 83 | { 84 | udpPeer.SendReliable(packet, length, offset); 85 | } 86 | 87 | public void SendUnreliable(byte[] packet, int length, int offset = 0) 88 | { 89 | udpPeer.SendUnreliable(packet, length, offset); 90 | } 91 | 92 | protected sealed override void OnTickUpdate(uint deltaTime) 93 | { 94 | UpdateTimer(deltaTime); 95 | } 96 | 97 | public void UpdateTimer(uint deltaTime) 98 | { 99 | if (IsRunning) 100 | udpPeer.UpdateTimer(deltaTime); 101 | } 102 | 103 | private void AcceptConnect(byte[] data, int length, EndPoint endPoint) 104 | { 105 | if (!UdpPeer.KeyMatching(KeyConnection, data, length, UdpPeer.HeaderSize)) 106 | { 107 | SendTo(endPoint, UdpHeader.Disconnected, (byte)Reason.IncorrectKey); 108 | return; 109 | } 110 | 111 | if (!remoteEndPoint.Equals(endPoint)) 112 | { 113 | SendTo(endPoint, UdpHeader.Disconnected, 0); 114 | return; 115 | } 116 | 117 | if (udpPeer.State == State.Connecting) 118 | { 119 | udpPeer.SetConnected(UdpConverter.GetUInt(data, UdpIndex.Unreliable)); 120 | 121 | UdpLog.Info($"[Client] Connected successfully!"); 122 | OnPeerConnected(); 123 | } 124 | 125 | SendTo(endPoint, UdpHeader.Connected); 126 | } 127 | 128 | private void AcceptDisconnect(byte[] data, int length, EndPoint endPoint) 129 | { 130 | byte reason = data[UdpIndex.Reason]; 131 | 132 | HandlerDiconnected(data, length, endPoint); 133 | SendTo(endPoint, UdpHeader.Disconnected, reason); 134 | } 135 | 136 | private void AcceptPing(EndPoint endPoint) 137 | { 138 | if (udpPeer.State == State.Connected) 139 | SendTo(endPoint, UdpHeader.Pong); 140 | else 141 | SendTo(endPoint, UdpHeader.Disconnected, 0); 142 | } 143 | 144 | public void SendBroadcast(ushort port, byte[] packet, int length) 145 | { 146 | if (IsRunning && EnableBroadcast && port != 0) 147 | { 148 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 149 | 150 | buffer[UdpIndex.Header] = UdpHeader.Broadcast; 151 | Buffer.BlockCopy(packet, 0, buffer, UdpIndex.Unreliable, length); 152 | 153 | SendTo(new IPEndPoint(IPAddress.Broadcast, port), buffer); 154 | } 155 | else throw new Exception("It is not possible to send a Broadcast message if EnableBroadcast = false or Port = 0."); 156 | } 157 | 158 | public void SendUnconnected(EndPoint endPoint, byte[] packet, int length) 159 | { 160 | if (IsRunning) 161 | { 162 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 163 | 164 | buffer[UdpIndex.Header] = UdpHeader.Unconnected; 165 | Buffer.BlockCopy(packet, 0, buffer, UdpIndex.Unreliable, length); 166 | 167 | SendTo(endPoint, buffer); 168 | } 169 | } 170 | 171 | protected sealed override void OnRawHandler(byte[] data, int length, EndPoint endPoint) 172 | { 173 | switch (data[UdpIndex.Header]) 174 | { 175 | case UdpHeader.Unreliable: 176 | HandlerUnreliable(data, length, endPoint); 177 | return; 178 | case UdpHeader.Reliable: 179 | HandlerReliable(data, length, endPoint); 180 | return; 181 | case UdpHeader.ReliableAck: 182 | HandlerReliableAck(data, length, endPoint); 183 | return; 184 | case UdpHeader.Ping: 185 | AcceptPing(endPoint); 186 | return; 187 | case UdpHeader.Pong: 188 | udpPeer.HandlerPong(); 189 | return; 190 | case UdpHeader.Connect: 191 | AcceptConnect(data, length, endPoint); 192 | return; 193 | case UdpHeader.Disconnect: 194 | AcceptDisconnect(data, length, endPoint); 195 | return; 196 | case UdpHeader.Disconnected: 197 | HandlerDiconnected(data, length, endPoint); 198 | return; 199 | case UdpHeader.Broadcast: 200 | HandlerBroadcast(data, length, endPoint); 201 | return; 202 | case UdpHeader.Unconnected: 203 | HandlerUnconnected(data, length, endPoint); 204 | return; 205 | } 206 | } 207 | 208 | private void HandlerDiconnected(byte[] data, int length, EndPoint endPoint) 209 | { 210 | if (udpPeer.State != State.NoConnect) 211 | { 212 | udpPeer.ReasonDisconnection = (Reason)data[UdpIndex.Reason]; 213 | LostConnection(udpPeer); 214 | } 215 | } 216 | 217 | private void HandlerReliable(byte[] data, int length, EndPoint endPoint) 218 | { 219 | if (udpPeer.IsNewAck(data)) 220 | { 221 | byte[] buffer = CopyPacket(data, length, UdpIndex.Reliable); 222 | OnPeerReceiveReliable(buffer); 223 | } 224 | } 225 | 226 | private void HandlerUnreliable(byte[] data, int length, EndPoint endPoint) 227 | { 228 | byte[] buffer = CopyPacket(data, length, UdpIndex.Unreliable); 229 | OnPeerReceiveUnreliable(buffer); 230 | } 231 | 232 | private void HandlerReliableAck(byte[] data, int length, EndPoint endPoint) 233 | { 234 | udpPeer.ClearAck(data); 235 | } 236 | 237 | private void HandlerBroadcast(byte[] data, int length, EndPoint endPoint) 238 | { 239 | byte[] buffer = new byte[length - UdpIndex.Unreliable]; 240 | Buffer.BlockCopy(data, UdpIndex.Unreliable, buffer, 0, length - UdpIndex.Unreliable); 241 | OnReceiveBroadcast?.Invoke(endPoint, buffer); 242 | } 243 | 244 | private void HandlerUnconnected(byte[] data, int length, EndPoint endPoint) 245 | { 246 | byte[] buffer = new byte[length - UdpIndex.Unreliable]; 247 | Buffer.BlockCopy(data, UdpIndex.Unreliable, buffer, 0, length - UdpIndex.Unreliable); 248 | OnReceiveUnconnected?.Invoke(endPoint, buffer); 249 | } 250 | 251 | private void LostConnection(UdpPeer udpPeer) 252 | { 253 | udpPeer.SetDisconnected(); 254 | 255 | UdpLog.Info($"[Client] Disconnected from: {EndPoint}"); 256 | OnPeerDisconnected(); 257 | } 258 | 259 | private void SetIPEndPoint(string host, ushort port) 260 | { 261 | IPAddress address = Dns.GetHostAddresses(host)[0]; 262 | 263 | remoteEndPoint = new IPEndPoint(address, port); 264 | } 265 | 266 | private byte[] CopyPacket(byte[] data, int length, int offset) 267 | { 268 | byte[] buffer = new byte[length - offset]; 269 | Buffer.BlockCopy(data, offset, buffer, 0, length - offset); 270 | 271 | return buffer; 272 | } 273 | 274 | protected override void OnListenerStopped() 275 | { 276 | udpPeer.SetDisconnected(); 277 | 278 | UdpLog.Info($"[Client] Stopped!"); 279 | base.OnListenerStopped(); 280 | } 281 | 282 | protected virtual void OnPeerConnected() 283 | { 284 | OnConnected?.Invoke(); 285 | } 286 | 287 | protected virtual void OnPeerDisconnected() 288 | { 289 | OnDisconnected?.Invoke(); 290 | } 291 | 292 | protected virtual void OnPeerReceiveReliable(byte[] packet) 293 | { 294 | OnReceiveReliable?.Invoke(packet); 295 | } 296 | 297 | protected virtual void OnPeerReceiveUnreliable(byte[] packet) 298 | { 299 | OnReceiveUnreliable?.Invoke(packet); 300 | } 301 | } 302 | } -------------------------------------------------------------------------------- /SimpleUDP/UdpPeer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Text; 4 | using SimpleUDP.Core; 5 | using SimpleUDP.Utils; 6 | using System.Diagnostics; 7 | using System.Threading.Tasks; 8 | 9 | namespace SimpleUDP 10 | { 11 | public enum State 12 | { 13 | NoConnect, 14 | Connecting, 15 | Connected, 16 | Disconnecting 17 | } 18 | public enum Reason : byte 19 | { 20 | Nothing, 21 | Kicked, 22 | TimeOut, 23 | ServerIsFull, 24 | IncorrectKey, 25 | Disconnected, 26 | QuietDisconnected, 27 | } 28 | public class UdpPeer 29 | { 30 | public uint Id { get; private set; } 31 | public uint Rtt { get; private set; } 32 | 33 | public State State { get; private set; } 34 | public EndPoint EndPoint { get; private set; } 35 | 36 | public Reason ReasonDisconnection { get; internal set; } 37 | 38 | public uint ElapsedMilliseconds => (uint)watch.ElapsedMilliseconds; 39 | 40 | internal uint TimeOut = 5000; 41 | internal uint Interval = 100; 42 | internal int DelayResendPing = 1000; 43 | 44 | internal Action OnLostConnection; 45 | 46 | internal const byte HeaderSize = 5; 47 | 48 | private SendTo sendTo; 49 | private Stopwatch watch; 50 | private UdpPending pending; 51 | private UdpChannel channel; 52 | 53 | private bool isSendingPing; 54 | private const float RetryTime = 1.4f; 55 | private const float SmoothingRtt = 0.125f; 56 | 57 | internal UdpPeer(SendTo sendTo) 58 | { 59 | this.sendTo = sendTo; 60 | 61 | watch = new Stopwatch(); 62 | pending = new UdpPending(RawSend); 63 | channel = new UdpChannel(RawSend); 64 | 65 | State = State.NoConnect; 66 | ReasonDisconnection = Reason.Nothing; 67 | } 68 | 69 | public void Disconnect() 70 | { 71 | ReasonDisconnection = Reason.Disconnected; 72 | SendDisconnect(Reason.Disconnected); 73 | } 74 | 75 | public void QuietDisconnect() 76 | { 77 | if (State != State.NoConnect) 78 | { 79 | ReasonDisconnection = Reason.QuietDisconnected; 80 | 81 | SetDisconnected(); 82 | OnLostConnection?.Invoke(this); 83 | } 84 | } 85 | 86 | public void SendReliable(byte[] packet) 87 | { 88 | if (State == State.Connected) 89 | channel.SendReliable(packet, packet.Length, 0); 90 | } 91 | 92 | public void SendUnreliable(byte[] packet) 93 | { 94 | if (State == State.Connected) 95 | channel.SendUnreliable(packet, packet.Length, 0); 96 | } 97 | 98 | public void SendReliable(byte[] packet, int length, int offset = 0) 99 | { 100 | if (State == State.Connected) 101 | channel.SendReliable(packet, length, offset); 102 | } 103 | 104 | public void SendUnreliable(byte[] packet, int length, int offset = 0) 105 | { 106 | if (State == State.Connected) 107 | channel.SendUnreliable(packet, length, offset); 108 | } 109 | 110 | internal void RawSend(params byte[] packet) 111 | { 112 | sendTo(packet, packet.Length, EndPoint); 113 | } 114 | 115 | internal void UpdateTimer(uint deltaTime) 116 | { 117 | if (State != State.NoConnect) 118 | { 119 | if (State == State.Connected) 120 | channel.UpdateTimer(deltaTime); 121 | 122 | pending.UpdateTimer(deltaTime, Interval); 123 | 124 | if (ElapsedMilliseconds >= TimeOut) 125 | { 126 | if (State != State.NoConnect) 127 | { 128 | ReasonDisconnection = Reason.TimeOut; 129 | 130 | SetDisconnected(); 131 | OnLostConnection?.Invoke(this); 132 | } 133 | } 134 | } 135 | } 136 | 137 | internal void SendConnect(EndPoint endPoint, uint newId, string key) 138 | { 139 | if (State == State.NoConnect) 140 | { 141 | State = State.Connecting; 142 | 143 | Id = newId; 144 | EndPoint = endPoint; 145 | 146 | SendConnectPacket(UdpHeader.Connect, newId, key); 147 | } 148 | } 149 | 150 | internal void SendDisconnect(Reason reason) 151 | { 152 | if (State != State.Disconnecting) 153 | { 154 | if (State == State.NoConnect) 155 | return; 156 | 157 | ClearPending(); 158 | 159 | State = State.Disconnecting; 160 | SendPending(UdpHeader.Disconnect, (byte)reason); 161 | } 162 | } 163 | 164 | internal void SetConnected(uint peerId) 165 | { 166 | if (State == State.Connecting) 167 | { 168 | Id = peerId; 169 | Rtt = ElapsedMilliseconds; 170 | 171 | ClearPending(); 172 | channel.Initialize(); 173 | 174 | State = State.Connected; 175 | SendPing(); 176 | } 177 | } 178 | 179 | internal void SetDisconnected() 180 | { 181 | if (State != State.NoConnect) 182 | { 183 | ClearPending(); 184 | 185 | channel.ClearChannel(); 186 | State = State.NoConnect; 187 | } 188 | } 189 | 190 | internal bool IsNewAck(byte[] data) 191 | { 192 | if (State == State.Connected) 193 | return channel.IsNewAck(data); 194 | 195 | return false; 196 | } 197 | 198 | internal void ClearAck(byte[] data) 199 | { 200 | if (State == State.Connected) 201 | channel.ClearAck(data); 202 | } 203 | 204 | internal static bool KeyMatching(string key, byte[] packet, int length, int offset) 205 | { 206 | if (key.Length != length - offset) 207 | return false; 208 | 209 | for (int i = 0; i < length - offset; i++) 210 | { 211 | if (key[i] != packet[offset + i]) 212 | return false; 213 | } 214 | 215 | return true; 216 | } 217 | 218 | private async void SendPing() 219 | { 220 | await Task.Delay(DelayResendPing); 221 | 222 | if (State == State.Connected) 223 | { 224 | isSendingPing = true; 225 | SendPending(UdpHeader.Ping); 226 | } 227 | } 228 | 229 | internal void HandlerPong() 230 | { 231 | if (State == State.Connected && isSendingPing) 232 | { 233 | isSendingPing = false; 234 | ClearPending(); 235 | SendPing(); 236 | } 237 | } 238 | 239 | private void SendConnectPacket(byte header, uint peerId, string key) 240 | { 241 | byte[] bytesKey = Encoding.ASCII.GetBytes(key); 242 | byte[] packet = new byte[HeaderSize + bytesKey.Length]; 243 | 244 | packet[UdpIndex.Header] = header; 245 | UdpConverter.SetUInt(peerId, packet, UdpIndex.Unreliable); 246 | Buffer.BlockCopy(bytesKey, 0, packet, HeaderSize, bytesKey.Length); 247 | 248 | SendPending(packet); 249 | } 250 | 251 | private void UpdateRtt(uint measuredRTT) 252 | { 253 | Rtt = (uint)((1 - SmoothingRtt) * Rtt + SmoothingRtt * measuredRTT); 254 | channel.Interval = (uint)Math.Max(10, Rtt * RetryTime); 255 | } 256 | 257 | private void SendPending(params byte[] packet) 258 | { 259 | watch.Restart(); 260 | pending.SendPacket(packet); 261 | } 262 | 263 | private void ClearPending() 264 | { 265 | watch.Stop(); 266 | pending.ClearPacket(); 267 | 268 | UpdateRtt(ElapsedMilliseconds); 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /SimpleUDP/UdpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using SimpleUDP.Core; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | 7 | namespace SimpleUDP 8 | { 9 | public class UdpServer : UdpListener 10 | { 11 | public Action OnConnected; 12 | public Action OnDisconnected; 13 | 14 | public Action OnReceiveReliable; 15 | public Action OnReceiveUnreliable; 16 | 17 | public Action OnReceiveBroadcast; 18 | public Action OnReceiveUnconnected; 19 | 20 | public uint TimeOut = 5000; 21 | public uint MaxConnections = 256; 22 | public string KeyConnection = ""; 23 | public uint MaxNumberId = ushort.MaxValue; 24 | 25 | public ReadOnlyDictionary Connections; 26 | public uint ConnectionsCount => (uint)connections.Count; 27 | 28 | private Stack disconnectedPeers; 29 | private Dictionary peers; 30 | private Dictionary connections; 31 | 32 | private const int ServerKilobytes = 2048; 33 | 34 | public UdpServer() 35 | { 36 | disconnectedPeers = new Stack(); 37 | 38 | peers = new Dictionary(); 39 | connections = new Dictionary(); 40 | 41 | Connections = new ReadOnlyDictionary(connections); 42 | } 43 | 44 | protected override void OnListenerStarted() 45 | { 46 | AdjustBufferSizes(ServerKilobytes); 47 | 48 | UdpLog.Info($"[Server] Started on port: {LocalPort}"); 49 | base.OnListenerStarted(); 50 | } 51 | 52 | public void Disconnect(uint peerId) 53 | { 54 | if (Connections.TryGetValue(peerId, out UdpPeer peer)) 55 | peer.SendDisconnect(Reason.Kicked); 56 | } 57 | 58 | public void SendReliable(uint peerId, byte[] packet) 59 | { 60 | SendReliable(peerId, packet, packet.Length); 61 | } 62 | 63 | public void SendUnreliable(uint peerId, byte[] packet) 64 | { 65 | SendUnreliable(peerId, packet, packet.Length); 66 | } 67 | 68 | public void SendReliable(uint peerId, byte[] packet, int length, int offset = 0) 69 | { 70 | if (Connections.TryGetValue(peerId, out UdpPeer peer)) 71 | peer.SendReliable(packet, length, offset); 72 | } 73 | 74 | public void SendUnreliable(uint peerId, byte[] packet, int length, int offset = 0) 75 | { 76 | if (Connections.TryGetValue(peerId, out UdpPeer peer)) 77 | peer.SendUnreliable(packet, length, offset); 78 | } 79 | 80 | public void SendAllReliable(byte[] packet, uint ignoreId = 0) 81 | { 82 | SendAllReliable(packet, packet.Length, 0, ignoreId); 83 | } 84 | 85 | public void SendAllUnreliable(byte[] packet, uint ignoreId = 0) 86 | { 87 | SendAllUnreliable(packet, packet.Length, 0, ignoreId); 88 | } 89 | 90 | public void SendAllReliable(byte[] packet, int length, int offset, uint ignoreId = 0) 91 | { 92 | lock (connections) 93 | { 94 | foreach (UdpPeer peer in Connections.Values) 95 | { 96 | if (peer.Id != ignoreId) 97 | peer.SendReliable(packet, length, offset); 98 | } 99 | } 100 | } 101 | 102 | public void SendAllUnreliable(byte[] packet, int length, int offset, uint ignoreId = 0) 103 | { 104 | lock (connections) 105 | { 106 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 107 | 108 | buffer[UdpIndex.Header] = UdpHeader.Unreliable; 109 | Buffer.BlockCopy(packet, offset, buffer, UdpIndex.Unreliable, length); 110 | 111 | foreach (UdpPeer peer in Connections.Values) 112 | { 113 | if (peer.Id != ignoreId) 114 | peer.RawSend(buffer); 115 | } 116 | } 117 | } 118 | 119 | protected sealed override void OnTickUpdate(uint deltaTime) 120 | { 121 | if (IsRunning) 122 | { 123 | UpdateTimer(deltaTime); 124 | UpdateDisconnecting(); 125 | } 126 | } 127 | 128 | public void UpdateTimer(uint deltaTime) 129 | { 130 | if (IsRunning) 131 | { 132 | lock (peers) 133 | { 134 | foreach (UdpPeer peer in peers.Values) 135 | peer.UpdateTimer(deltaTime); 136 | } 137 | } 138 | } 139 | 140 | public void UpdateDisconnecting() 141 | { 142 | if (IsRunning) 143 | { 144 | while (disconnectedPeers.Count != 0) 145 | RemovePeer(disconnectedPeers.Pop()); 146 | } 147 | } 148 | 149 | private void AcceptPing(EndPoint endPoint) 150 | { 151 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 152 | SendTo(endPoint, UdpHeader.Pong); 153 | else 154 | SendTo(endPoint, UdpHeader.Disconnected, 0); 155 | } 156 | 157 | private void AcceptConnect(byte[] data, int length, EndPoint endPoint) 158 | { 159 | lock (peers) 160 | { 161 | if (!UdpPeer.KeyMatching(KeyConnection, data, length, UdpPeer.HeaderSize)) 162 | { 163 | SendTo(endPoint, UdpHeader.Disconnected, (byte)Reason.IncorrectKey); 164 | return; 165 | } 166 | 167 | if (!peers.ContainsKey(endPoint)) 168 | { 169 | if (peers.Count >= MaxConnections) 170 | { 171 | SendTo(endPoint, UdpHeader.Disconnected, (byte)Reason.ServerIsFull); 172 | return; 173 | } 174 | 175 | UdpPeer udpPeer = new UdpPeer(SendTo) 176 | { 177 | TimeOut = TimeOut, 178 | OnLostConnection = disconnectedPeers.Push, 179 | }; 180 | 181 | peers.Add(endPoint, udpPeer); 182 | udpPeer.SendConnect(endPoint, GetNewId(udpPeer), KeyConnection); 183 | } 184 | } 185 | } 186 | 187 | private void AcceptDisconnect(byte[] data, int length, EndPoint endPoint) 188 | { 189 | byte reason = data[UdpIndex.Reason]; 190 | 191 | HandlerDiconnected(data, length, endPoint); 192 | SendTo(endPoint, UdpHeader.Disconnected, reason); 193 | } 194 | 195 | public void SendBroadcast(ushort port, byte[] packet, int length) 196 | { 197 | if (IsRunning && EnableBroadcast && port != 0) 198 | { 199 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 200 | 201 | buffer[UdpIndex.Header] = UdpHeader.Broadcast; 202 | Buffer.BlockCopy(packet, 0, buffer, UdpIndex.Unreliable, length); 203 | 204 | SendTo(new IPEndPoint(IPAddress.Broadcast, port), buffer); 205 | } 206 | else throw new Exception("It is not possible to send a Broadcast message if EnableBroadcast = false or Port = 0."); 207 | } 208 | 209 | public void SendUnconnected(EndPoint endPoint, byte[] packet, int length) 210 | { 211 | if (IsRunning) 212 | { 213 | byte[] buffer = new byte[length + UdpIndex.Unreliable]; 214 | 215 | buffer[UdpIndex.Header] = UdpHeader.Unconnected; 216 | Buffer.BlockCopy(packet, 0, buffer, UdpIndex.Unreliable, length); 217 | 218 | SendTo(endPoint, buffer); 219 | } 220 | } 221 | 222 | protected sealed override void OnRawHandler(byte[] data, int length, EndPoint endPoint) 223 | { 224 | switch (data[UdpIndex.Header]) 225 | { 226 | case UdpHeader.Unreliable: 227 | HandlerUnreliable(data, length, endPoint); 228 | return; 229 | case UdpHeader.Reliable: 230 | HandlerReliable(data, length, endPoint); 231 | return; 232 | case UdpHeader.ReliableAck: 233 | HandlerReliableAck(data, length, endPoint); 234 | return; 235 | case UdpHeader.Ping: 236 | AcceptPing(endPoint); 237 | return; 238 | case UdpHeader.Pong: 239 | HandlerPong(endPoint); 240 | return; 241 | case UdpHeader.Connect: 242 | AcceptConnect(data, length, endPoint); 243 | return; 244 | case UdpHeader.Connected: 245 | HandlerConnected(endPoint); 246 | return; 247 | case UdpHeader.Disconnect: 248 | AcceptDisconnect(data, length, endPoint); 249 | return; 250 | case UdpHeader.Disconnected: 251 | HandlerDiconnected(data, length, endPoint); 252 | return; 253 | case UdpHeader.Broadcast: 254 | HandlerBroadcast(data, length, endPoint); 255 | return; 256 | case UdpHeader.Unconnected: 257 | HandlerUnconnected(data, length, endPoint); 258 | return; 259 | } 260 | } 261 | 262 | private void HandlerPong(EndPoint endPoint) 263 | { 264 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 265 | peer.HandlerPong(); 266 | } 267 | 268 | private void HandlerConnected(EndPoint endPoint) 269 | { 270 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 271 | { 272 | if (connections.ContainsKey(peer.Id)) 273 | return; 274 | 275 | lock (connections) 276 | { 277 | peer.SetConnected(peer.Id); 278 | peer.OnLostConnection = LostConnection; 279 | 280 | connections.Add(peer.Id, peer); 281 | 282 | UdpLog.Info($"[Server] Client Connected: {peer.Id}!"); 283 | } 284 | 285 | OnPeerConnected(peer); 286 | } 287 | } 288 | 289 | private void HandlerDiconnected(byte[] data, int length, EndPoint endPoint) 290 | { 291 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 292 | { 293 | if (peer.State != State.NoConnect) 294 | { 295 | peer.ReasonDisconnection = (Reason)data[UdpIndex.Reason]; 296 | 297 | peer.SetDisconnected(); 298 | LostConnection(peer); 299 | } 300 | } 301 | } 302 | 303 | private void HandlerReliable(byte[] data, int length, EndPoint endPoint) 304 | { 305 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 306 | { 307 | if (peer.IsNewAck(data)) 308 | { 309 | byte[] buffer = CopyPacket(data, length, UdpIndex.Reliable); 310 | OnPeerReceiveReliable(peer, buffer); 311 | } 312 | } 313 | } 314 | 315 | private void HandlerUnreliable(byte[] data, int length, EndPoint endPoint) 316 | { 317 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 318 | { 319 | byte[] buffer = CopyPacket(data, length, UdpIndex.Unreliable); 320 | OnPeerReceiveUnreliable(peer, buffer); 321 | } 322 | } 323 | 324 | private void HandlerReliableAck(byte[] data, int length, EndPoint endPoint) 325 | { 326 | if (peers.TryGetValue(endPoint, out UdpPeer peer)) 327 | peer.ClearAck(data); 328 | } 329 | 330 | private void HandlerBroadcast(byte[] data, int length, EndPoint endPoint) 331 | { 332 | byte[] buffer = new byte[length - UdpIndex.Unreliable]; 333 | Buffer.BlockCopy(data, UdpIndex.Unreliable, buffer, 0, length - UdpIndex.Unreliable); 334 | 335 | OnReceiveBroadcast?.Invoke(endPoint, buffer); 336 | } 337 | 338 | private void HandlerUnconnected(byte[] data, int length, EndPoint endPoint) 339 | { 340 | byte[] buffer = new byte[length - UdpIndex.Unreliable]; 341 | Buffer.BlockCopy(data, UdpIndex.Unreliable, buffer, 0, length - UdpIndex.Unreliable); 342 | 343 | OnReceiveUnconnected?.Invoke(endPoint, buffer); 344 | } 345 | 346 | private void LostConnection(UdpPeer udpPeer) 347 | { 348 | lock (connections) 349 | { 350 | disconnectedPeers.Push(udpPeer); 351 | connections.Remove(udpPeer.Id); 352 | 353 | UdpLog.Info($"[Server] Client Disconnected: {udpPeer.Id}!"); 354 | } 355 | 356 | OnPeerDisconnected(udpPeer); 357 | } 358 | 359 | private void RemovePeer(UdpPeer udpPeer) 360 | { 361 | lock (peers) 362 | { 363 | if (peers.ContainsKey(udpPeer.EndPoint)) 364 | peers.Remove(udpPeer.EndPoint); 365 | } 366 | } 367 | 368 | private uint GetNewId(UdpPeer udpPeer) 369 | { 370 | uint newId = (uint)(Math.Abs(udpPeer.GetHashCode()) % MaxNumberId); 371 | 372 | if (newId == 0) newId += 1; 373 | 374 | while (connections.ContainsKey(newId)) 375 | { 376 | newId = (newId + 1) % MaxNumberId; 377 | if (newId == 0) newId += 1; 378 | } 379 | 380 | return newId; 381 | } 382 | 383 | private byte[] CopyPacket(byte[] data, int length, int offset) 384 | { 385 | byte[] buffer = new byte[length - offset]; 386 | Buffer.BlockCopy(data, offset, buffer, 0, length - offset); 387 | 388 | return buffer; 389 | } 390 | 391 | protected override void OnListenerStopped() 392 | { 393 | lock (peers) 394 | { 395 | foreach (UdpPeer peer in peers.Values) 396 | peer.SetDisconnected(); 397 | 398 | peers.Clear(); 399 | connections.Clear(); 400 | disconnectedPeers.Clear(); 401 | 402 | UdpLog.Info($"[Server] Stopped!"); 403 | base.OnListenerStopped(); 404 | } 405 | } 406 | 407 | protected virtual void OnPeerConnected(UdpPeer peer) 408 | { 409 | OnConnected?.Invoke(peer); 410 | } 411 | 412 | protected virtual void OnPeerDisconnected(UdpPeer peer) 413 | { 414 | OnDisconnected?.Invoke(peer); 415 | } 416 | 417 | protected virtual void OnPeerReceiveReliable(UdpPeer peer, byte[] packet) 418 | { 419 | OnReceiveReliable?.Invoke(peer, packet); 420 | } 421 | 422 | protected virtual void OnPeerReceiveUnreliable(UdpPeer peer, byte[] packet) 423 | { 424 | OnReceiveUnreliable?.Invoke(peer, packet); 425 | } 426 | } 427 | } --------------------------------------------------------------------------------