Add CLI client

master
Elias Fleckenstein 2021-03-23 15:53:17 +01:00
parent 539323eef3
commit c63f419e35
19 changed files with 419 additions and 161 deletions

View File

@ -1,11 +1,11 @@
COMMON = array.o binsearch.o linkedlist.o map.o util.o types.o
SERVER = $(COMMON) server.o server_command_handlers.o
CLIENT = $(COMMON) client.o
COMMON = array.o binsearch.o linkedlist.o map.o signal.o util.o types.o
SERVER = $(COMMON) server.o servercommands.o
CLIENT = $(COMMON) client.o clientcommands.o
all: Dragonblocks DragonblocksServer
Dragonblocks: $(CLIENT)
cc -g -o Dragonblocks $(CLIENT)
cc -g -o Dragonblocks $(CLIENT) -pthread
DragonblocksServer: $(SERVER)
cc -g -o DragonblocksServer $(SERVER) -pthread

170
client.c
View File

@ -1,17 +1,160 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "map.h"
#include <errno.h>
#include <signal.h>
#include <sched.h>
#include "client.h"
#include "signal.h"
#include "util.h"
#include "network.c"
void client_disconnect(Client *client, bool send, const char *detail)
{
if (send)
send_command(client, SC_DISCONNECT);
pthread_mutex_lock(&client->mtx);
client->state = CS_DISCONNECTED;
close(client->fd);
pthread_mutex_unlock(&client->mtx);
printf("Disconnected %s%s%s\n", INBRACES(detail));
}
static void *reciever_thread(void *cliptr)
{
Client *client = cliptr;
handle_packets(client);
if (client->state != CS_DISCONNECTED) {
if (errno == EINTR)
client_disconnect(client, true, NULL);
else
client_disconnect(client, false, "network error");
}
if (client->name)
free(client->name);
pthread_mutex_destroy(&client->mtx);
exit(EXIT_SUCCESS);
return NULL;
}
static void client_loop(Client *client)
{
while (client->state != CS_DISCONNECTED) {
if (client->state == CS_CREATED) {
printf("Enter name: ");
fflush(stdout);
char name[NAME_MAX];
if (scanf("%s", name) == EOF)
return;
client->name = strdup(name);
pthread_mutex_lock(&client->mtx);
if (write_u32(client->fd, SC_AUTH) && write(client->fd, client->name, strlen(name) + 1)) {
client->state = CS_AUTH;
printf("Authenticating...\n");
}
pthread_mutex_unlock(&client->mtx);
} else if (client->state == CS_ACTIVE) {
printf("%s: ", client->name);
fflush(stdout);
char buffer[BUFSIZ] = {0};
if (scanf("%s", buffer) == EOF)
return;
if (strcmp(buffer, "disconnect") == 0) {
return;
} else if (strcmp(buffer, "setnode") == 0) {
v3s32 pos;
char node[BUFSIZ] = {0};
if (scanf("%d %d %d %s", &pos.x, &pos.y, &pos.z, node) == EOF)
return;
Node node_type = NODE_INVALID;
if (strcmp(node, "air") == 0)
node_type = NODE_AIR;
else if (strcmp(node, "grass") == 0)
node_type = NODE_GRASS;
else if (strcmp(node, "dirt") == 0)
node_type = NODE_DIRT;
else if (strcmp(node, "stone") == 0)
node_type = NODE_STONE;
if (node_type == NODE_INVALID) {
printf("Invalid node\n");
} else {
pthread_mutex_lock(&client->mtx);
if (write_u32(client->fd, SC_SETNODE) && write_v3s32(client->fd, pos))
write_u32(client->fd, node_type);
pthread_mutex_unlock(&client->mtx);
}
} else if (strcmp(buffer, "getnode") == 0) {
v3s32 pos;
if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
return;
pthread_mutex_lock(&client->mtx);
if (write_u32(client->fd, SC_GETBLOCK))
write_v3s32(client->fd, (v3s32) {pos.x / 16, pos.y / 16, pos.z / 16});
pthread_mutex_unlock(&client->mtx);
} else if (strcmp(buffer, "printnode") == 0) {
v3s32 pos;
if (scanf("%d %d %d", &pos.x, &pos.y, &pos.z) == EOF)
return;
MapNode node = map_get_node(client->map, pos);
const char *nodename;
switch (node.type) {
case NODE_UNLOADED:
nodename = "unloaded";
break;
case NODE_AIR:
nodename = "air";
break;
case NODE_GRASS:
nodename = "grass";
break;
case NODE_DIRT:
nodename = "dirt";
break;
case NODE_STONE:
nodename = "stone";
break;
case NODE_INVALID:
nodename = "invalid";
break;
}
printf("%s\n", nodename);
} else {
printf("Invalid command: %s\n", buffer);
}
} else {
sched_yield();
}
}
}
int main(int argc, char **argv)
{
program_name = argv[0];
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
Client client = {
.fd = -1,
.map = NULL,
.name = NULL,
.state = CS_CREATED,
};
if (sockfd == -1)
pthread_mutex_init(&client.mtx, NULL);
client.fd = socket(AF_INET, SOCK_STREAM, 0);
if (client.fd == -1)
syscall_error("socket");
if (argc <= 1)
@ -28,21 +171,20 @@ int main(int argc, char **argv)
.sin_addr = addr_buf,
};
if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
if (connect(client.fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
syscall_error("connect");
Map *map = map_create(NULL);
init_signal_handlers();
MapBlock *block = map_deserialize_block(sockfd);
if (block)
map_create_block(map, (v3s32) {0, 0, 0}, block);
else
internal_error("invalid block recieved");
client.map = map_create();
MapNode node = map_get_node(map, (v3s32) {0, 0, 0});
printf("%d\n", node.type);
pthread_t recv_thread;
pthread_create(&recv_thread, NULL, &reciever_thread, &client);
close(sockfd);
client_loop(&client);
map_delete(map);
if (client.state != CS_DISCONNECTED)
client_disconnect(&client, true, NULL);
pthread_join(recv_thread, NULL);
}

22
client.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include <stdbool.h>
#include <pthread.h>
#include "servercommands.h"
#include "clientcommands.h"
#include "network.h"
#include "map.h"
typedef struct Client
{
int fd;
char *name;
Map *map;
ClientState state;
pthread_mutex_t mtx;
} Client;
void client_disconnect(Client *client, bool send, const char *detail);
#endif

View File

@ -1,4 +0,0 @@
#! /bin/bash
# AUTH Name SETNODE x = 0 y = 0 z = 0 DIRT GETBLOCK x = 0 y = 0 z = 0 DISCONNECT
echo -ne "\0\0\0\x02Fleckenstein\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01" | nc localhost 4000 | hexdump

View File

@ -1,12 +0,0 @@
#ifndef _CLIENT_COMMAND_H_
#define _CLIENT_COMMAND_H_
typedef enum
{
CC_DISCONNECT,
CC_AUTH_SUCCESS,
CC_AUTH_FAILURE,
CC_BLOCK,
} ClientCommand;
#endif

44
clientcommands.c Normal file
View File

@ -0,0 +1,44 @@
#include <stdio.h>
#include "client.h"
#include "types.h"
static bool disconnect_handler(Client *client, bool good)
{
if (good)
client_disconnect(client, false, NULL);
return true;
}
static bool auth_handler(Client *client, bool good)
{
u8 success;
if (! read_u8(client->fd, &success))
return false;
if (! good)
return true;
if (success) {
printf("Authenticated successfully\n");
client->state = CS_ACTIVE;
} else {
printf("Authentication failed, please try again\n");
client->state = CS_CREATED;
}
return true;
}
static bool block_handler(Client *client, bool good)
{
if (good)
return map_deserialize_block(client->fd, client->map);
return true;
}
CommandHandler command_handlers[CLIENT_COMMAND_COUNT] = {
{0},
{&disconnect_handler, "DISCONNECT", CS_CREATED | CS_AUTH | CS_ACTIVE},
{&auth_handler, "AUTH", CS_AUTH},
{&block_handler, "BLOCK", CS_ACTIVE},
};

22
clientcommands.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _CLIENT_COMMAND_H_
#define _CLIENT_COMMAND_H_
typedef enum
{
CLIENT_COMMAND_NULL,
CC_DISCONNECT,
CC_AUTH,
CC_BLOCK,
CLIENT_COMMAND_COUNT
} ClientCommand;
#ifdef _SERVER_H_
typedef ClientCommand RemoteCommand;
#endif
#ifdef _CLIENT_H_
typedef ClientCommand HostCommand;
#define HOST_COMMAND_COUNT CLIENT_COMMAND_COUNT
#endif
#endif

40
map.c
View File

@ -68,20 +68,6 @@ MapBlock *map_get_block(Map *map, v3s32 pos, bool create)
return block;
}
void map_create_block(Map *map, v3s32 pos, MapBlock *block)
{
block->pos = pos;
MapSector *sector = map_get_sector(map, (v2s32) {pos.x, pos.z}, true);
BinsearchResult res = binsearch(&pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
if (res.success) {
raw_delete_block(sector->blocks.ptr[res.index]);
sector->blocks.ptr[res.index] = block;
} else {
array_insert(&sector->blocks, block, res.index);
}
}
bool map_deserialize_node(int fd, MapNode *node)
{
Node type;
@ -99,6 +85,9 @@ bool map_deserialize_node(int fd, MapNode *node)
bool map_serialize_block(int fd, MapBlock *block)
{
if (! write_v3s32(fd, block->pos))
return false;
ITERATE_MAPBLOCK {
if (! write_u32(fd, block->data[x][y][z].type))
return false;
@ -107,16 +96,30 @@ bool map_serialize_block(int fd, MapBlock *block)
return true;
}
MapBlock *map_deserialize_block(int fd)
bool map_deserialize_block(int fd, Map *map)
{
MapBlock *block = malloc(sizeof(MapBlock));
if (! read_v3s32(fd, &block->pos))
return false;
ITERATE_MAPBLOCK {
if (! map_deserialize_node(fd, &block->data[x][y][z])) {
free(block);
return NULL;
return false;
}
}
return block;
MapSector *sector = map_get_sector(map, (v2s32) {block->pos.x, block->pos.z}, true);
BinsearchResult res = binsearch(&block->pos.y, sector->blocks.ptr, sector->blocks.siz, &block_compare);
if (res.success) {
raw_delete_block(sector->blocks.ptr[res.index]);
sector->blocks.ptr[res.index] = block;
} else {
array_insert(&sector->blocks, block, res.index);
}
return true;
}
MapNode map_get_node(Map *map, v3s32 pos)
@ -145,10 +148,9 @@ void map_node_clear(MapNode *node)
linked_list_clear(&node->meta);
}
Map *map_create(FILE *file)
Map *map_create()
{
Map *map = malloc(sizeof(Map));
map->file = file;
map->sectors = array_create();
return map;

9
map.h
View File

@ -1,7 +1,6 @@
#ifndef _MAP_H_
#define _MAP_H_
#include <stdio.h>
#include <stdbool.h>
#include "array.h"
#include "linkedlist.h"
@ -32,16 +31,14 @@ typedef struct
typedef struct
{
Array sectors;
FILE *file;
} Map;
MapSector *map_get_sector(Map *map, v2s32 pos, bool create);
MapBlock *map_get_block(Map *map, v3s32 pos, bool create);
void map_create_block(Map *map, v3s32 pos, MapBlock *block);
bool map_deserialize_node(int fd, MapNode *buf);
bool map_serialize_block(int fd, MapBlock *);
MapBlock *map_deserialize_block(int fd);
bool map_serialize_block(int fd, MapBlock *block);
bool map_deserialize_block(int fd, Map *map);
void map_delete_block(MapBlock *); // ToDo
void map_unload_block(MapBlock *); // ToDo
@ -51,7 +48,7 @@ void map_set_node(Map *map, v3s32 pos, MapNode node);
MapNode map_node_create(Node type);
void map_node_clear(MapNode *node);
Map *map_create(FILE *file);
Map *map_create();
void map_delete(Map *map);
#endif

30
network.c Normal file
View File

@ -0,0 +1,30 @@
bool send_command(Client *client, RemoteCommand cmd)
{
pthread_mutex_lock(&client->mtx);
bool ret = write_u32(client->fd, cmd);
pthread_mutex_unlock(&client->mtx);
return ret;
}
static void handle_packets(Client *client) {
while (client->state != CS_DISCONNECTED) {
HostCommand command;
if (! read_u32(client->fd, &command))
break;
CommandHandler *handler = NULL;
if (command < HOST_COMMAND_COUNT)
handler = &command_handlers[command];
if (handler && handler->func) {
bool good = client->state & handler->state_flags;
if (! good)
printf("Recieved %s command, but client is in invalid state: %d\n", handler->name, client->state);
if (! handler->func(client, good))
break;
} else {
printf("Recieved invalid command %d (max = %d)\n", command, HOST_COMMAND_COUNT);
}
}
}

27
network.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef _NETWORK_H_
#define _NETWORK_H_
#include <stdbool.h>
#define NAME_MAX 64
typedef enum
{
CS_CREATED = 0x01,
CS_AUTH = 0x02,
CS_ACTIVE = 0x04,
CS_DISCONNECTED = 0x08,
} ClientState;
struct Client;
typedef struct {
bool (*func)(struct Client *client, bool good);
const char *name;
int state_flags;
} CommandHandler;
extern CommandHandler command_handlers[];
bool send_command(struct Client *client, RemoteCommand cmd);
#endif

View File

@ -1,29 +1,19 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <signal.h>
#include <errno.h>
#include <endian.h>
#include <pthread.h>
#include "linkedlist.h"
#include "map.h"
#include "server.h"
#include "server_commands.h"
#include "server_command_handlers.h"
#include "servercommands.h"
#include "signal.h"
#include "util.h"
#define ever (;;)
bool server_send_command(Client *client, ClientCommand command)
{
pthread_mutex_lock(&client->mtx);
bool ret = write_s32(client->fd, command);
pthread_mutex_unlock(&client->mtx);
return ret;
}
#include "network.c"
char *server_get_client_name(Client *client)
{
@ -36,16 +26,16 @@ void server_disconnect_client(Client *client, int flags, const char *detail)
linked_list_delete(&client->server->clients, client->name);
if (! (flags & DISCO_NO_MESSAGE))
printf("Disconnected %s %s%s%s\n", server_get_client_name(client), detail ? "(" : "", detail ? detail : "", detail ? ")" : "");
printf("Disconnected %s %s%s%s\n", server_get_client_name(client), INBRACES(detail));
if (! (flags & DISCO_NO_SEND))
server_send_command(client, CC_DISCONNECT);
send_command(client, CC_DISCONNECT);
client->state = CS_DISCONNECTED;
pthread_mutex_lock(&client->mtx);
close(client->fd);
pthread_mutex_unlock(&client->mtx);
client->state = CS_DISCONNECTED;
}
void server_shutdown(Server *srv)
@ -63,35 +53,14 @@ void server_shutdown(Server *srv)
exit(EXIT_SUCCESS);
}
static void *client_handler_thread(void *clientptr)
static void *reciever_thread(void *clientptr)
{
Client *client = clientptr;
while (client->state != CS_DISCONNECTED) {
ServerCommand command;
if (! read_u32(client->fd, &command))
break;
ServerCommandHandler *handler = NULL;
if (command < SERVER_COMMAND_COUNT)
handler = &server_command_handlers[command];
if (handler && handler->func) {
if (client->state & handler->state_flags) {
if (! handler->func(client))
break;
} else {
printf("Recieved %s command from client %s, but client is in invalid state: %d\n", handler->name, server_get_client_name(client), client->state);
}
} else {
printf("Recieved invalid command %d from client %s\n", command, server_get_client_name(client));
}
}
handle_packets(client);
if (client->state != CS_DISCONNECTED)
server_disconnect_client(client, DISCO_NO_SEND, "connection error");
server_disconnect_client(client, DISCO_NO_SEND, "network error");
if (client->name)
free(client->name);
@ -125,17 +94,7 @@ static void accept_client(Server *srv)
pthread_mutex_init(&client->mtx, NULL);
pthread_t thread;
pthread_create(&thread, NULL, &client_handler_thread, client);
}
static void interrupt_handler(int sig)
{
fprintf(stderr, "%s\n", strsignal(sig));
}
static void silent_handler(int sig)
{
(void) sig;
pthread_create(&thread, NULL, &reciever_thread, client);
}
int main(int argc, char **argv)
@ -170,14 +129,7 @@ int main(int argc, char **argv)
if (listen(server.sockfd, 3) == -1)
syscall_error("listen");
struct sigaction sigact_interrupt = {0};
sigact_interrupt.sa_handler = &interrupt_handler;
sigaction(SIGINT, &sigact_interrupt, NULL);
sigaction(SIGTERM, &sigact_interrupt, NULL);
struct sigaction sigact_silent = {0};
sigact_silent.sa_handler = &silent_handler;
sigaction(SIGPIPE, &sigact_silent, NULL);
init_signal_handlers();
server.map = map_create(NULL);

View File

@ -2,9 +2,11 @@
#define _SERVER_H_
#include <pthread.h>
#include "client_commands.h"
#include "clientcommands.h"
#include "servercommands.h"
#include "linkedlist.h"
#include "map.h"
#include "network.h"
typedef struct
{
@ -13,19 +15,12 @@ typedef struct
LinkedList clients;
} Server;
typedef enum
{
CS_CREATED = 0x01,
CS_ACTIVE = 0x02,
CS_DISCONNECTED = 0x04,
} ClientState;
typedef struct
typedef struct Client
{
int fd;
char *name;
Server *server;
ClientState state;
int fd;
pthread_mutex_t mtx;
} Client;
@ -36,7 +31,6 @@ typedef enum
DISCO_NO_MESSAGE = 0x04,
} DiscoFlag;
bool server_send_command(Client *client, ClientCommand command);
char *server_get_client_name(Client *client);
void server_disconnect_client(Client *client, int flags, const char *detail);
void server_shutdown(Server *srv);

View File

@ -1,14 +0,0 @@
#ifndef _SERVER_COMMAND_HANDLERS_H_
#define _SERVER_COMMAND_HANDLERS_H_
#include "server.h"
typedef struct {
bool (*func)(Client *client);
const char *name;
int state_flags;
} ServerCommandHandler;
extern ServerCommandHandler server_command_handlers[];
#endif

View File

@ -1,44 +1,57 @@
#include <stdio.h>
#include "util.h"
#include <stdlib.h>
#include "server.h"
#include "server_commands.h"
#include "server_command_handlers.h"
#include "util.h"
static bool disconnect_handler(Client *client)
static bool disconnect_handler(Client *client, bool good)
{
server_disconnect_client(client, 0, NULL);
if (good)
server_disconnect_client(client, DISCO_NO_SEND, NULL);
return true;
}
static bool auth_handler(Client *client)
static bool auth_handler(Client *client, bool good)
{
char *name = read_string(client->fd, NAME_MAX);
if (! name)
return false;
if (! good) {
free(name);
return true;
}
u8 success;
if (linked_list_put(&client->server->clients, name, client)) {
client->name = name;
client->state = CS_ACTIVE;
printf("Auth success: %s\n", server_get_client_name(client));
if (! server_send_command(client, CC_AUTH_SUCCESS))
return false;
success = 1;
} else {
printf("Auth failure: %s\n", server_get_client_name(client));
if (! server_send_command(client, CC_AUTH_FAILURE))
return false;
free(name);
success = 0;
}
return true;
pthread_mutex_lock(&client->mtx);
bool ret = write_u32(client->fd, CC_AUTH) && write_u8(client->fd, success);
pthread_mutex_unlock(&client->mtx);
return ret;
}
static bool getblock_handler(Client *client)
static bool getblock_handler(Client *client, bool good)
{
v3s32 pos;
if (! read_v3s32(client->fd, &pos))
return false;
if (! good)
return true;
MapBlock *block = map_get_block(client->server->map, pos, false);
if (block) {
pthread_mutex_lock(&client->mtx);
@ -51,7 +64,7 @@ static bool getblock_handler(Client *client)
return true;
}
static bool setnode_handler(Client *client)
static bool setnode_handler(Client *client, bool good)
{
v3s32 pos;
@ -63,12 +76,13 @@ static bool setnode_handler(Client *client)
if (! map_deserialize_node(client->fd, &node))
return false;
map_set_node(client->server->map, pos, node);
if (good)
map_set_node(client->server->map, pos, node);
return true;
}
ServerCommandHandler server_command_handlers[SERVER_COMMAND_COUNT] = {
CommandHandler command_handlers[SERVER_COMMAND_COUNT] = {
{0},
{&disconnect_handler, "DISCONNECT", CS_CREATED | CS_ACTIVE},
{&auth_handler, "AUTH", CS_CREATED},

View File

@ -1,8 +1,6 @@
#ifndef _SERVER_COMMAND_H_
#define _SERVER_COMMAND_H_
#define NAME_MAX 64
typedef enum
{
SERVER_COMMAND_NULL,
@ -13,4 +11,13 @@ typedef enum
SERVER_COMMAND_COUNT,
} ServerCommand;
#ifdef _CLIENT_H_
typedef ServerCommand RemoteCommand;
#endif
#ifdef _SERVER_H_
typedef ServerCommand HostCommand;
#define HOST_COMMAND_COUNT SERVER_COMMAND_COUNT
#endif
#endif

26
signal.c Normal file
View File

@ -0,0 +1,26 @@
#include <signal.h>
#include <stdio.h>
#include <string.h>
static void interrupt_handler(int sig)
{
fprintf(stderr, "%s\n", strsignal(sig));
}
static void silent_handler(int sig)
{
(void) sig;
}
static struct sigaction sigact_interrupt = {0};
static struct sigaction sigact_silent = {0};
void init_signal_handlers()
{
sigact_interrupt.sa_handler = &interrupt_handler;
sigaction(SIGINT, &sigact_interrupt, NULL);
sigaction(SIGTERM, &sigact_interrupt, NULL);
sigact_silent.sa_handler = &silent_handler;
sigaction(SIGPIPE, &sigact_silent, NULL);
}

6
signal.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _SIGNAL_H_
#define _SIGNAL_H_
void init_signal_handlers();
#endif

3
util.h
View File

@ -3,6 +3,9 @@
#include "types.h"
#define ever (;;)
#define INBRACES(str) str ? "(" : "", str ? str : "", str ? ")" : ""
extern const char *program_name;
void syscall_error(const char *err);