miku gamer
This commit is contained in:
		| @@ -10,29 +10,54 @@ | ||||
| #include "input.h" | ||||
| #include "console/console.h" | ||||
| #include "net/server/server.h" | ||||
| #include "net/client/client.h" | ||||
|  | ||||
| #include "entity/entity.h" | ||||
| int32_t status = 0; | ||||
|  | ||||
| void gameInit() { | ||||
|   consoleInit(); | ||||
|   gameTimeInit(); | ||||
|   inputInit(); | ||||
|  | ||||
|   consolePrint("Press LEFT to start server, RIGHT to join server."); | ||||
|  | ||||
|   memset(&SERVER, 0, sizeof(server_t)); | ||||
|   memset(&CLIENT, 0, sizeof(client_t)); | ||||
|  | ||||
|   // serverInit((serverinit_t){ | ||||
|   //   .type = SERVER_TYPE_ONLINE, | ||||
|   //   .online = { | ||||
|   //     .port = SERVER_PORT_DEFAULT | ||||
|   //   } | ||||
|   // }); | ||||
| } | ||||
|  | ||||
| void gameUpdate(const float delta) { | ||||
|   gameTimeUpdate(delta); | ||||
|   inputUpdate(); | ||||
|  | ||||
|   switch(status) { | ||||
|     case 0: | ||||
|       if(inputWasPressed(INPUT_LEFT)) { | ||||
|  | ||||
|         serverInit((serverinit_t){ | ||||
|           .type = SERVER_TYPE_ONLINE, | ||||
|           .online = { | ||||
|             .port = SERVER_PORT_DEFAULT | ||||
|           } | ||||
|         }); | ||||
|       } else if(inputWasPressed(INPUT_RIGHT)) { | ||||
|  | ||||
|   entityInit(&ENTITY_TEST); | ||||
|         clientInit((clientinit_t){ | ||||
|           .host = "127.0.0.1", | ||||
|           .port = SERVER_PORT_DEFAULT | ||||
|         }); | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
|  | ||||
| void gameUpdate(const float delta) { | ||||
|   gameTimeUpdate(delta); | ||||
|   inputUpdate(); | ||||
| } | ||||
|  | ||||
| void gameDispose() { | ||||
|   clientDispose(); | ||||
|   serverDispose(); | ||||
| } | ||||
| @@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME} | ||||
| ) | ||||
|  | ||||
| # Subdirs | ||||
| add_subdirectory(client) | ||||
| add_subdirectory(server) | ||||
							
								
								
									
										10
									
								
								src/dusk/net/client/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/dusk/net/client/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| # Copyright (c) 2023 Dominic Masters | ||||
| #  | ||||
| # This software is released under the MIT License. | ||||
| # https:#opensource.org/licenses/MIT | ||||
|  | ||||
| # Sources | ||||
| target_sources(${DUSK_TARGET_NAME} | ||||
|   PRIVATE | ||||
|     client.c | ||||
| ) | ||||
							
								
								
									
										119
									
								
								src/dusk/net/client/client.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/dusk/net/client/client.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #include "client.h" | ||||
| #include "console/console.h" | ||||
| #include "assert/assert.h" | ||||
|  | ||||
| client_t CLIENT; | ||||
|  | ||||
| void clientInit(const clientinit_t init) { | ||||
|   memset(&CLIENT, 0, sizeof(client_t)); | ||||
|   CLIENT.state = CLIENT_STATE_CONNECTING; | ||||
|  | ||||
|   CLIENT.socket = socket(AF_INET, SOCK_STREAM, 0); | ||||
|   CLIENT.serverAddress.sin_family = AF_INET; | ||||
|   CLIENT.serverAddress.sin_port = htons(init.port); | ||||
|  | ||||
|   if(inet_pton(AF_INET, init.host, &CLIENT.serverAddress.sin_addr) <= 0) { | ||||
|     assertUnreachable("Invalid address or address not supported."); | ||||
|   } | ||||
|  | ||||
|   if(connect(CLIENT.socket, (struct sockaddr *)&CLIENT.serverAddress, sizeof(CLIENT.serverAddress)) < 0) { | ||||
|     assertUnreachable("Connection failed."); | ||||
|   } | ||||
|  | ||||
|   pthread_create(&CLIENT.thread, NULL, clientThread, NULL); | ||||
|  | ||||
|   // Wait for client to connect | ||||
|   while(CLIENT.state == CLIENT_STATE_CONNECTING) usleep(1000); | ||||
|  | ||||
|   consolePrint("Connected to server."); | ||||
| } | ||||
|  | ||||
| void * clientThread(void* arg) { | ||||
|   assertNull(arg, "Client thread does not accept arguments."); | ||||
|  | ||||
|  | ||||
|   // Server sends 16 bytes. | ||||
|   { | ||||
|     char_t buffer[16]; | ||||
|     consolePrint("Reading first packet."); | ||||
|     if(_clientReceive((uint8_t*)buffer, sizeof(buffer)) != sizeof(buffer)) { | ||||
|       assertUnreachable("Failed to read initial server data."); | ||||
|     } | ||||
|  | ||||
|     // First 4 bytes must be "Dusk" | ||||
|     if(memcmp(buffer, "Dusk", 4) != 0) { | ||||
|       assertUnreachable("Invalid server data."); | ||||
|     } | ||||
|  | ||||
|     // Next, server will send its version number, we compare to DUSK_VERSION | ||||
|     char_t cmp[] = DUSK_VERSION; | ||||
|     if(memcmp(buffer+4, cmp, 4) != 0) { | ||||
|       assertUnreachable("Server version mismatch."); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Send "OK" and nothing else (don't send NULL term) | ||||
|   if(_clientSend((uint8_t*)"OK", 2) != 2) { | ||||
|     assertUnreachable("Failed to send OK to server."); | ||||
|   } | ||||
|  | ||||
|   // Server will send the first properly formed packet now. | ||||
|  | ||||
|   CLIENT.state = CLIENT_STATE_CONNECTED; | ||||
|   while(CLIENT.state == CLIENT_STATE_CONNECTED) { | ||||
|     usleep(1000); | ||||
|   } | ||||
|  | ||||
|   CLIENT.state = CLIENT_STATE_DISCONNECTED; | ||||
|   close(CLIENT.socket); | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| ssize_t _clientSend(const uint8_t *data, const size_t size) { | ||||
|   assertNotNull(data, "Data cannot be NULL."); | ||||
|   assertTrue(size > 0, "Size cannot be zero."); | ||||
|  | ||||
|   return send(CLIENT.socket, data, size, 0); | ||||
| } | ||||
|  | ||||
| ssize_t _clientReceive(uint8_t *data, const size_t size) { | ||||
|   assertNotNull(data, "Data cannot be NULL."); | ||||
|   assertTrue(size > 0, "Size cannot be zero."); | ||||
|  | ||||
|   fd_set readfds; | ||||
|   struct timeval timeout; | ||||
|   int32_t activity; | ||||
|  | ||||
|   FD_ZERO(&readfds); | ||||
|   FD_SET(CLIENT.socket, &readfds); | ||||
|   timeout.tv_sec = 5; // 5 seconds timeout | ||||
|   timeout.tv_usec = 0; | ||||
|  | ||||
|   activity = select(CLIENT.socket + 1, &readfds, NULL, NULL, &timeout); | ||||
|   if(activity <= 0) { | ||||
|     assertUnreachable("Timeout or error while waiting for server data."); | ||||
|   } | ||||
|  | ||||
|   return recv(CLIENT.socket, data, size, 0); | ||||
| } | ||||
|  | ||||
| void clientDispose() { | ||||
|   if(CLIENT.state == CLIENT_STATE_CONNECTED) { | ||||
|     CLIENT.state = CLIENT_STATE_DISCONNECTING; | ||||
|  | ||||
|     // Wait for client to disconnect | ||||
|     while(CLIENT.state == CLIENT_STATE_DISCONNECTING) usleep(1000); | ||||
|     pthread_join(CLIENT.thread, NULL); | ||||
|  | ||||
|     consolePrint("Disconnected from server."); | ||||
|   } | ||||
|  | ||||
|   close(CLIENT.socket); | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/dusk/net/client/client.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/dusk/net/client/client.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| /** | ||||
|  * Copyright (c) 2025 Dominic Masters | ||||
|  *  | ||||
|  * This software is released under the MIT License. | ||||
|  * https://opensource.org/licenses/MIT | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include "dusk.h" | ||||
| #include <sys/socket.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| #define CLIENT_HOST_STR_LEN 256 | ||||
|  | ||||
| typedef enum { | ||||
|   CLIENT_STATE_DISCONNECTED, | ||||
|   CLIENT_STATE_CONNECTING, | ||||
|   CLIENT_STATE_CONNECTED, | ||||
|   CLIENT_STATE_DISCONNECTING | ||||
| } clientstate_t; | ||||
|  | ||||
| typedef struct { | ||||
|   clientstate_t state; | ||||
|   int32_t socket; | ||||
|   pthread_t thread; | ||||
|   struct sockaddr_in serverAddress; | ||||
| } client_t; | ||||
|  | ||||
| typedef struct { | ||||
|   char_t host[CLIENT_HOST_STR_LEN+1]; | ||||
|   uint32_t port; | ||||
| } clientinit_t; | ||||
|  | ||||
| extern client_t CLIENT; | ||||
|  | ||||
| void clientInit(const clientinit_t init); | ||||
| void clientDispose(); | ||||
| void * clientThread(void* arg); | ||||
|  | ||||
| ssize_t _clientSend(const uint8_t *data, const size_t size); | ||||
| ssize_t _clientReceive(uint8_t *data, const size_t size); | ||||
| @@ -9,8 +9,7 @@ | ||||
| #include "dusk.h" | ||||
|  | ||||
| typedef enum { | ||||
|   PACKET_TYPE_HELLO, | ||||
|   PACKET_TYPE_PING | ||||
|   PACKET_TYPE_CONNECTED | ||||
| } packettype_t; | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
| @@ -54,6 +54,8 @@ void serverInit(const serverinit_t init) { | ||||
|  | ||||
|   // Wait for server to start | ||||
|   while(SERVER.state == SERVER_STATE_STARTING) usleep(1000); | ||||
|  | ||||
|   consolePrint("Server started"); | ||||
| } | ||||
|  | ||||
| void * serverThread(void *arg) { | ||||
| @@ -141,5 +143,7 @@ void serverDispose() { | ||||
|         assertUnreachable("Invalid server type."); | ||||
|         break; | ||||
|     } | ||||
|      | ||||
|     consolePrint("Server stopped"); | ||||
|   } | ||||
| } | ||||
| @@ -31,30 +31,41 @@ void * serverClientThread(void *arg) { | ||||
|   serverclient_t *client = (serverclient_t*)arg; | ||||
|   assertNotNull(client, "Client cannot be NULL."); | ||||
|  | ||||
|   // Set the client state to connected | ||||
|   client->state = SERVER_CLIENT_STATE_CONNECTED; | ||||
|  | ||||
|   // Send hello to client, as well as the version number. | ||||
|   { | ||||
|     uint8_t hello[16]; | ||||
|     memset(hello, 0, sizeof(hello)); | ||||
|     memcpy(hello, "Dusk", 4); | ||||
|     strcpy((char*)&hello[4], DUSK_VERSION); | ||||
|      | ||||
|     _serverClientSend(client, hello, sizeof(hello)); | ||||
|     ssize_t bytesSent = serverClientSend(client, hello, sizeof(hello)); | ||||
|     assertTrue( | ||||
|       bytesSent == sizeof(hello), | ||||
|       "Failed to send hello to client." | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   // Client should respond "OK" | ||||
|   { | ||||
|     uint8_t response[2]; | ||||
|     _serverClientReceive(client, response, sizeof(response)); | ||||
|     ssize_t bytesReceived = serverClientReceive(client, response, sizeof(response)); | ||||
|     assertTrue( | ||||
|       bytesReceived == sizeof(response) && | ||||
|       response[0] == 'O' && | ||||
|       response[1] == 'K', | ||||
|       "Client did not respond with OK." | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   // At this point we can start communicating "properly", so let's send the  | ||||
|   // first real packet. | ||||
|   packet_t packet = { .type = PACKET_TYPE_CONNECTED }; | ||||
|   if(!serverClientSendPacket(client, &packet)) { | ||||
|     assertUnreachable("Failed to send connected packet to client."); | ||||
|   } | ||||
|  | ||||
|   // Set the client state to connected | ||||
|   client->state = SERVER_CLIENT_STATE_CONNECTED; | ||||
|    | ||||
|   while(client->state == SERVER_CLIENT_STATE_CONNECTED) { | ||||
|     // Do nothing for now. | ||||
|     usleep(1000); | ||||
| @@ -65,7 +76,7 @@ void * serverClientThread(void *arg) { | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| void _serverClientSend( | ||||
| ssize_t serverClientSend( | ||||
|   serverclient_t *client, | ||||
|   const uint8_t *data, | ||||
|   const size_t size | ||||
| @@ -73,9 +84,11 @@ void _serverClientSend( | ||||
|   assertNotNull(client, "Client cannot be NULL."); | ||||
|   assertNotNull(data, "Data cannot be NULL."); | ||||
|   assertTrue(size > 0, "Size cannot be zero."); | ||||
|  | ||||
|   return send(client->socket, data, size, 0); | ||||
| } | ||||
|  | ||||
| void _serverClientReceive( | ||||
| ssize_t serverClientReceive( | ||||
|   serverclient_t *client, | ||||
|   uint8_t *data, | ||||
|   const size_t size | ||||
| @@ -84,7 +97,32 @@ void _serverClientReceive( | ||||
|   assertNotNull(data, "Data cannot be NULL."); | ||||
|   assertTrue(size > 0, "Size cannot be zero."); | ||||
|  | ||||
|   recv(client->socket, data, size, 0); | ||||
|   fd_set readfds; | ||||
|   struct timeval timeout; | ||||
|   int32_t activity; | ||||
|  | ||||
|   FD_ZERO(&readfds); | ||||
|   FD_SET(client->socket, &readfds); | ||||
|   timeout.tv_sec = 5; // 5 seconds timeout | ||||
|   timeout.tv_usec = 0; | ||||
|  | ||||
|   activity = select(client->socket + 1, &readfds, NULL, NULL, &timeout); | ||||
|   if(activity <= 0) { | ||||
|     assertUnreachable("Timeout or error while waiting for client data."); | ||||
|   } | ||||
|  | ||||
|   return recv(client->socket, data, size, 0); | ||||
| } | ||||
|  | ||||
| bool_t serverClientSendPacket( | ||||
|   serverclient_t *client, | ||||
|   const packet_t *packet | ||||
| ) { | ||||
|   assertNotNull(client, "Client cannot be NULL."); | ||||
|   assertNotNull(packet, "Packet cannot be NULL."); | ||||
|  | ||||
|   ssize_t sent = serverClientSend(client, (const uint8_t*)packet, sizeof(packet_t)); | ||||
|   return sent == sizeof(packet_t); | ||||
| } | ||||
|  | ||||
| void serverClientDispose(serverclient_t *client) { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
|  | ||||
| #pragma once | ||||
| #include "dusk.h" | ||||
| #include "net/packet.h" | ||||
| #include <netinet/in.h> | ||||
| #include <fcntl.h> | ||||
|  | ||||
| @@ -30,16 +31,21 @@ typedef struct { | ||||
| void serverClientInit(serverclient_t *client, const serverclientinit_t init); | ||||
| void * serverClientThread(void *arg); | ||||
|  | ||||
| void _serverClientSend( | ||||
| ssize_t serverClientSend( | ||||
|   serverclient_t *client, | ||||
|   const uint8_t *data, | ||||
|   const size_t size | ||||
| ); | ||||
|  | ||||
| void _serverClientReceive( | ||||
| ssize_t serverClientReceive( | ||||
|   serverclient_t *client, | ||||
|   uint8_t *buffer, | ||||
|   const size_t size | ||||
| ); | ||||
|  | ||||
| bool_t serverClientSendPacket( | ||||
|   serverclient_t *client, | ||||
|   const packet_t *packet | ||||
| ); | ||||
|  | ||||
| void serverClientDispose(serverclient_t *client); | ||||
		Reference in New Issue
	
	Block a user