├── LICENSE ├── README.md ├── basic-enet-tutorial-series-part1-introduction.md ├── basic-enet-tutorial-series-part2-chatapp.md ├── files ├── chat_screen.cpp └── chat_screen.hpp └── images ├── .comments └── thumbnail.png.xml ├── Basic_ENet_Tutorial_Series_1.webp ├── Basic_ENet_Tutorial_Series_2_Part_1.jpg ├── Basic_ENet_Tutorial_Series_2_Part_2.jpg └── thumbnail.png /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | including for purposes of Section 3(b); and 307 | 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public licenses. 411 | Notwithstanding, Creative Commons may elect to apply one of its public 412 | licenses to material it publishes and in those instances will be 413 | considered the “Licensor.” The text of the Creative Commons public 414 | licenses is dedicated to the public domain under the CC0 Public Domain 415 | Dedication. Except for the limited purpose of indicating that material 416 | is shared under a Creative Commons public license or as otherwise 417 | permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the public 425 | licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. 428 | 429 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ENet Tutorial Series! 2 | 3 | ![alt text](./images/thumbnail.png "Usergames' Enet Tutorial Series") 4 | 5 | ## Home Of The ENet Tutorial Series! 6 | 7 | Here you will find a blend of videos, text, and code snippets! We hope that this supplementary content helps you learn the magical world of networking! 8 | 9 | From zero to hero, this series will start off slow assuming you know next to nothing about networking code, and we will build up to using more advanced concepts and examples. 10 | 11 | [Part 1 - Introduction](./basic-enet-tutorial-series-part1-introduction.md) 12 | [Part 2 - Chat App](./basic-enet-tutorial-series-part2-chatapp.md) 13 | 14 | © Copyright 2020, Ezra Hradecky ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)). 15 | -------------------------------------------------------------------------------- /basic-enet-tutorial-series-part1-introduction.md: -------------------------------------------------------------------------------- 1 | [home](./index.md) | [next](./basic-enet-tutorial-series-part2-chatapp.md) 2 | 3 | # ENet Tutorial Series! 4 | 5 | ![alt text](./images/thumbnail.png "Usergames' Enet Tutorial Series") 6 | 7 | [![Basic ENet Tutorial Series 1/3 - Server/Client Setup](./images/Basic_ENet_Tutorial_Series_1.webp)](https://www.youtube.com/embed/FxrKS_1zE9s) 8 | 9 | ## Welcome To ENet 10 | 11 | Creating networked applications is very interesting indeed. 12 | 13 | *"ENet's purpose is to provide a relatively thin, simple and robust network communication layer on top of UDP (User Datagram Protocol). The primary feature it provides is optional reliable, in-order delivery of packets." — [enet.bespin.org](http://enet.bespin.org/Tutorial.html)* 14 | 15 | > ℹ️ **Please Note!** 16 | > 17 | > A chunk of the code and directives here has taken directly from the [ENet website](http://enet.bespin.org/Tutorial.html) itself, we suggest checking it out! It's a good basis before getting into creating more advanced things such as a multiplayer game. 18 | 19 | ## Setting Up 20 | 21 | The first step to our grand ENet adventure is to install the darn thing. The process will different depending if you are on Windows, Mac or Linux. 22 | 23 | ### Microsoft Windows 24 | 25 | ENet can be downloaded [here](enet.bespin.org/download/enet-1.3.14.tar.gz) or on [github](https://github.com/lsalzman/enet). You may simply use the included enet.lib or enet64.lib static libraries. However, if you wish to build the library yourself, then the following instructions apply. 26 | 27 | There is an included MSVC 6 project (enet.dsp) which you may use to build a suitable library file. Alternatively, you may simply drag all the ENet source files into your main project. 28 | 29 | You will have to link to the Winsock2 libraries, so make sure to add ws2_32.lib and winmm.lib to your library list (Project Settings | Link | Object/library modules). 30 | 31 | Load the included enet.dsp. MSVC may ask you to convert it if you are on a newer version of MSVC - just allow the conversion and save the resulting project as "enet" or similar. After you build this project, it will output an "enet.lib" file to either the "Debug/" or "Release/" directory, depending on which configuration you have selected to build. By default, it should produce "Debug/enet.lib". 32 | 33 | You may then copy the resulting "enet.lib" file and the header files found in the "include/" directory to your other projects and add it to their library lists. Make sure to also link against "ws2_32.lib" and "winmm.lib" as described above. 34 | 35 | ### Unix-like Operating Systems 36 | 37 | If you are using an ENet release, then you should simply be able to build it by doing the following: 38 | 39 | ```console 40 | ./configure && make && make install 41 | ``` 42 | 43 | If you obtained the package from github, you must have automake and autoconf available to generate the build system first by doing the following command before using the above mentioned build procedure 44 | 45 | ```autoreconf -vfi``` 46 | 47 | ### Linux 48 | 49 | Linux makes things quite simple; write one the following commands based on your distribution. 50 | 51 | #### Debian-based 52 | 53 | ```console 54 | foo@bar:~# apt install enet 55 | ``` 56 | 57 | #### Arch-based 58 | 59 | ```console 60 | btw@arch:~# pacman -S enet 61 | ``` 62 | 63 | ## Code 64 | 65 | ### ./client/main.c 66 | 67 | ```cpp 68 | /* ./client/main.c */ 69 | 70 | #include 71 | #include 72 | 73 | int main(int argc, char ** argv) 74 | { 75 | if(enet_initialize() != 0) 76 | { 77 | fprintf(stderr, "An error occurred while initializing ENet!\n"); 78 | return EXIT_FAILURE; 79 | } 80 | atexit(enet_deinitialize); 81 | ``` 82 | 83 | So, we've got our first two self-explanatory lines of code here. `enet_initialize();` returns an error code if things are to not initialize correctly, returning 0 if everything went well. As such, we check if the function doesn't return 0 and if it does we handle the error. 84 | 85 | Next, we use `atexit(enet_deinitialize);` to setup proper deinitialization of enet to occur at the exit of the program. So far, so good. 86 | 87 | Now we will create a client using `enet_host_create()`. 88 | 89 | ```cpp 90 | ENetHost* client; 91 | client = enet_host_create(NULL, 1, 1, 0, 0); 92 | 93 | if(client == NULL) 94 | { 95 | fprintf(stderr, "An error occurred while trying to create an ENet client host!\n"); 96 | return EXIT_FAILURE; 97 | } 98 | ``` 99 | 100 | Both clients and servers are constructed with `enet_host_create()`. When no address is specified to bind the host to, it will be a client. Otherwise it is a server. 101 | 102 | Five parameters can be specified, in order they are: 103 | 104 | 1. `ENetAddress* address` 105 | 2. `size_t peerCount` 106 | 3. `size_t channelLimit` 107 | 4. `enet_uint32 incomingBandwidth` 108 | 5. `enet_uint32 outgoingBandwidth` 109 | 110 | So, in my example here, we are creating the client that will connect to one peer (the server), that has channel limit of 1, and has both unlimited incoming and outgoing bandwidth. 111 | 112 | ```cpp 113 | ENetAddress address; 114 | ENetEvent event; 115 | ENetPeer* peer; 116 | 117 | enet_address_set_host(&address, "127.0.0.1"); 118 | address.port = 7777; 119 | 120 | peer = enet_host_connect(client, &address, 1, 0); 121 | if(peer == NULL) 122 | { 123 | fprintf(stderr, "No available peers for initiating an ENet connection!\n"); 124 | return EXIT_FAILURE; 125 | } 126 | ``` 127 | 128 | Here we create various variables; `ENetAddress` to hold all ip/port data, `ENetEvent` to handle events, `ENetPeer` to hold the server within. Peers can be either servers or clients. A server hold a list of connected peers. 129 | 130 | We setup the address using the `enet_address_set_host()` function with a reference to the address variable as the first paramater. The second being the ip of the desired server to connect to. 131 | 132 | Afterwards, we use `enet_host_connect` to both connect to the server and set our peer accordingly. The parameters are as follows: 133 | 134 | * `ENetHost* host` 135 | * `const ENetAddress* address` 136 | * `size_t channelCount` 137 | * `enet_uint32 data` 138 | 139 | ```cpp 140 | if(enet_host_service(client, &event, 5000) > 0 && 141 | event.type == ENET_EVENT_TYPE_CONNECT) 142 | { 143 | puts("Connection to 127.0.0.1:7777 succeeded."); 144 | } 145 | else 146 | { 147 | enet_peer_reset(peer); 148 | puts("Connection to 127.0.0.1:7777 failed."); 149 | return EXIT_SUCCESS; 150 | } 151 | ``` 152 | 153 | To check for any events the server might be sending us, we use `enet_host_service` with the following paramaters: 154 | 155 | * `ENetHost* host` 156 | * `ENetEvent* event` 157 | * `enet_uint32 timeout` 158 | 159 | Events will be stored in our `ENetEvent event;` variable. Here we check if we received any events from the server and if so and if the event is of type `ENET_EVENT_TYPE_CONNECT` we print out the fact that we've managed to connect successfully to the server. Otherwise we reset our peer with `enet_peer_reset` print an error message and return the program. Because this is not a crash, I return 0 but I would suggest to simply return to a previous menu of the application instead of closing it altogether. 160 | 161 | ```cpp 162 | // [...Game Loop...] 163 | 164 | while(enet_host_service(client, &event, 1000) > 0) 165 | { 166 | switch(event.type) 167 | { 168 | case ENET_EVENT_TYPE_RECEIVE: 169 | printf ("A packet of length %u containing %s was received from %x:%u on channel %u.\n", 170 | event.packet -> dataLength, 171 | event.packet -> data, 172 | event.peer -> address.host, 173 | event.peer -> address.port, 174 | event.channelID); 175 | break; 176 | } 177 | } 178 | ``` 179 | 180 | Somewhere within the game loop, you'll want to write this chunk of code. We're once again using `enet_host_service` to get some event from the server. This time I've put it in a while loop so that we may process every event that we received since our last call. I've set the timeout to 1000ms, but in a real time application such as an action video game, you might consider setting it to 0. 181 | 182 | Next we switch the `event.type` to find out what event we are currently processing. Here we are simply checking for the case `ENET_EVENT_TYPE_RECEIVE` where we do nothing more than print our a bunch of values, used for testing at the moment. 183 | 184 | ```cpp 185 | enet_peer_disconnect(peer, 0); 186 | 187 | while(enet_host_service(client, &event, 3000) > 0) 188 | { 189 | switch(event.type) 190 | { 191 | case ENET_EVENT_TYPE_RECEIVE: 192 | enet_packet_destroy(event.packet); 193 | break; 194 | case ENET_EVENT_TYPE_DISCONNECT: 195 | puts("Disconnection succeeded."); 196 | break; 197 | } 198 | } 199 | 200 | return EXIT_SUCCESS; 201 | ``` 202 | 203 | Finally, we call `enet_peer_disconnect` when we want to disconnect our client from the server. We call our faithful `enet_host_service` with a small timeout, 3 seconds in this case, to make sure the server is very much aware of the fact we are disconnecting! We do this by checking the received events and discarding events that are not of `ENET_EVENT_TYPE_DISCONNECT` and once we do receive that event we may print the fact we've successfully disconnected. And of course, we return 0 at the very end of our program. 204 | 205 | ## Last Few Steps! 206 | 207 | We've seen nearly all the functions that we need to create a server! For this reason I will simply show you the server code, you'll see that it's very simple indeed! 208 | 209 | > ⚠️ **Important Note!** 210 | > 211 | > We never suggest anyone to do `while(true)`! 212 | That said, we used it to showcase ENet code more and software architecture less. 213 | Perhaps we will modify this in the future. 214 | 215 | ### ./server/main.c 216 | 217 | ```cpp 218 | /* ./server/main.c */ 219 | 220 | #include 221 | #include 222 | 223 | int main (int argc, char ** argv) 224 | { 225 | if (enet_initialize () != 0) 226 | { 227 | fprintf (stderr, "An error occurred while initializing ENet.\n"); 228 | return EXIT_FAILURE; 229 | } 230 | atexit (enet_deinitialize); 231 | 232 | ENetEvent event; 233 | ENetAddress address; 234 | ENetHost* server; 235 | 236 | /* Bind the server to the default localhost. */ 237 | /* A specific host address can be specified by */ 238 | /* enet_address_set_host (& address, "x.x.x.x"); */ 239 | address.host = ENET_HOST_ANY; // This allows 240 | /* Bind the server to port 7777. */ 241 | address.port = 7777; 242 | 243 | 244 | 245 | server = enet_host_create (&address /* the address to bind the server host to */, 246 | 32 /* allow up to 32 clients and/or outgoing connections */, 247 | 1 /* allow up to 1 channel to be used, 0. */, 248 | 0 /* assume any amount of incoming bandwidth */, 249 | 0 /* assume any amount of outgoing bandwidth */); 250 | 251 | if (server == NULL) 252 | { 253 | printf("An error occurred while trying to create an ENet server host."); 254 | return 1; 255 | } 256 | 257 | // gameloop 258 | while(true) 259 | { 260 | ENetEvent event; 261 | /* Wait up to 1000 milliseconds for an event. */ 262 | while (enet_host_service (server, & event, 1000) > 0) 263 | { 264 | switch (event.type) 265 | { 266 | case ENET_EVENT_TYPE_CONNECT: 267 | printf ("A new client connected from %x:%u.\n", 268 | event.peer -> address.host, 269 | event.peer -> address.port); 270 | break; 271 | 272 | case ENET_EVENT_TYPE_RECEIVE: 273 | printf ("A packet of length %u containing %s was received from %s on channel %u.\n", 274 | event.packet -> dataLength, 275 | event.packet -> data, 276 | event.peer -> data, 277 | event.channelID); 278 | /* Clean up the packet now that we're done using it. */ 279 | enet_packet_destroy (event.packet); 280 | break; 281 | 282 | case ENET_EVENT_TYPE_DISCONNECT: 283 | printf ("%s disconnected.\n", event.peer -> data); 284 | /* Reset the peer's client information. */ 285 | event.peer -> data = NULL; 286 | } 287 | } 288 | } 289 | 290 | enet_host_destroy(server); 291 | 292 | return 0; 293 | } 294 | ``` 295 | 296 | ## Not too hard to understand, right? 297 | 298 | If you still have some trouble understanding, I'm sure you'll get the hang of it as we play around with it in this tutorial series. 299 | 300 | [home](./index.md) | [next](./basic-enet-tutorial-series-part2-chatapp.md) 301 | 302 | © Copyright 2020, Ezra Hradecky ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)). 303 | -------------------------------------------------------------------------------- /basic-enet-tutorial-series-part2-chatapp.md: -------------------------------------------------------------------------------- 1 | [previous](./basic-enet-tutorial-series-part1-introduction.md) | [next](#) 2 | 3 | # ENet Tutorial Series! 4 | 5 | ![alt text](./images/thumbnail.png "Usergames' Enet Tutorial Series") 6 | 7 | [![Basic ENet Tutorial Series 2/3 Part 1 - Chat App](./images/Basic_ENet_Tutorial_Series_2_Part_1.jpg)](https://www.youtube.com/embed/lWlZOTFW5lM) 8 | 9 | [![Basic ENet Tutorial Series 2/3 Part 2 - Chat App](./images/Basic_ENet_Tutorial_Series_2_Part_2.jpg)](https://www.youtube.com/embed/lWlZOTFW5lM) 10 | 11 | ## Creating A Basic Chatting Program 12 | 13 | Now that we know how to set up a client and a server, it would be nice if we could send packets between them to do.. Well, anything. What better way to learn than to create a chatting program! 14 | 15 | ## Setting Up 16 | 17 | We will be building upon the code we wrote last time. 18 | So, if you haven't already, we suggest you read the first part. 19 | I'll use **ncurses** as the backbone of this terminal based chatting app, but I will not be explaining everything that I've done.. If you wish to follow step by step exactly what I did, then you can download the files here: 20 | 21 | - [chat_screen.hpp](./files/chat_screen.hpp) 22 | - [chat_screen.cpp](./files/chat_screen.cpp) 23 | 24 | I've kept them completly sperated from everything else so that we can focus on the subject in hand: ENet. 25 | 26 | > ⚠️ **Important Note!** 27 | > 28 | > We never suggest anyone to do `while(true)`! 29 | That said, we used it to showcase ENet code more and software architecture less. 30 | Perhaps we will modify this in the future. 31 | 32 | To use my `ChatScreen` class, start by adding the files to your project. Afterwards, you can set up everything with the following code: 33 | 34 | ```cpp 35 | /* ./client/main.cpp */ 36 | 37 | #include 38 | #include 39 | #include "chat_screen.hpp" 40 | 41 | static ChatScreen chatScreen; // Create static chatscreen object 42 | 43 | int main(int argc, char ** argv) 44 | { 45 | // User Can Set Their Username 46 | printf("Please Enter Your Username?\n"); 47 | 48 | // Variable that will store username 49 | char username[80]; 50 | 51 | // Put User input into 'username' 52 | scanf("%s", &username); 53 | 54 | chatScreen.Init(); // Initialize the chatScreen 55 | while(true) // Game Loop 56 | { 57 | std::string msg = chatScreen.CheckBoxInput(); // Get (and wait for) input from screen 58 | chatScreen.PostMessage("username", msg.c_str()); // Puts user input into chat log (displays on screen) 59 | } 60 | 61 | return EXIT_SUCCESS; 62 | } 63 | ``` 64 | 65 | We might make a ncurses tutorial in the future, but for now just see this as a magic box. 66 | It's not important for you to understand ncurses for this tutorial. 67 | 68 | ## Code 69 | 70 | > ℹ️ **Note!** 71 | > 72 | > We will be using C++. This will help simplify the tutorial... The advanced series will most likely be in C. 73 | 74 | ### ./client/main.cpp 75 | 76 | Last time we created a client, we didn't keep track of any data. ENet kept track of who was connected to the server, but that's about it. Now that we are creating something a little more complicated, we will need to actually keep track of some data. I'll do this by creating a class, but it can just as well be done with a struct. 77 | 78 | ```cpp 79 | // The Client's data will be managed by this class 80 | class ClientData 81 | { 82 | private: 83 | int m_id; 84 | std::string m_username; 85 | 86 | public: 87 | ClientData(int id) : m_id(id) {} 88 | 89 | void SetUsername(std::string username){ m_username = username; } 90 | 91 | int GetID(){ return m_id; } 92 | std::string GetUsername(){ return m_username; } 93 | }; 94 | ``` 95 | 96 | You'll notice that we will keep track of two things here. The client's username and id. Okay, the username that makes sense, but what about this 'id'? 97 | Well, this will be a number giving to the client by the server and is a way for clients to keep track of other clients. Given the fact that the server is already plenty aware of what the various clients are doing, it won't need the id. It is truly only for our clients to synchronize. 98 | 99 | Next, we will create a `std::map` of the clients, that way we can grab the client we want by their id and not their position in an array. 100 | This will require you to include map. 101 | 102 | ```cpp 103 | // A std::map of all clients that are currently connected. 104 | std::map client_map; // id, ClientData* 105 | ``` 106 | 107 | And we mustn't forget, a simple way to send packets to a peer! (the server in this case) 108 | 109 | ```cpp 110 | // A simple way to send packets to a peer 111 | void SendPacket(ENetPeer* peer, const char* data) 112 | { 113 | // Create the packet using enet_packet_create and the data we want to send 114 | // We are using the flag ENET_PACKET_FLAG_RELIABLE that acts a bit like TCP. 115 | // That is to say, it'll make sure the packet makes it to the destination. 116 | ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE); 117 | 118 | // Finally, send the packet to the peer on channel 0! 119 | enet_peer_send(peer, 0, packet); 120 | } 121 | ``` 122 | 123 | For now we've hard coded it to always send packets that are `ENET_PACKET_FLAG_RELIABLE` on channel 0, but in the final part of this tutorial series, we will changing this. 124 | You'll notice that we do `strlen(data) + 1` this states the size of the data we want to send, the length of the str data + 1 (because strings end with '\0'). 125 | 126 | ```cpp 127 | // The main function 128 | int main(int argc, char ** argv) 129 | { 130 | // User set username 131 | printf("Please Enter Your Username?\n"); 132 | char username[80]; 133 | scanf("%s", &username); 134 | 135 | // Normal enet initialization (see part 1) 136 | if(enet_initialize() != 0) 137 | { 138 | fprintf(stderr, "An error occurred while initializing ENet!\n"); 139 | return EXIT_FAILURE; 140 | } 141 | atexit(enet_deinitialize); 142 | 143 | ENetHost* client; 144 | client = enet_host_create(NULL, 1, 1, 0, 0); 145 | 146 | if(client == NULL) 147 | { 148 | fprintf(stderr, "An error occurred while trying to create an ENet client host!\n"); 149 | return EXIT_FAILURE; 150 | } 151 | 152 | ENetAddress address; 153 | ENetEvent event; 154 | ENetPeer* peer; 155 | 156 | enet_address_set_host(&address, "127.0.0.1"); 157 | address.port = 7777; 158 | 159 | peer = enet_host_connect(client, &address, 1, 0); 160 | if(peer == NULL) 161 | { 162 | fprintf(stderr, "No available peers for initiating an ENet connection!\n"); 163 | return EXIT_FAILURE; 164 | } 165 | 166 | if(enet_host_service(client, &event, 5000) > 0 && 167 | event.type == ENET_EVENT_TYPE_CONNECT) 168 | { 169 | puts("Connection to 127.0.0.1:7777 succeeded."); 170 | } 171 | else 172 | { 173 | enet_peer_reset(peer); 174 | puts("Connection to 127.0.0.1:7777 failed."); 175 | return EXIT_SUCCESS; 176 | } 177 | 178 | // Send The Server The User's Username 179 | char str_data[80] = "2|"; 180 | strcat(str_data, username); 181 | SendPacket(peer, str_data); 182 | 183 | // Init the chatScreen 184 | chatScreen.Init(); 185 | 186 | while(true) 187 | { 188 | std::string msg = chatScreen.CheckBoxInput(); 189 | chatScreen.PostMessage(username, msg.c_str()); 190 | 191 | char message_data[80] = "1|"; 192 | strcat(message_data, msg.c_str()); 193 | SendPacket(peer, message_data); 194 | } 195 | 196 | // Disconnect peer from server 197 | enet_peer_disconnect(peer, 0); 198 | 199 | while(enet_host_service(client, &event, 3000) > 0) 200 | { 201 | switch(event.type) 202 | { 203 | case ENET_EVENT_TYPE_RECEIVE: 204 | enet_packet_destroy(event.packet); 205 | break; 206 | case ENET_EVENT_TYPE_DISCONNECT: 207 | puts("Disconnection succeeded."); 208 | break; 209 | } 210 | } 211 | 212 | return EXIT_SUCCESS; 213 | } 214 | ``` 215 | 216 | Woah woah woah! That's a big chunk of code, ain't it? Well, don't worry about it too much, most of it we've already written in the first part of this series. But we have added some new things. First, we added the chat_screen logic, we already know how that works. Second, we are using our new SendPacket function to send the username to the server.. But, something seems odd. Why are we concatenating "2|" to our username before sending it? 217 | 218 | ```cpp 219 | // Send The Server The User's Username 220 | char str_data[80] = "2|"; 221 | strcat(str_data, username); 222 | SendPacket(peer, str_data); 223 | ``` 224 | 225 | To start, we first need to understand that when we send a packet to any peer, they are unaware of what the packet is suppost to do. Like a file without an extension. We must find a way to identify the mission of the packet. Thus, 2 in this case is a identifier that will tell the server that this packet is a username. 226 | 227 | Same goes for the packets we send in our while loop. 228 | 229 | ```cpp 230 | char message_data[80] = "1|"; 231 | strcat(message_data, msg.c_str()); 232 | SendPacket(peer, message_data); 233 | ``` 234 | 235 | Although, the id is `1` in this case. 236 | 237 | > ℹ️ **Note!** 238 | > 239 | > This is a simplification of proper practices. I parse complete string to visualise the data that is being sent, obviously it would be much more optimized if we were to send raw data! 240 | i.e. reserving the first byte as the identifier whilst keeping the rest of the data the same. 241 | 242 | You'll also notice that we use `"|"` to divide the different segments of data. This is an arbitrary character chosen for this tutorial. 243 | 244 | If you are astute, you may have noticed that we aren't receiving any data. Let's fix this. 245 | 246 | > ⚠️ **Important Note!** 247 | > 248 | > The way we set up ncruses does not allow us to run anycode whilst waiting for input (which we do all the time in a loop) 249 | Our solution is multithreading. This isn't necessarily bad, but definitly more advanced that we wanted to show for this tutorial. 250 | In a normal application, you'd just have the receiving code be in the game loop along side everything else. 251 | 252 | First, we'll include `pthread.h` then at the top, under our includes, we'll add `static int CLIENT_ID = -1;` 253 | This will keep track of our client's id. 254 | Next, we'll create a void pointer `MsgLoop` that will be a function with it's own game loop in a sepereate thread. 255 | 256 | ```cpp 257 | void* MsgLoop(ENetHost* client) 258 | { 259 | while(true) 260 | { 261 | ENetEvent event; 262 | while(enet_host_service(client, &event, 0) > 0) 263 | { 264 | switch(event.type) 265 | { 266 | case ENET_EVENT_TYPE_RECEIVE: 267 | 268 | ParseData(event.packet->data); // Parse the receiving data. 269 | enet_packet_destroy(event.packet); 270 | break; 271 | } 272 | } 273 | } 274 | } 275 | ``` 276 | 277 | We have a special function here. One we are yet to create.. 278 | `ParseData` that takes the data of any received packet and, well, parses it. 279 | Let's see how that is done. 280 | 281 | > ℹ️ **Note!** 282 | > 283 | > Packets received by the client will have an extra argument. They will come with the id of the client that sent out the data. 284 | > That is to say, if we send `"2|myname"` the server will receive this and re-transmit the packet with the client's id. If my id is 5, everyone will receive `"2|5|myname"`. 285 | 286 | ```cpp 287 | void ParseData(char* data) 288 | { 289 | // Will store the data type (e.g. 1, 2, etc) 290 | int data_type; 291 | 292 | // Will store the id of the client that is sending the data 293 | int id; 294 | 295 | // Get first two numbers from the data (data_type and id) and but them in their respective variables 296 | sscanf(data,"%d|%d", &data_type, &id); 297 | 298 | // Switch between the different data_types 299 | switch(data_type) 300 | { 301 | case 1: // data is a message 302 | if(id != CLIENT_ID) 303 | { 304 | // Get message and Post it using the ClientData at id's username and the parsed msg. 305 | char msg[80]; 306 | sscanf(data, "%*d|%*d|%[^|]", &msg); 307 | chatScreen.PostMessage(client_map[id]->GetUsername().c_str(), msg); 308 | } 309 | break; 310 | case 2: // data is a username 311 | if(id != CLIENT_ID) 312 | { 313 | // Create a new ClientData with username and add it to map at id. 314 | char username[80]; 315 | sscanf(data, "%*d|%*d|%[^|]", &username); 316 | 317 | client_map[id] = new ClientData(id); 318 | client_map[id]->SetUsername(username); 319 | } 320 | break; 321 | case 3: // data is our ID. 322 | CLIENT_ID = id; // Set our id to the received id. 323 | break; 324 | } 325 | } 326 | ``` 327 | 328 | Ok, going back to the main game loop, we now need to create our thread. 329 | Before the loop starts, we'll create the thread. 330 | 331 | ```cpp 332 | // Create a thread for receiving data 333 | pthread_t thread; 334 | pthread_create(&thread, NULL, MsgLoop, client); 335 | ``` 336 | 337 | And after the game loop, we'll join in back in. 338 | 339 | ```cpp 340 | // Join thread 341 | pthread_join(thread, NULL); 342 | ``` 343 | 344 | ### ./server/main.cpp 345 | 346 | Oh, we're halfway there! 347 | Now, it's time to work on ther server. Don't worry, there's no multithreading in this part. 348 | Chunks of the code will be exactly the same as the client and server of the first part. 349 | 350 | > ℹ️ **My Different Data IDs!** 351 | > 1. Message 352 | > 2. Username 353 | > 3. Server Given ID 354 | > 4. Disconnect 355 | 356 | ```cpp 357 | #include 358 | #include 359 | 360 | #include 361 | #include 362 | #include 363 | 364 | class ClientData 365 | { 366 | private: 367 | int m_id; 368 | std::string m_username; 369 | 370 | public: 371 | ClientData(int id) : m_id(id) {} 372 | 373 | void SetUsername(std::string username){ m_username = username; } 374 | 375 | int GetID(){ return m_id; } 376 | std::string GetUsername(){ return m_username; } 377 | }; 378 | 379 | std::map client_map; 380 | 381 | void BroadcastPacket(ENetHost* server, const char* data) 382 | { 383 | ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE); 384 | enet_host_broadcast(server, 0, packet); 385 | } 386 | 387 | void SendPacket(ENetPeer* peer, const char* data) 388 | { 389 | ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE); 390 | enet_peer_send(peer, 0, packet); 391 | } 392 | 393 | void ParseData(ENetHost* server, int id, char* data) 394 | { 395 | 396 | int data_type; 397 | sscanf(data, "%d|", &data_type); 398 | 399 | switch(data_type) 400 | { 401 | case 1: 402 | { 403 | char msg[80]; 404 | sscanf(data, "%*d|%[^\n]", &msg); 405 | 406 | char send_data[1024] = {'\0'}; 407 | sprintf(send_data, "1|%d|%s", id, msg); 408 | BroadcastPacket(server, send_data); 409 | break; 410 | } 411 | case 2: 412 | { 413 | char username[80]; 414 | sscanf(data, "2|%[^\n]", &username); 415 | 416 | char send_data[1024] = {'\0'}; 417 | sprintf(send_data, "2|%d|%s", id, username); 418 | 419 | BroadcastPacket(server, send_data); 420 | client_map[id]->SetUsername(username); 421 | 422 | break; 423 | } 424 | } 425 | } 426 | 427 | int main(int argc, char ** argv) 428 | { 429 | if(enet_initialize() != 0) 430 | { 431 | fprintf (stderr, "An error occurred while initializing ENet.\n"); 432 | return EXIT_FAILURE; 433 | } 434 | atexit(enet_deinitialize); 435 | 436 | ENetAddress address; 437 | ENetHost* server; 438 | ENetEvent event; 439 | 440 | address.host = ENET_HOST_ANY; 441 | address.port = 7777; 442 | 443 | server = enet_host_create(&address, 32, 1, 0, 0); 444 | 445 | if(server == NULL) 446 | { 447 | fprintf(stderr, "An error occurred while trying to create an ENet server host!\n"); 448 | return EXIT_FAILURE; 449 | } 450 | 451 | // GAME LOOP START 452 | int new_player_id = 0; // An auto incrementing variable to give to any new clients 453 | while(true) 454 | { 455 | 456 | while(enet_host_service(server, &event, 1000) > 0) 457 | { 458 | switch(event.type) 459 | { 460 | case ENET_EVENT_TYPE_CONNECT: 461 | { 462 | printf("A new client connected from %x:%u.\n", 463 | event.peer -> address.host, 464 | event.peer -> address.port); 465 | 466 | for(auto const& x : client_map) 467 | { 468 | char send_data[1024] = {'\0'}; 469 | sprintf(send_data, "2|%d|%s", x.first, x.second->GetUsername().c_str()); 470 | BroadcastPacket(server, send_data); 471 | } 472 | 473 | // Increment new_player_id with each new connection 474 | new_player_id++; 475 | client_map[new_player_id] = new ClientData(new_player_id); 476 | event.peer->data = client_map[new_player_id]; 477 | 478 | char data_to_send[126] = {'\0'}; 479 | sprintf(data_to_send, "3|%d", new_player_id); 480 | SendPacket(event.peer, data_to_send); 481 | 482 | break; 483 | } 484 | case ENET_EVENT_TYPE_RECEIVE: 485 | { 486 | printf ("A packet of length %u containing %s was received from %x:%u on channel %u.\n", 487 | event.packet -> dataLength, 488 | event.packet -> data, 489 | event.peer -> address.host, 490 | event.peer -> address.port, 491 | event.channelID); 492 | 493 | ParseData(server, static_cast(event.peer->data)->GetID(), event.packet->data); 494 | enet_packet_destroy(event.packet); 495 | break; 496 | } 497 | case ENET_EVENT_TYPE_DISCONNECT: 498 | { 499 | printf("%x:%u disconnected.\n", 500 | event.peer -> address.host, 501 | event.peer -> address.port); 502 | 503 | char disconnected_data[126] = {'\0'}; 504 | sprintf(disconnected_data, "4|%d", static_cast(event.peer->data)->GetID()); 505 | BroadcastPacket(server, disconnected_data); 506 | 507 | event.peer->data = NULL; 508 | break; 509 | } 510 | } 511 | } 512 | 513 | } 514 | // GAME LOOP END 515 | 516 | enet_host_destroy(server); 517 | 518 | return EXIT_SUCCESS; 519 | } 520 | ``` 521 | 522 | We have a variable, `int new_player_id = 0;`, that is the ID we will send out to any new connections. 523 | 524 | `BroadcastPacket` is very similar to `SendPacket`, only difference being who the data is sent to. 525 | `SendPacket` will send the packet to a defined peer. `BroadcastPacket` will send the packet to every connect peer. 526 | 527 | ```cpp 528 | void BroadcastPacket(ENetHost* server, const char* data) 529 | { 530 | ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE); 531 | enet_host_broadcast(server, 0, packet); 532 | } 533 | ``` 534 | 535 | ## That's it! 536 | 537 | You've done great to make it this far! 538 | Hopfully we didn't jump too much, but we didn't want to repeat ourselves. 539 | Consider it an exercise! 540 | 541 | Next up, creating a multiplayer game! (putting it all together) 542 | 543 | [previous](./basic-enet-tutorial-series-part1-introduction.md) | [next](#) 544 | 545 | © Copyright 2020, Ezra Hradecky ([CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)). 546 | -------------------------------------------------------------------------------- /files/chat_screen.cpp: -------------------------------------------------------------------------------- 1 | #include "chat_screen.hpp" 2 | 3 | ChatScreen::ChatScreen(){} 4 | 5 | ChatScreen::~ChatScreen() 6 | { 7 | endwin(); 8 | delete inputwin; 9 | } 10 | 11 | void ChatScreen::Init() 12 | { 13 | // ncurses setup 14 | initscr(); 15 | 16 | // create input box 17 | int yMax, xMax; 18 | getmaxyx(stdscr, yMax, xMax); 19 | inputwin = newwin(3, xMax-12, yMax-5, 5); 20 | box(inputwin, 0, 0); 21 | refresh(); 22 | wrefresh(inputwin); 23 | } 24 | 25 | std::string ChatScreen::CheckBoxInput() 26 | { 27 | // Clear the input box 28 | wclear(inputwin); 29 | box(inputwin, 0, 0); 30 | 31 | // Await user input 32 | char msg[80]; 33 | mvwscanw(inputwin, 1, 1, "%[^\n]", msg); 34 | 35 | return msg; 36 | } 37 | 38 | void ChatScreen::PostMessage(char username[80], char msg[80]) 39 | { 40 | mvprintw(msg_y, 1, "%s: %s", username, msg); 41 | refresh(); 42 | msg_y++; 43 | } 44 | -------------------------------------------------------------------------------- /files/chat_screen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CHAT_SCREEN_HPP 2 | #define CHAT_SCREEN_HPP 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | class ChatScreen { 12 | public: 13 | ChatScreen(); 14 | ~ChatScreen(); 15 | 16 | void Init(); 17 | 18 | void PostMessage(char username[80], char msg[80]); 19 | 20 | std::string CheckBoxInput(); 21 | 22 | 23 | private: 24 | int msg_y = 0; 25 | WINDOW * inputwin = nullptr; 26 | 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /images/.comments/thumbnail.png.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with GIMP 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /images/Basic_ENet_Tutorial_Series_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usergames-net/basic-enet-tutorial-series/ae6444b7994149d474d907860b9942ac7c400e57/images/Basic_ENet_Tutorial_Series_1.webp -------------------------------------------------------------------------------- /images/Basic_ENet_Tutorial_Series_2_Part_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usergames-net/basic-enet-tutorial-series/ae6444b7994149d474d907860b9942ac7c400e57/images/Basic_ENet_Tutorial_Series_2_Part_1.jpg -------------------------------------------------------------------------------- /images/Basic_ENet_Tutorial_Series_2_Part_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usergames-net/basic-enet-tutorial-series/ae6444b7994149d474d907860b9942ac7c400e57/images/Basic_ENet_Tutorial_Series_2_Part_2.jpg -------------------------------------------------------------------------------- /images/thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usergames-net/basic-enet-tutorial-series/ae6444b7994149d474d907860b9942ac7c400e57/images/thumbnail.png --------------------------------------------------------------------------------