aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-01-20 23:47:53 -0500
committerCara Salter <cara@devcara.com>2022-01-20 23:47:53 -0500
commitc2352ee043546b518f69c615a1ece778e942e650 (patch)
tree6efe18199f0fe2810c879c9251db2897a3244604
downloadcmud-c2352ee043546b518f69c615a1ece778e942e650.tar.gz
cmud-c2352ee043546b518f69c615a1ece778e942e650.zip
initial commit
yay start of a state machine and also learning sockets
-rw-r--r--.gitignore111
-rw-r--r--Makefile22
-rw-r--r--src/data.c60
-rw-r--r--src/data.h9
-rw-r--r--src/login.c68
-rw-r--r--src/login.h32
-rw-r--r--src/main.c32
-rw-r--r--src/server.c84
-rw-r--r--src/server.h17
9 files changed, 435 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a426513
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,111 @@
+
+# Created by https://www.toptal.com/developers/gitignore/api/c,emacs
+# Edit at https://www.toptal.com/developers/gitignore?templates=c,emacs
+
+### C ###
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+### Emacs ###
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+# network security
+/network-security.data
+
+
+# End of https://www.toptal.com/developers/gitignore/api/c,emacs
+cmud*
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1b79565
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,22 @@
+CC=gcc
+CFLAGS=-I. -static
+SRCS=src/main.c src/login.c src/server.c src/data.c
+BIN=cmud
+
+%.o: %.c
+ $(CC) -c -o $@ $< $(CFLAGS)
+
+dev: $(SRCS)
+ $(CC) -o $(BIN)-devel $(SRCS) $(LFLAGS) -g
+
+main: $(SRCS)
+ $(CC) -o $(BIN) $(SRCS) $(LFLAGS) $(CFLAGS)
+
+dist: main
+ tar czvf $(BIN).tar.gz $(BIN)
+
+clean:
+ rm $(BIN)*
+
+run: dev
+ ./cmud-devel
diff --git a/src/data.c b/src/data.c
new file mode 100644
index 0000000..15617e0
--- /dev/null
+++ b/src/data.c
@@ -0,0 +1,60 @@
+#include <asm-generic/errno-base.h>
+#define _GNU_SOURCE
+#include "data.h"
+#include "login.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+int try_make_data_dirs() {
+ int err = 0;
+ err = mkdir("data", S_IRWXU);
+ err = mkdir("data/players", S_IRWXU);
+
+ return err;
+}
+
+int try_load_plr(char *player_name, playerc_t *plr) {
+ int err = 0;
+ err = try_make_data_dirs();
+ if (err) {
+ switch (err) {
+ // Now the errors that continue will be from I/O, which we can use to figure out if the file exists in login
+ case EACCES: /* No permissions***************************************/
+ printf("No permissions to create directories\n");
+ return -1;
+ default:/* Do nothing because this is fine****************************/
+ break;
+ }
+ }
+
+ char *fname;
+ asprintf(&fname, "data/players/%s.plr", player_name);
+
+ FILE *fp = fopen(fname, "rb");
+
+ fread(&plr, sizeof(playerc_t), 1, fp);
+
+ return err;
+}
+
+int try_write_file(player_t *plr) {
+ int err = 0;
+
+ err = try_make_data_dirs();
+ if (err) {
+ perror("creating data directories");
+ printf("Could not create data/\n");
+ return err;
+ }
+
+ char *fname;
+ asprintf(&fname, "data/players/%s.plr", *plr->name);
+
+ FILE *fp = fopen(fname, "wb");
+
+ fwrite(&plr, sizeof(player_t), 1, fp);
+
+ return err;
+}
diff --git a/src/data.h b/src/data.h
new file mode 100644
index 0000000..a030d50
--- /dev/null
+++ b/src/data.h
@@ -0,0 +1,9 @@
+#ifndef DATA_H
+#define DATA_H
+
+#include "login.h"
+int try_load_file(char *player_name);
+
+int try_write_file(player_t *plr);
+
+#endif
diff --git a/src/login.c b/src/login.c
new file mode 100644
index 0000000..c6ab408
--- /dev/null
+++ b/src/login.c
@@ -0,0 +1,68 @@
+#include <string.h>
+#define _GNU_SOURCE
+#include "data.h"
+#include "login.h"
+#include "server.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+void handle_player(int conn_fd) {
+ playerc_t plrc = {0};
+ plrc.state = LoggedOut;
+ plrc.conn = conn_fd;
+ player_t plr = {0};
+ plrc.plr = plr;
+
+ int err = step_login(&plrc, conn_fd);
+ if (err) {
+ err = send_to_fd(conn_fd, "Error in login process, exiting");
+ if (err) {
+ printf("Could not send error message");
+ }
+ close(conn_fd);
+ return;
+ }
+
+ close(conn_fd);
+}
+
+int step_login(playerc_t *player, int conn_fd) {
+ int err = 0;
+
+ for (;;) {
+ switch (player->state) {
+ case LoggedOut: /* the player has yet to log in, this is a fresh connection!
+ */
+ send_to_fd(player->conn,
+ "Welcome! Please enter your **username** below\n");
+ char *buf = (char *)malloc(1 << 10);
+ recv(player->conn, buf, sizeof(buf), 0);
+ err = try_load_file(buf);
+ if (err) {
+ // File doesn't exist, let's create a player!
+ strcpy(player->plr.name, buf);
+ send_to_fd(player->conn, "Howdy! Want to introduce yourself? [y|n]\n");
+ recv(player->conn, buf, sizeof(buf), 0);
+ if (buf[0] == 'y') {
+ player->state = WantMakeAccount;
+ } else if (buf[0] == 'n') {
+ send_to_fd(player->conn,
+ "Awww :(. Oh well, hope to see you again soon!\n");
+ return 4;
+ }
+ continue;
+ } else {
+ player->state = EnterPassword;
+ continue;
+ }
+ case WantMakeAccount:
+ asprintf(&buf,
+ "Welcome aboard %s! Why don't you start us off by giving me a "
+ "password.\n", ) send_to_fd(player->conn, "Welcome aboard! ")
+ }
+ }
+
+ return err;
+}
diff --git a/src/login.h b/src/login.h
new file mode 100644
index 0000000..6d4cc3d
--- /dev/null
+++ b/src/login.h
@@ -0,0 +1,32 @@
+#ifndef LOGIN_H
+#define LOGIN_H
+enum login_state {
+ LoggedOut,
+ EnterPassword,
+ WantMakeAccount,
+ MakePassword,
+ MakeConfirmPassword,
+ Complete
+};
+
+typedef struct player {
+ int xp;
+ int hp;
+ int max_hp;
+ int level;
+ int location_id;
+
+ char *name[];
+} player_t;
+
+typedef struct player_conn {
+ int conn;
+ enum login_state state;
+ struct player plr;
+} playerc_t;
+
+int step_login(playerc_t *player, int conn_fd);
+
+void handle_player(int conn_fd);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..4f848ba
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,32 @@
+/**
+ * Creates a socket for the server
+ * */
+#include "server.h"
+#include <stdio.h>
+
+/**
+ * Server entrypoint
+ *
+ * sets up everything needed to handle sockets and connections
+ * */
+int main() {
+ int err = 0;
+ server_t server = {0};
+
+ err = server_listen(&server);
+
+ if (err) {
+ printf("Failed to listen on address");
+ return err;
+ }
+
+ for (;;) {
+ err = server_accept(&server);
+ if (err) {
+ printf("Failed accepting connection");
+ return err;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/server.c b/src/server.c
new file mode 100644
index 0000000..bee6153
--- /dev/null
+++ b/src/server.c
@@ -0,0 +1,84 @@
+#include "server.h"
+#include "login.h"
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int server_listen(server_t *server) {
+ /*
+ **
+ ** The `socket(2)` syscall returns a file descriptor for a socket
+ **
+ ** it takes two arguments that we care about:
+ ** - domain -- AF_INET for IPv4
+ ** - type -- SOCK_STREAM sequenced and reliable
+ **
+ */
+ int err = (server->listen_fd = socket(AF_INET, SOCK_STREAM, 0));
+
+ if (err == -1) {
+ perror("socket");
+ printf("Failed to create socket");
+ return err;
+ }
+
+ struct sockaddr_in server_addr = {0};
+
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ server_addr.sin_port = htons(8000);
+
+ err = bind(server->listen_fd, (struct sockaddr *)&server_addr,
+ sizeof(server_addr));
+ if (err == -1) {
+ perror("bind");
+ printf("Failed to bind socket\n");
+ return err;
+ }
+
+ err = listen(server->listen_fd, 100);
+ if (err == -1) {
+ perror("listen");
+ printf("Failed to listen on socket\n");
+ return err;
+ }
+
+ return 0;
+}
+
+int server_accept(server_t *server) {
+ int err = 0;
+ int conn_fd;
+ socklen_t client_len;
+
+ struct sockaddr_in client_addr;
+
+ client_len = sizeof(client_addr);
+
+ for (;;) {
+ err = (conn_fd = accept(server->listen_fd, (struct sockaddr *)&client_addr,
+ &client_len));
+ if (err == -1) {
+ perror("Accept");
+ printf("failed accepting connection\n");
+ return err;
+ }
+
+ if (!fork()) {
+ handle_player(conn_fd);
+ }
+ }
+
+ return err;
+}
+
+int send_to_fd(int fd, char *msg) {
+ int err = 0;
+
+ err = send(fd, msg, strlen(msg), 0);
+
+ return err;
+}
diff --git a/src/server.h b/src/server.h
new file mode 100644
index 0000000..5392f56
--- /dev/null
+++ b/src/server.h
@@ -0,0 +1,17 @@
+#ifndef SERVER_H_
+#define SERVER_H_
+
+/**
+ * Yay for storing the server properties
+ * */
+typedef struct server {
+ int listen_fd;
+} server_t;
+
+int server_listen(server_t *server);
+
+int server_accept(server_t *server);
+
+int send_to_fd(int fd, char *msg);
+
+#endif // SERVER_H_