aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2016-05-19 16:47:06 -0400
committerFranklin Wei <git@fwei.tk>2016-05-19 16:47:06 -0400
commitcb8af6e7bf5f6e70fc9722d36448213c719e83c8 (patch)
treeec9f93d6e1256d62a49fb628d8d7a3a931b58330 /src
parent7f3ba14388a586a946d721a71eb3a9862f7f6c02 (diff)
downloadnetcosm-cb8af6e7bf5f6e70fc9722d36448213c719e83c8.zip
netcosm-cb8af6e7bf5f6e70fc9722d36448213c719e83c8.tar.gz
netcosm-cb8af6e7bf5f6e70fc9722d36448213c719e83c8.tar.bz2
netcosm-cb8af6e7bf5f6e70fc9722d36448213c719e83c8.tar.xz
implement more of dunnet
Diffstat (limited to 'src')
-rw-r--r--src/SOURCES1
-rw-r--r--src/client.c59
-rw-r--r--src/client.h1
-rw-r--r--src/client_reqs.c7
-rw-r--r--src/globals.h5
-rw-r--r--src/room.c14
-rw-r--r--src/room.h9
-rw-r--r--src/server.c10
-rw-r--r--src/server.h4
-rw-r--r--src/server_reqs.c44
-rw-r--r--src/server_reqs.h8
-rw-r--r--src/userdb.c13
-rw-r--r--src/userdb.h4
-rw-r--r--src/util.c14
-rw-r--r--src/util.h8
-rw-r--r--src/world.c11
-rw-r--r--src/world.h4
-rw-r--r--src/world_api.c113
18 files changed, 283 insertions, 46 deletions
diff --git a/src/SOURCES b/src/SOURCES
index 5cb3c9b..30204ab 100644
--- a/src/SOURCES
+++ b/src/SOURCES
@@ -13,3 +13,4 @@ userdb.c
util.c
verb.c
world.c
+world_api.c
diff --git a/src/client.c b/src/client.c
index 7c69708..e06058d 100644
--- a/src/client.c
+++ b/src/client.c
@@ -38,6 +38,8 @@ static volatile sig_atomic_t output_locked = 0;
char *current_user = NULL;
+bool child_rawmode = false;
+
bool poll_requests(void);
void out_raw(const void *buf, size_t len)
@@ -160,6 +162,7 @@ tryagain:
if(len <= 0)
error("lost connection");
+ /* null-terminate */
buf[CLIENT_READ_SZ - 1] = '\0';
enum telnet_status ret = telnet_parse_data((unsigned char*)buf + bufidx, len);
@@ -609,43 +612,47 @@ auth:
while(1)
{
- out(">> ");
+ if(!child_rawmode)
+ out(">> ");
char *line = client_read();
char *orig = strdup(line);
char *save = NULL;
- char *tok = strtok_r(line, WSPACE, &save);
+ if(!child_rawmode)
+ {
+ char *tok = strtok_r(line, WSPACE, &save);
- if(!tok)
- goto next_cmd;
+ if(!tok)
+ goto next_cmd;
- all_upper(tok);
+ all_upper(tok);
- const struct client_cmd *cmd = hash_lookup(cmd_map, tok);
- if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && are_admin)))
- {
- int ret = cmd->cb(&save);
- switch(ret)
+ const struct client_cmd *cmd = hash_lookup(cmd_map, tok);
+ if(cmd && cmd->cb && (!cmd->admin_only || (cmd->admin_only && are_admin)))
+ {
+ int ret = cmd->cb(&save);
+ switch(ret)
+ {
+ case CMD_OK:
+ goto next_cmd;
+ case CMD_LOGOUT:
+ free(line);
+ free(orig);
+ goto auth;
+ case CMD_QUIT:
+ free(line);
+ free(orig);
+ goto done;
+ default:
+ error("client: bad callback return value");
+ }
+ }
+ else if(cmd && cmd->admin_only && !are_admin)
{
- case CMD_OK:
+ out("You are not allowed to do that.\n");
goto next_cmd;
- case CMD_LOGOUT:
- free(line);
- free(orig);
- goto auth;
- case CMD_QUIT:
- free(line);
- free(orig);
- goto done;
- default:
- error("client: bad callback return value");
}
}
- else if(cmd && cmd->admin_only && !are_admin)
- {
- out("You are not allowed to do that.\n");
- goto next_cmd;
- }
/* if we can't handle it, let the master process try */
send_master(REQ_EXECVERB, orig, strlen(orig) + 1);
diff --git a/src/client.h b/src/client.h
index 223bf86..0bd74bb 100644
--- a/src/client.h
+++ b/src/client.h
@@ -23,6 +23,7 @@
extern int client_fd, to_parent, from_parent;
extern bool are_admin;
+extern bool child_rawmode;
/* call from child process ONLY */
void send_master(unsigned char cmd, const void *data, size_t sz);
diff --git a/src/client_reqs.c b/src/client_reqs.c
index 21174c4..a7e6a01 100644
--- a/src/client_reqs.c
+++ b/src/client_reqs.c
@@ -56,7 +56,7 @@ bool poll_requests(void)
/* parent closed pipe */
if(!packetlen)
{
- debugf("master process died\n");
+ debugf("master process died, dropping client.\n");
exit(0);
}
@@ -66,6 +66,11 @@ bool poll_requests(void)
switch(cmd)
{
+ case REQ_RAWMODE:
+ {
+ child_rawmode = !child_rawmode;
+ break;
+ }
case REQ_BCASTMSG:
{
out((char*)data, datalen);
diff --git a/src/globals.h b/src/globals.h
index 6e050ee..100ec1d 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -55,11 +55,6 @@
#include <time.h>
#include <unistd.h>
-/* convenience macros */
-#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
/* global constants */
#define USERFILE "users.dat"
#define WORLDFILE "world.dat"
diff --git a/src/room.c b/src/room.c
index 2e7688a..19a4b73 100644
--- a/src/room.c
+++ b/src/room.c
@@ -25,6 +25,8 @@
#include "userdb.h"
#include "world.h"
+/* these do not perform hook checking on the requested action, that is
+ * the responsibility of the caller */
bool room_user_add(room_id id, struct child_data *child)
{
struct room_t *room = room_get(id);
@@ -60,6 +62,16 @@ bool room_user_del(room_id id, struct child_data *child)
return false;
}
+/* ignores hooks */
+void room_user_teleport(struct child_data *child, room_id id)
+{
+ if(child->room != id)
+ {
+ room_user_del(child->room, child);
+ room_user_add(id, child);
+ }
+}
+
void room_free(struct room_t *room)
{
if(room->data.hook_destroy)
@@ -99,7 +111,7 @@ bool room_obj_add(room_id room, struct object_t *obj)
return status;
}
-bool room_obj_add_alias(room_id room, struct object_t *obj, char *alias)
+bool room_obj_add_alias(room_id room, struct object_t *obj, const char *alias)
{
/* the primary name must be added */
if(!room_obj_get(room, obj->name))
diff --git a/src/room.h b/src/room.h
index c0304c7..62901c8 100644
--- a/src/room.h
+++ b/src/room.h
@@ -69,15 +69,19 @@ struct room_t {
/* hash maps */
void *objects; /* multimap of object name -> object */
- void *verbs;
+ void *verbs; /* name -> verb_t */
void *users; /* username -> child_data */
void *userdata;
};
/* room/world */
+
+/* none of these care about room hooks, which are the caller's
+ * responsibility */
bool room_user_add(room_id id, struct child_data *child);
bool room_user_del(room_id id, struct child_data *child);
+void room_user_teleport(struct child_data *child, room_id id);
/* On the first call, room should be a valid room id, and *save should
* point to a void pointer. On subsequent calls, room should be
@@ -92,10 +96,9 @@ const struct multimap_list *room_obj_iterate(room_id room, void **save, size_t *
*/
bool room_obj_add(room_id room, struct object_t *obj);
-bool room_obj_add_alias(room_id room, struct object_t *obj, char *alias);
+bool room_obj_add_alias(room_id room, struct object_t *obj, const char *alias);
bool room_obj_del(room_id room, const char *name);
-bool room_obj_del_alias(room_id room, struct object_t *obj, const char *alias);
bool room_obj_del_by_ptr(room_id room, struct object_t *obj);
const struct multimap_list *room_obj_get(room_id room, const char *obj);
diff --git a/src/server.c b/src/server.c
index b736124..9a28679 100644
--- a/src/server.c
+++ b/src/server.c
@@ -48,7 +48,7 @@ static char *module_handle = NULL;
/* save after every X changes to the world state */
#define SAVE_INTERVAL 10
-/* saves state periodically */
+/* saves game state periodically */
void server_save_state(bool force)
{
if(!are_child)
@@ -231,6 +231,9 @@ static void load_worldfile(void)
}
}
+ netcosm_write_userdata_cb = dlsym(module_handle, "netcosm_write_userdata_cb");
+ netcosm_read_userdata_cb = dlsym(module_handle, "netcosm_read_userdata_cb");
+
if(access(WORLDFILE, F_OK) < 0)
{
world_init(netcosm_world, netcosm_world_sz, netcosm_world_name);
@@ -484,6 +487,11 @@ static void parse_args(int argc, char *argv[])
}
}
}
+ else
+ {
+ debugf("Unknown argument `%s'\n", argv[i]);
+ exit(0);
+ }
}
}
diff --git a/src/server.h b/src/server.h
index 6d3d029..a0efb39 100644
--- a/src/server.h
+++ b/src/server.h
@@ -24,6 +24,7 @@
enum room_id;
/* everything the server needs to manage its children */
+/* aliased as user_t */
struct child_data {
pid_t pid;
@@ -40,6 +41,9 @@ struct child_data {
ev_io *io_watcher;
ev_child *sigchld_watcher;
+ /* raw mode callback (NULL if none, set by world module) */
+ void (*raw_mode_cb)(struct child_data*, char *data, size_t len);
+
/* remote IP */
struct in_addr addr;
};
diff --git a/src/server_reqs.c b/src/server_reqs.c
index ab283ca..ed2d09c 100644
--- a/src/server_reqs.c
+++ b/src/server_reqs.c
@@ -26,18 +26,29 @@
#include "world.h"
/* sends a single packet to a child, mostly reliable */
+
+/* splits REQ_BCASTMSG message into multiple packets if data length
+ * exceeds MSG_MAX, however, other requests will not be split and will
+ * cause a failed assertion */
+
static void send_packet(struct child_data *child, unsigned char cmd,
const void *data, size_t datalen)
{
- assert(datalen < MSG_MAX);
+ assert(datalen < MSG_MAX || cmd == REQ_BCASTMSG);
unsigned char pkt[MSG_MAX];
pkt[0] = cmd;
- //if((data?datalen:0) + 1 > MSG_MAX && cmd == REQ_BCASTMSG)
- //{
- // /* TODO: split long messages */
- // ;
- //}
+ if(cmd == REQ_BCASTMSG && (data?datalen:0) + 1 > MSG_MAX)
+ {
+ /* split long messages */
+ const char *ptr = data, *stop = (const char*)data + datalen;
+ while(ptr < stop)
+ {
+ send_packet(child, cmd, ptr, MIN(stop - ptr, MSG_MAX - 1));
+ ptr += MSG_MAX - 1;
+ }
+ return;
+ }
if(data && datalen)
memcpy(pkt + 1, data, datalen);
@@ -50,6 +61,19 @@ tryagain:
}
}
+void child_toggle_rawmode(struct child_data *child, void (*cb)(user_t*, char *data, size_t len))
+{
+ if(!are_child)
+ {
+ send_packet(child, REQ_RAWMODE, NULL, 0);
+ /* this pointer also indicates whether raw mode is on */
+ if(!child->raw_mode_cb)
+ child->raw_mode_cb = cb;
+ else
+ child->raw_mode_cb = NULL;
+ }
+}
+
void __attribute__((format(printf,2,3))) send_msg(struct child_data *child, const char *fmt, ...)
{
va_list ap;
@@ -495,6 +519,14 @@ static void req_listusers(unsigned char *data, size_t datalen, struct child_data
static void req_execverb(unsigned char *data, size_t datalen, struct child_data *sender)
{
(void) datalen;
+
+ /* if the child is in raw mode, pass the data to the world module */
+ if(sender->raw_mode_cb)
+ {
+ sender->raw_mode_cb(sender, (char*)data, datalen);
+ return;
+ }
+
/* first look for a room-local verb */
char *save;
char *tok = strtok_r((char*)data, " \t", &save);
diff --git a/src/server_reqs.h b/src/server_reqs.h
index 10c1755..8db6927 100644
--- a/src/server_reqs.h
+++ b/src/server_reqs.h
@@ -50,7 +50,7 @@
#define REQ_DROP 22 /* server: drop user object if allowed */
#define REQ_LISTUSERS 23 /* server: list users in USERFILE */
#define REQ_EXECVERB 24 /* server: execute a verb with its arguments */
-#define REQ_RAWMODE 25 /* child: toggle the child's processing of commands and instead sending input directly to master */
+#define REQ_RAWMODE 25 /* child: toggle the child's processing of commands and instead send input directly to master */
/* child states, sent as an int to the master */
#define STATE_INIT 0 /* initial state */
@@ -66,3 +66,9 @@ void reqmap_init(void);
void reqmap_free(void);
void send_msg(user_t *child, const char *fmt, ...) __attribute__((format(printf,2,3)));
+
+/* toggle the child into "raw mode": all commands typed by the
+ * connected client will be send to the world module;
+ * if the child is already in raw mode the callback is ignored */
+
+void child_toggle_rawmode(user_t *child, void (*cb)(user_t*, char *data, size_t len));
diff --git a/src/userdb.c b/src/userdb.c
index 0ebe159..14e9fd8 100644
--- a/src/userdb.c
+++ b/src/userdb.c
@@ -26,6 +26,7 @@
#include "server.h"
#include "server_reqs.h"
#include "userdb.h"
+#include "world.h"
static void *map = NULL;
static char *db_file = NULL;
@@ -101,6 +102,10 @@ void userdb_init(const char *file)
}
}
+ /* now we read in the world module's data, if possible */
+ if(netcosm_read_userdata_cb)
+ data->userdata = netcosm_read_userdata_cb(fd);
+
hash_insert(map, data->username, data);
}
@@ -124,8 +129,10 @@ bool userdb_write(const char *file)
if(!user)
break;
+ /* dump structure */
write(fd, user, sizeof(*user));
+ /* now go back and write what the pointers are pointing at */
size_t n_objects;
if(user->objects)
n_objects = obj_count_noalias(user->objects);
@@ -158,6 +165,12 @@ bool userdb_write(const char *file)
}
}
}
+
+ /* write world module's data, if possible */
+ if(netcosm_write_userdata_cb)
+ {
+ netcosm_write_userdata_cb(fd, user->userdata);
+ }
}
close(fd);
diff --git a/src/userdb.h b/src/userdb.h
index 25bdc7b..7958fb3 100644
--- a/src/userdb.h
+++ b/src/userdb.h
@@ -39,12 +39,16 @@ struct userdata_t {
time_t last_login;
void *objects; /* multihash of object names -> objects */
+
+ /* for use by world module */
+ void *userdata;
};
/* call before using anything else */
void userdb_init(const char *dbfile);
/* looks up a username in the DB, returns NULL upon failure */
+/* changes made to the returned structure will persist */
struct userdata_t *userdb_lookup(const char *username);
bool userdb_remove(const char *username);
diff --git a/src/util.c b/src/util.c
index 9c4b447..c5b606c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -173,6 +173,20 @@ void write_size(int fd, size_t b)
error("write failed");
}
+int read_int(int fd)
+{
+ int ret;
+ if(read(fd, &ret, sizeof(ret)) != sizeof(ret))
+ error("unexpected EOF");
+ return ret;
+}
+
+void write_int(int fd, int b)
+{
+ if(write(fd, &b, sizeof(b)) != sizeof(b))
+ error("write failed");
+}
+
bool is_vowel(char c)
{
switch(tolower(c))
diff --git a/src/util.h b/src/util.h
index 8007ec8..a6965be 100644
--- a/src/util.h
+++ b/src/util.h
@@ -23,6 +23,11 @@
#define WSPACE " \t\r\n"
+/* convenience macros */
+#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
/* utility functions */
void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...);
void __attribute__((format(printf,4,5))) debugf_real(const char*, int, const char*, const char *fmt, ...);
@@ -48,6 +53,9 @@ uint64_t read_uint64(int fd);
void write_size(int fd, size_t);
size_t read_size(int fd);
+void write_int(int fd, int i);
+int read_int(int fd);
+
bool is_vowel(char c);
size_t strlcat(char *dst, const char *src, size_t siz);
diff --git a/src/world.c b/src/world.c
index d0e1711..45a1717 100644
--- a/src/world.c
+++ b/src/world.c
@@ -39,6 +39,9 @@ size_t netcosm_world_sz;
void (*netcosm_world_simulation_cb)(void) = NULL;
unsigned netcosm_world_simulation_interval = 0;
+void (*netcosm_write_userdata_cb)(int fd, void *ptr) = NULL;
+void *(*netcosm_read_userdata_cb)(int fd) = NULL;
+
const char *netcosm_world_name;
/* processed world data */
@@ -188,8 +191,6 @@ void world_free(void)
free(world);
world = NULL;
}
- if(sim_timer)
- free(sim_timer);
}
static void start_sim_callback(void)
@@ -440,6 +441,12 @@ bool world_verb_add(struct verb_t *verb)
return !hash_insert(verb_map, verb->name, verb);
}
+bool world_verb_del(struct verb_t *verb)
+{
+ init_map();
+ return hash_remove(verb_map, verb->name);
+}
+
void *world_verb_map(void)
{
init_map();
diff --git a/src/world.h b/src/world.h
index 4ce253e..f850876 100644
--- a/src/world.h
+++ b/src/world.h
@@ -45,6 +45,10 @@ extern size_t netcosm_world_sz;
extern void (*netcosm_world_simulation_cb)(void);
extern unsigned netcosm_world_simulation_interval;
+/* user data callback */
+extern void (*netcosm_write_userdata_cb)(int fd, void *ptr);
+extern void* (*netcosm_read_userdata_cb)(int fd);
+
extern const char *netcosm_world_name;
#endif
diff --git a/src/world_api.c b/src/world_api.c
new file mode 100644
index 0000000..f38f892
--- /dev/null
+++ b/src/world_api.c
@@ -0,0 +1,113 @@
+/*
+ * 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 "globals.h"
+
+#include "server.h"
+#include "server_reqs.h"
+#include "hash.h"
+#include "multimap.h"
+#include "userdb.h"
+#include "world.h"
+#include "world_api.h"
+
+static const struct world_api api = {
+ obj_new,
+ obj_dup,
+ obj_copy,
+ obj_free,
+ room_user_teleport,
+ room_obj_add,
+ room_obj_add_alias,
+ room_obj_del,
+ room_obj_del_by_ptr,
+ room_obj_get,
+ room_obj_get_size,
+ room_obj_count,
+ room_obj_count_noalias,
+ room_verb_add,
+ room_verb_del,
+ room_verb_map,
+ room_get,
+ room_get_id,
+ world_verb_add,
+ world_verb_del,
+ world_verb_map,
+ verb_new,
+ verb_free,
+ hash_djb,
+ compare_strings,
+ compare_strings_nocase,
+ hash_init,
+ hash_setfreedata_cb,
+ hash_setfreekey_cb,
+ hash_free,
+ hash_insert,
+ hash_overwrite,
+ hash_lookup,
+ hash_remove,
+ hash_iterate,
+ hash_insert_pairs,
+ hash_getkeyptr,
+ hash_dup,
+ hash_setdupdata_cb,
+ multimap_init,
+ multimap_free,
+ multimap_lookup,
+ multimap_insert,
+ multimap_delete,
+ multimap_delete_all,
+ multimap_iterate,
+ multimap_size,
+ multimap_setfreedata_cb,
+ multimap_dup,
+ multimap_setdupdata_cb,
+ multimap_copy,
+ send_msg,
+ child_toggle_rawmode,
+ userdb_lookup,
+ userdb_remove,
+ userdb_size,
+ userdb_add,
+ userdb_iterate,
+ userdb_add_obj,
+ userdb_del_obj,
+ userdb_del_obj_by_ptr,
+ error,
+ all_upper,
+ all_lower,
+ write_string,
+ read_string,
+ write_roomid,
+ read_roomid,
+ write_bool,
+ read_bool,
+ write_uint32,
+ read_uint32,
+ write_uint64,
+ read_uint64,
+ write_size,
+ read_size,
+ write_int,
+ read_int,
+ is_vowel,
+ strlcat,
+ format_noun
+};
+
+const struct world_api *nc = &api;