aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-04-16 21:16:44 -0400
committerFranklin Wei <git@fwei.tk>2016-04-16 21:16:44 -0400
commita305a931726bc13c604afca1209a656a8fbedc46 (patch)
treee8ce29b2354aca21dae1bb1f0b1bc227b34adf17
parentcfe28eb7eda17e4a604749fe2f027153a2ac5b6e (diff)
downloadnetcosm-a305a931726bc13c604afca1209a656a8fbedc46.zip
netcosm-a305a931726bc13c604afca1209a656a8fbedc46.tar.gz
netcosm-a305a931726bc13c604afca1209a656a8fbedc46.tar.bz2
netcosm-a305a931726bc13c604afca1209a656a8fbedc46.tar.xz
allow for modular world loading
-rw-r--r--.gitignore1
-rw-r--r--Makefile22
-rw-r--r--SOURCES16
-rw-r--r--src/SOURCES15
-rw-r--r--src/globals.h1
-rw-r--r--src/obj.c3
-rw-r--r--src/server.c83
-rw-r--r--src/world.c21
-rw-r--r--src/world.h16
-rw-r--r--tools/worldgen.c122
-rw-r--r--worlds/SOURCES2
-rw-r--r--worlds/netcosm_default.c (renamed from worlds/test.c)0
-rw-r--r--worlds/template.c99
13 files changed, 354 insertions, 47 deletions
diff --git a/.gitignore b/.gitignore
index 229fb89..b6b8e0f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ a.out
*~
*.d
netcosm.log
+build
diff --git a/Makefile b/Makefile
index 3b59ad4..5214414 100644
--- a/Makefile
+++ b/Makefile
@@ -27,19 +27,22 @@ PLATFORM = unix
OUT = $(BUILDDIR)/$(PLATFORM).bin
PREFIX = /usr/local
-SRC = $(shell cat SOURCES)
-OBJ = $(patsubst %.c,$(BUILDDIR)/%.o,$(SRC))
+SRC = $(shell cat src/SOURCES)
+OBJ = $(patsubst %.c,$(BUILDDIR)/src/%.o,$(SRC))
+
+WORLD_SRC = $(shell cat worlds/SOURCES)
+WORLD_OBJ = $(patsubst %.c,$(BUILDDIR)/worlds/%.so,$(WORLD_SRC))
INCLUDES = -I src/ -I export/include/
WARNFLAGS = -Wall -Wextra -Wshadow -fno-strict-aliasing
OPTFLAGS = -O2
-DEBUGFLAGS = -g -fstack-protector -D_FORTIFY_SOURCE=2
+DEBUGFLAGS = -g -fstack-protector
CFLAGS = $(OPTFLAGS) $(DEBUGFLAGS) $(WARNFLAGS) -std=c99 $(INCLUDES)
-LDFLAGS = -lev -lcrypto
+LDFLAGS = -lev -lcrypto -ldl
HEADERS = src/*.h export/include/*.h
@@ -49,11 +52,11 @@ DEPS = $(patsubst %.c,$(BUILDDIR)/%.d,$(SRC))
################################################################################
.PHONY: all
-all: | $(OUT)
+all: | $(OUT) $(WORLD_OBJ)
$(OUT): $(OBJ)
@echo "LD $@"
- @$(CC) $(OBJ) $(CFLAGS) -o $@ $(LDFLAGS)
+ @$(CC) $(OBJ) $(CFLAGS) -o $@ $(LDFLAGS) -rdynamic
$(OBJ): | $(BUILDDIR)
@@ -65,6 +68,11 @@ $(BUILDDIR)/%.o: %.c $(BUILDDIR)/%.d Makefile
@mkdir -p `dirname $@`
@$(CC) -c $< $(CFLAGS) -o $@
+$(BUILDDIR)/%.so: %.c $(BUILDDIR)/%.d Makefile
+ @echo "CC $<"
+ @mkdir -p `dirname $@`
+ @$(CC) $< $(CFLAGS) -shared -o $@ -D_WORLD_MODULE_ -fPIC
+
# Dependencies
################################################################################
@@ -86,7 +94,7 @@ setcap:
.PHONY: clean
clean:
@echo "Cleaning build directory..."
- @rm -f $(OBJ) $(OUT)
+ @rm -f $(OBJ) $(OUT) $(WORLD_OBJ)
.PHONY: veryclean
veryclean:
diff --git a/SOURCES b/SOURCES
deleted file mode 100644
index 289b682..0000000
--- a/SOURCES
+++ /dev/null
@@ -1,16 +0,0 @@
-src/auth.c
-src/client.c
-src/client_reqs.c
-src/hash.c
-src/main.c
-src/multimap.c
-src/obj.c
-src/room.c
-src/server.c
-src/server_reqs.c
-src/telnet.c
-src/userdb.c
-src/util.c
-src/verb.c
-src/world.c
-worlds/test.c
diff --git a/src/SOURCES b/src/SOURCES
new file mode 100644
index 0000000..5cb3c9b
--- /dev/null
+++ b/src/SOURCES
@@ -0,0 +1,15 @@
+auth.c
+client.c
+client_reqs.c
+hash.c
+main.c
+multimap.c
+obj.c
+room.c
+server.c
+server_reqs.c
+telnet.c
+userdb.c
+util.c
+verb.c
+world.c
diff --git a/src/globals.h b/src/globals.h
index 193bed4..6e050ee 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -30,6 +30,7 @@
#include <arpa/telnet.h>
#include <assert.h>
#include <ctype.h>
+#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
diff --git a/src/obj.c b/src/obj.c
index ce4a890..c842d70 100644
--- a/src/obj.c
+++ b/src/obj.c
@@ -21,6 +21,7 @@
#include "hash.h"
#include "multimap.h"
#include "obj.h"
+#include "world.h"
/* map of class names -> object classes */
static void *obj_class_map = NULL;
@@ -41,8 +42,6 @@ struct object_t *obj_new(const char *class_name)
{
if(!obj_class_map)
{
- extern const struct obj_class_t netcosm_obj_classes[];
- extern const size_t netcosm_obj_classes_sz;
obj_class_map = hash_init(netcosm_obj_classes_sz / 2 + 1,
hash_djb,
compare_strings);
diff --git a/src/server.c b/src/server.c
index a4558bc..55cb1f3 100644
--- a/src/server.c
+++ b/src/server.c
@@ -41,6 +41,8 @@ static uint16_t port = DEFAULT_PORT;
static int server_socket;
+static char *world_module = "build/worlds/netcosm_default.so";
+
#define SAVE_INTERVAL 10
/* saves state periodically */
@@ -179,10 +181,28 @@ static void check_userfile(void)
static void load_worldfile(void)
{
- extern const struct roomdata_t netcosm_world[];
- extern const size_t netcosm_world_sz;
+ /* load the world module */
+ void *handle = dlopen(world_module, RTLD_NOW);
+ if(!handle)
+ error("cannot load world module `%s' (%s)", world_module, dlerror());
+
+ /* load symbols */
+ size_t *ptr;
+
+ netcosm_verb_classes = dlsym(handle, "netcosm_verb_classes");
+ ptr = dlsym(handle, "netcosm_verb_classes_sz");
+ netcosm_verb_classes_sz = *ptr;
- extern const char *netcosm_world_name;
+ netcosm_obj_classes = dlsym(handle, "netcosm_obj_classes");
+ ptr = dlsym(handle, "netcosm_obj_classes_sz");
+ netcosm_obj_classes_sz = *ptr;
+
+ netcosm_world = dlsym(handle, "netcosm_world");
+ ptr = dlsym(handle, "netcosm_world_sz");
+ netcosm_world_sz = *ptr;
+
+ char **tmp = dlsym(handle, "netcosm_world_name");
+ netcosm_world_name = *tmp;
if(access(WORLDFILE, F_OK) < 0)
{
@@ -375,6 +395,21 @@ static void init_signals(void)
error("sigaction");
}
+static void __attribute__((noreturn)) print_help(char *argv[])
+{
+ debugf("Usage: %s [OPTION]...\n", argv[0]);
+ debugf("NetCosm MUD server\n");
+ debugf("\n");
+ debugf(" -a USER PASS\tautomatic setup with USER/PASS\n");
+ debugf(" -d PREFIX\tcreate and change to PREFIX before writing data files\n");
+ debugf(" -h, -?\t\tshow this help\n");
+ debugf(" -p PORT\tlisten on PORT\n");
+ debugf(" -w MODULE\tuse a different world module\n");
+ exit(0);
+}
+
+static char *data_prefix = NULL;
+
static void parse_args(int argc, char *argv[])
{
for(int i = 1; i < argc; ++i)
@@ -388,20 +423,29 @@ static void parse_args(int argc, char *argv[])
switch(c)
{
case 'h': /* help */
- debugf("Usage: %s [-d PREFIX] [-a <username> <password>]\n", argv[0]);
- exit(0);
+ case '?':
+ print_help(argv);
case 'a': /* automatic first-run config */
autoconfig = true;
+ if(i + 2 > argc)
+ print_help(argv);
autouser = argv[++i];
autopass = argv[++i];
break;
case 'd': /* set data prefix */
- mkdir(argv[++i], 0700);
- if(chdir(argv[i]) < 0)
- {
- debugf("Cannot access data prefix.\n");
- exit(0);
- }
+ if(i + 1 > argc)
+ print_help(argv);
+ data_prefix = argv[++i];
+ break;
+ case 'p': /* set port */
+ if(i + 1 > argc)
+ print_help(argv);
+ port = strtol(argv[++i], NULL, 10);
+ break;
+ case 'w': /* world */
+ if(i + 1 > argc)
+ print_help(argv);
+ world_module = argv[++i];
break;
default:
c = 'h';
@@ -409,8 +453,6 @@ static void parse_args(int argc, char *argv[])
}
}
}
- else
- port = strtol(argv[i], NULL, 10);
}
}
@@ -431,13 +473,24 @@ int server_main(int argc, char *argv[])
parse_args(argc, argv);
+ /* this must be done before any world module data is used */
+ load_worldfile();
+
+ if(data_prefix)
+ {
+ mkdir(data_prefix, 0700);
+ if(chdir(data_prefix) < 0)
+ {
+ debugf("Cannot access data prefix.\n");
+ exit(0);
+ }
+ }
+
userdb_init(USERFILE);
/* also performs first-time setup: */
check_userfile();
- load_worldfile();
-
/* initialize request map */
reqmap_init();
diff --git a/src/world.c b/src/world.c
index b3bb4b3..d7c3590 100644
--- a/src/world.c
+++ b/src/world.c
@@ -23,6 +23,20 @@
#include "room.h"
#include "world.h"
+/* verb classes */
+const struct verb_class_t *netcosm_verb_classes;
+size_t netcosm_verb_classes_sz;
+
+/* object classes */
+const struct obj_class_t *netcosm_obj_classes;
+size_t netcosm_obj_classes_sz;
+
+/* rooms */
+const struct roomdata_t *netcosm_world;
+size_t netcosm_world_sz;
+
+const char *netcosm_world_name;
+
/* processed world data */
static struct room_t *world;
@@ -184,7 +198,10 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
read(fd, &world_sz, sizeof(world_sz));
if(world_sz != data_sz)
+ {
+ debugf("Incompatible world state.\n");
return false;
+ }
world = calloc(world_sz, sizeof(struct room_t));
@@ -192,10 +209,12 @@ bool world_load(const char *fname, const struct roomdata_t *data, size_t data_sz
if(strcmp(name, world_name))
{
free(world_name);
- debugf("Incompatible world state.\n");
+ debugf("Incompatible world state (%s %s).\n", name, world_name);
return false;
}
+ debugf("Loading world `%s'.\n", world_name);
+
obj_set_idcounter(1);
size_t n_global_verbs = read_size(fd);
diff --git a/src/world.h b/src/world.h
index 50f9769..5dcd85d 100644
--- a/src/world.h
+++ b/src/world.h
@@ -26,19 +26,23 @@
/* the world module MUST define all of the following: */
+#ifndef _WORLD_MODULE_
+/* note that these are now dynamically loaded */
+
/* verb classes */
-extern const struct verb_class_t netcosm_verb_classes[];
-extern const size_t netcosm_verb_classes_sz;
+extern const struct verb_class_t *netcosm_verb_classes;
+extern size_t netcosm_verb_classes_sz;
/* object classes */
-extern const struct obj_class_t netcosm_obj_classes[];
-extern const size_t netcosm_obj_classes_sz;
+extern const struct obj_class_t *netcosm_obj_classes;
+extern size_t netcosm_obj_classes_sz;
/* rooms */
-extern const struct roomdata_t netcosm_world[];
-extern const size_t netcosm_world_sz;
+extern const struct roomdata_t *netcosm_world;
+extern size_t netcosm_world_sz;
extern const char *netcosm_world_name;
+#endif
/*** loads the world into RAM for the first time, resets the game state ***/
void world_init(const struct roomdata_t *data, size_t sz, const char *name);
diff --git a/tools/worldgen.c b/tools/worldgen.c
new file mode 100644
index 0000000..e0cd947
--- /dev/null
+++ b/tools/worldgen.c
@@ -0,0 +1,122 @@
+/* generates a 3d world with no content */
+
+#include <globals.h>
+#include <hash.h>
+#include <room.h>
+
+/* x and y are horizontal and forward-back axes, respectively */
+/* z is the vertical axis */
+
+struct direction_info_t {
+ enum direction_t dir;
+ int off_x, off_y, off_z;
+} dirs[] = {
+ { DIR_N, 0, 1, 0 },
+ { DIR_NE, 1, 1, 0 },
+ { DIR_E, 1, 0, 0 },
+ { DIR_SE, 1, -1, 0 },
+ { DIR_S, 0, -1, 0 },
+ { DIR_SW, -1, -1, 0 },
+ { DIR_W, -1, 0, 0 },
+ { DIR_NW, -1, 1, 0 },
+ { DIR_UP, 0, 0, 1 },
+ { DIR_DN, 0, 0, -1 },
+};
+
+int main()
+{
+ printf("#include <world_api.h>\n");
+ printf("const struct roomdata_t netcosm_world[] = {\n");
+
+ for(int x = 0; x < WORLD_DIM; ++x)
+ for(int y = 0; y < WORLD_DIM; ++y)
+ for(int z = 0; z < WORLD_DIM; ++z)
+ {
+ printf("{\n");
+ printf("\"room_%d_%d_%d\",\n", x, y, z);
+ printf("\"Room (%d,%d,%d)\",\n", x, y, z);
+ printf("\"You are in a room...\",");
+
+ char *adj[ARRAYLEN(dirs)];
+ for(int i = 0; i < ARRAYLEN(dirs); ++i)
+ {
+ int new_x = x + dirs[i].off_x,
+ new_y = y + dirs[i].off_y,
+ new_z = z + dirs[i].off_z;
+
+ if(new_x < 0 || new_x >= WORLD_DIM ||
+ new_y < 0 || new_y >= WORLD_DIM ||
+ new_z < 0 || new_z >= WORLD_DIM)
+ asprintf(adj + i, "NULL");
+ else
+ asprintf(adj + i, "\"room_%d_%d_%d\"",
+ new_x,
+ new_y,
+ new_z);
+ }
+
+ printf("{ ");
+ for(int i = 0; i < ARRAYLEN(dirs); ++i)
+ {
+ printf("%s, ", adj[i]);
+ free(adj[i]);
+ }
+ printf("NONE_IN, NONE_OT },\n");
+
+ for(int i = 0; i < 6; ++i)
+ printf("NULL,\n");
+
+ printf("},\n");
+ }
+
+ printf("};\n");
+ printf("const size_t netcosm_world_sz = ARRAYLEN(netcosm_world);\n");
+ printf("const char *netcosm_world_name = \"World Name Here\";\n");
+
+ printf("static void generic_ser(int fd, struct object_t *obj)\n");
+ printf("{\n");
+ printf(" write_string(fd, obj->userdata);\n");
+ printf("}\n");
+
+ printf("static void generic_deser(int fd, struct object_t *obj)\n");
+ printf("{\n");
+ printf(" obj->userdata = read_string(fd);\n");
+ printf("}\n");
+
+ printf("static void generic_destroy(struct object_t *obj)\n");
+ printf("{\n");
+ printf(" free(obj->userdata);\n");
+ printf("}\n");
+
+ printf("static const char *generic_desc(struct object_t *obj, user_t *user)\n");
+ printf("{\n");
+ printf(" (void) user;\n");
+ printf(" return obj->userdata;\n");
+ printf("}\n");
+
+ printf("static void *generic_dup(struct object_t *obj)\n");
+ printf("{\n");
+ printf(" return strdup(obj->userdata);\n");
+ printf("}\n");
+
+ printf("const struct obj_class_t netcosm_obj_classes[] = {\n");
+ printf(" {\n");
+ printf(" \"/generic\",\n");
+ printf(" generic_ser,\n");
+ printf(" generic_deser,\n");
+ printf(" NULL,\n");
+ printf(" NULL,\n");
+ printf(" generic_destroy,\n");
+ printf(" generic_desc,\n");
+ printf(" generic_dup,\n");
+ printf(" },\n");
+ printf("};\n");
+ printf("const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes);\n");
+
+ printf("const struct verb_class_t netcosm_verb_classes[] = {\n");
+ printf("\n");
+ printf("};\n");
+ printf("\n");
+ printf("const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes);\n");
+
+};
diff --git a/worlds/SOURCES b/worlds/SOURCES
new file mode 100644
index 0000000..1fcc434
--- /dev/null
+++ b/worlds/SOURCES
@@ -0,0 +1,2 @@
+netcosm_default.c
+template.c
diff --git a/worlds/test.c b/worlds/netcosm_default.c
index 11096df..11096df 100644
--- a/worlds/test.c
+++ b/worlds/netcosm_default.c
diff --git a/worlds/template.c b/worlds/template.c
new file mode 100644
index 0000000..d22ecb4
--- /dev/null
+++ b/worlds/template.c
@@ -0,0 +1,99 @@
+/*
+ * NetCosm - a MUD server
+ * Copyright (C) 2016 Franklin Wei
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <world_api.h>
+
+/* This is a sample world implemented in NetCosm. */
+
+/* A world is composed of rooms, object classes, and verb classes. For
+ * now they are defined as global arrays, but this is subject to change.
+ */
+
+/* This is our array of rooms. Each contains multiple callbacks and
+ strings pointing to other rooms, see room.h for details */
+const struct roomdata_t netcosm_world [] = {
+ {
+ "uniq_id", // this must be globally unique
+ "Room name",
+ "Initial description",
+
+ /* these can be replaced with strings identifying other rooms */
+ { NONE_N, NONE_NE, NONE_E, NONE_SE, NONE_S, NONE_SW, NONE_W, NONE_NW, NONE_UP, NONE_DN, NONE_IN, NONE_OT },
+
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ },
+};
+
+const size_t netcosm_world_sz = ARRAYLEN(netcosm_world);
+const char *netcosm_world_name = "World Name Here";
+
+/********* OBJECTS *********/
+
+static void generic_ser(int fd, struct object_t *obj)
+{
+ write_string(fd, obj->userdata);
+}
+
+static void generic_deser(int fd, struct object_t *obj)
+{
+ obj->userdata = read_string(fd);
+}
+
+static void generic_destroy(struct object_t *obj)
+{
+ free(obj->userdata);
+}
+
+static const char *generic_desc(struct object_t *obj, user_t *user)
+{
+ (void) user;
+ return obj->userdata;
+}
+
+static void *generic_dup(struct object_t *obj)
+{
+ return strdup(obj->userdata);
+}
+
+const struct obj_class_t netcosm_obj_classes[] = {
+ {
+ "/generic",
+ generic_ser,
+ generic_deser,
+ NULL,
+ NULL,
+ generic_destroy,
+ generic_desc,
+ generic_dup,
+ },
+};
+
+const size_t netcosm_obj_classes_sz = ARRAYLEN(netcosm_obj_classes);
+
+/********* VERBS *********/
+
+const struct verb_class_t netcosm_verb_classes[] = {
+
+};
+
+const size_t netcosm_verb_classes_sz = ARRAYLEN(netcosm_verb_classes);