From 2acbe5e19b6daf254adebc742926834220f4b27f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Tue, 7 Nov 2023 11:52:20 +0000 Subject: [PATCH 1/9] Initial test implementation of output message queuing. --- Makefile.am | 3 +- source/server/main.c | 46 ++++++++----- source/server/output-queue.c | 129 +++++++++++++++++++++++++++++++++++ source/server/output-queue.h | 61 +++++++++++++++++ 4 files changed, 220 insertions(+), 19 deletions(-) create mode 100644 source/server/output-queue.c create mode 100644 source/server/output-queue.h diff --git a/Makefile.am b/Makefile.am index 6f59a34..37985b6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,8 @@ SilverMUDServer_SOURCES = \ source/server/player-data.c \ source/server/connections.c \ source/server/scheme-integration.c \ - source/server/main.c + source/server/output-queue.c \ + source/server/main.c SilverMUDClient_SOURCES = \ source/messages.c \ diff --git a/source/server/main.c b/source/server/main.c index a7bab43..23fac61 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -19,6 +19,7 @@ #include #include +#include "output-queue.h" #include "player-data.h" #include "connections.h" #include "../messages.h" @@ -104,7 +105,7 @@ int main (int argc, char ** argv) // Create some structures needed to store global state: struct PlayerList * globalPlayerList = createPlayerList(); - + struct OutputQueue * globalOutputQueue = createOutputQueue(); // Start a REPL thread: //pthread_t schemeREPLThread; //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); @@ -213,23 +214,10 @@ int main (int argc, char ** argv) continue; } // ONLY FOR DEMO - - struct ServerToClientMessage outputMessage; - - // Copy the message to the output format: - outputMessage.type = LOCAL_CHAT; - - strncpy(outputMessage.name, connection->player->name, 64); - strncpy(outputMessage.content, message.content, MESSAGE_CONTENT_LENGTH); - - // Echo the message into all other clients: (Temporary) - struct ClientConnectionNode * currentClient = clientConnections.head; - while (currentClient != NULL) - { - gnutls_record_send(*currentClient->connection->tlsSession, &outputMessage, - sizeof(struct ServerToClientMessage)); - currentClient = currentClient->next; - } +] + pushOutputMessage(globalOutputQueue, false, globalPlayerList, LOCAL_CHAT, + connection->player->name, message.content, + MESSAGE_NAME_LENGTH, MESSAGE_CONTENT_LENGTH); } } else @@ -241,7 +229,29 @@ int main (int argc, char ** argv) close(events[index].data.fd); removeConnectionByFileDescriptor(&clientConnections, events[index].data.fd); } + } + } + + // Output the queue: + struct OutputMessage * currentMessage = NULL; + while (globalOutputQueue->count != 0) + { + currentMessage = popOutputMessage(globalOutputQueue); + struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; + + while (currentPlayerNode != NULL) + { + gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, + currentMessage->message, sizeof(struct ServerToClientMessage)); + currentPlayerNode = currentPlayerNode->next; } + + if (currentMessage->deallocatePlayerList == true) + { + deallocatePlayerList(¤tMessage->recepients); + } + + deallocateOutputMessage(¤tMessage); } } diff --git a/source/server/output-queue.c b/source/server/output-queue.c new file mode 100644 index 0000000..906638f --- /dev/null +++ b/source/server/output-queue.c @@ -0,0 +1,129 @@ +// ========================================= +// | SilverMUD Server - output-queue.c | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#include +#include +#include +#include "output-queue.h" + +struct OutputQueue * const createOutputQueue() +{ + // Allocate a new queue: + struct OutputQueue * const newQueue = calloc(1, sizeof(struct OutputQueue)); + + // Initialize it: + pthread_mutex_init(&newQueue->mutex, NULL); + newQueue->count = 0; + newQueue->front = NULL; + newQueue->back = NULL; + + // Return the new queue: + return newQueue; +} + +size_t pushOutputMessage(struct OutputQueue * const queue, + const bool deallocatePlayerList, + struct PlayerList * const recepients, + const enum MessageTypes type, + const char const * name, const char const * content, + const size_t nameLength, const size_t contentLength) +{ + // Allocate the appropriate memory for the queued message: + struct OutputMessage * newMessage = calloc(1, sizeof(struct OutputMessage)); + newMessage->message = calloc(1, sizeof(struct ServerToClientMessage)); + + // Copy in the appropriate values to the ServerToClientMessage: + newMessage->message->type = type; + + strncpy(newMessage->message->name, name, (nameLength < MESSAGE_NAME_LENGTH) ? + nameLength : MESSAGE_NAME_LENGTH); + newMessage->message->name[MESSAGE_NAME_LENGTH - 1] = '\0'; + + strncpy(newMessage->message->content, content, (contentLength < MESSAGE_CONTENT_LENGTH) ? + contentLength : MESSAGE_CONTENT_LENGTH); + newMessage->message->content[MESSAGE_CONTENT_LENGTH - 1] = '\0'; + + // Copy in the appropriate values to the OutputMessage: + newMessage->deallocatePlayerList = deallocatePlayerList; + newMessage->recepients = recepients; + + // Entering critical section - Lock the queue: + pthread_mutex_lock(&queue->mutex); + + // Add it to the queue: + if (queue->back != NULL) + { + queue->back->next = newMessage; + queue->back = newMessage; + } + + if (queue->front == NULL) + { + queue->front = newMessage; + queue->back = newMessage; + } + + queue->count++; + + // Leaving critical section - Unlock the queue: + pthread_mutex_unlock(&queue->mutex); + + return queue->count; +} + +struct OutputMessage * popOutputMessage(struct OutputQueue * queue) +{ + if (queue->count == 0) + { + return NULL; + } + + // Entering the critical section - Lock the queue: + pthread_mutex_lock(&queue->mutex); + + struct OutputMessage * message = queue->front; + queue->count--; + + if(queue->count == 0) + { + queue->front = NULL; + queue->back = NULL; + } + + else + { + queue->front = queue->front->next; + } + + // Leaving the critical section - Unlock the queue: + pthread_mutex_unlock(&queue->mutex); + + return message; +} + +void deallocateOutputMessage(struct OutputMessage ** message) +{ + // Free and set the pointer to NULL: + free(*message); + message = NULL; +} + +// ==================================================== +// | End of output-queue.c, copyright notice follows. | +// ==================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + diff --git a/source/server/output-queue.h b/source/server/output-queue.h new file mode 100644 index 0000000..d2b1f9a --- /dev/null +++ b/source/server/output-queue.h @@ -0,0 +1,61 @@ +// ========================================= +// | SilverMUD Server - output-queue.h | +// | Copyright (C) 2023, Barra Ó Catháin | +// | See end of file for copyright notice. | +// ========================================= +#ifndef OUTPUT_QUEUE_H +#define OUTPUT_QUEUE_H +#include +#include + +#include "../messages.h" + +struct OutputMessage +{ + // Allows for easy reuse of existing player lists, such as the global list + // or an area's playerlist: + bool deallocatePlayerList; + + struct OutputMessage * next; + struct PlayerList * recepients; + struct ServerToClientMessage * message; +}; + +struct OutputQueue +{ + pthread_mutex_t mutex; + size_t count; + struct OutputMessage * front, * back; +}; + +struct OutputQueue * const createOutputQueue(); + +size_t pushOutputMessage(struct OutputQueue * const queue, + const bool deallocatePlayerList, + struct PlayerList * const recepients, + const enum MessageTypes type, + const char const * name, const char const * content, + const size_t nameLength, const size_t contentLength); + +struct OutputMessage * popOutputMessage(); + +void deallocateOutputMessage(struct OutputMessage ** message); + +#endif +// ==================================================== +// | End of output-queue.h, copyright notice follows. | +// ==================================================== + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + From e11a7b3a76d305c2bbbb1b103c299f46ac24f10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Tue, 7 Nov 2023 22:46:15 +0000 Subject: [PATCH 2/9] Basic global messaging functions for Scheme --- source/server/main.c | 9 +++++++-- source/server/output-queue.h | 6 +++--- source/server/scheme-integration.c | 30 +++++++++++++++++++++++++++++- source/server/scheme-integration.h | 2 ++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/source/server/main.c b/source/server/main.c index 23fac61..fdc4475 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -106,6 +106,11 @@ int main (int argc, char ** argv) // Create some structures needed to store global state: struct PlayerList * globalPlayerList = createPlayerList(); struct OutputQueue * globalOutputQueue = createOutputQueue(); + + scm_c_define_gsubr("push-output-message", 6, 0, 0, &push_output_message); + scm_c_define("*globalPlayerList*", scm_from_pointer(globalPlayerList, NULL)); + scm_c_define("*globalOutputQueue*", scm_from_pointer(globalOutputQueue, NULL)); + // Start a REPL thread: //pthread_t schemeREPLThread; //pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); @@ -214,10 +219,10 @@ int main (int argc, char ** argv) continue; } // ONLY FOR DEMO -] + pushOutputMessage(globalOutputQueue, false, globalPlayerList, LOCAL_CHAT, connection->player->name, message.content, - MESSAGE_NAME_LENGTH, MESSAGE_CONTENT_LENGTH); + MESSAGE_NAME_LENGTH, MESSAGE_CONTENT_LENGTH); } } else diff --git a/source/server/output-queue.h b/source/server/output-queue.h index d2b1f9a..b988ba8 100644 --- a/source/server/output-queue.h +++ b/source/server/output-queue.h @@ -14,8 +14,8 @@ struct OutputMessage { // Allows for easy reuse of existing player lists, such as the global list // or an area's playerlist: - bool deallocatePlayerList; - + bool deallocatePlayerList; + struct OutputMessage * next; struct PlayerList * recepients; struct ServerToClientMessage * message; @@ -37,7 +37,7 @@ size_t pushOutputMessage(struct OutputQueue * const queue, const char const * name, const char const * content, const size_t nameLength, const size_t contentLength); -struct OutputMessage * popOutputMessage(); +struct OutputMessage * popOutputMessage(struct OutputQueue * queue); void deallocateOutputMessage(struct OutputMessage ** message); diff --git a/source/server/scheme-integration.c b/source/server/scheme-integration.c index 56bd86d..ce61391 100644 --- a/source/server/scheme-integration.c +++ b/source/server/scheme-integration.c @@ -3,9 +3,37 @@ // | Copyright (C) 2023, Barra Ó Catháin | // | See end of file for copyright notice. | // =========================================== +#include #include -SCM scheme_get_player_by_name(SCM name); +#include "../messages.h" +#include "output-queue.h" + +SCM scheme_get_player_by_name(SCM name, SCM queue); + +SCM push_output_message(SCM queue, SCM deallocate_list, SCM recepients, SCM type, SCM name, SCM content) +{ + // Convert our scheme values into appropriate data types: + struct OutputQueue * queue_c = scm_to_pointer(queue); + bool deallocate_list_c = scm_to_bool(deallocate_list); + struct PlayerList * recepients_c = scm_to_pointer(recepients); + enum MessageTypes type_c = scm_to_int(type); + + // Turn the Scheme strings into C strings: + size_t nameLength, contentLength; + char * name_c = scm_to_locale_stringn(name, &nameLength); + char * content_c = scm_to_locale_stringn(content, &contentLength); + + // Call the C function: + pushOutputMessage(queue_c, deallocate_list_c, recepients_c, type_c, name_c, content_c, + nameLength, contentLength); + + // Free the created C strings: + free(name_c); + free(content_c); + + return SCM_BOOL_T; +} // ========================================================== // | End of scheme-integration.c, copyright notice follows. | diff --git a/source/server/scheme-integration.h b/source/server/scheme-integration.h index 9992bfb..41accfc 100644 --- a/source/server/scheme-integration.h +++ b/source/server/scheme-integration.h @@ -8,6 +8,8 @@ SCM scheme_get_player_by_name(SCM name); +SCM push_output_message(SCM queue, SCM deallocate_list, SCM recepients, SCM type, SCM name, SCM content); + #endif // ========================================================== // | End of scheme-integration.h, copyright notice follows. | From 2e813ae29c3bc0865d31a47296232ce0a62a72a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Thu, 9 Nov 2023 23:51:50 +0000 Subject: [PATCH 3/9] Added independent output thread. --- .gitignore | 4 +++- Makefile.am | 2 ++ configure.ac | 4 +--- source/server/main.c | 26 ++++---------------------- source/server/output-queue.c | 31 +++++++++++++++++++++++++++++++ source/server/output-queue.h | 2 ++ 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 7d9403b..c9e1630 100644 --- a/.gitignore +++ b/.gitignore @@ -108,4 +108,6 @@ SilverMUDServer SilverMUDClient config.h config.h.in -stamp-h1 \ No newline at end of file +stamp-h1 + +build/ \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 37985b6..f239522 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,7 @@ bin_PROGRAMS = SilverMUDServer SilverMUDClient dist_doc_DATA = README.org +schemedir = $(pkgdatadir)/scheme +dist_scheme_DATA = lisp SilverMUDServer_CFLAGS = -lgnutls -g $(GUILE_CFLAGS) $(GUILE_LIBS) SilverMUDClient_CFLAGS = -lgnutls -g -lncurses $(GUILE_CFLAGS) $(GUILE_LIBS) diff --git a/configure.ac b/configure.ac index 1151bc2..9e4bdeb 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,6 @@ AC_INIT([SilverMUD], [0.0.1], [barra@ocathain.ie]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) AC_PROG_CC AC_CONFIG_HEADERS([source/config.h]) -AC_CONFIG_FILES([ - Makefile -]) +AC_CONFIG_FILES([Makefile]) PKG_CHECK_MODULES([GUILE], [guile-3.0]) AC_OUTPUT diff --git a/source/server/main.c b/source/server/main.c index fdc4475..50ca891 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -110,6 +110,10 @@ int main (int argc, char ** argv) scm_c_define_gsubr("push-output-message", 6, 0, 0, &push_output_message); scm_c_define("*globalPlayerList*", scm_from_pointer(globalPlayerList, NULL)); scm_c_define("*globalOutputQueue*", scm_from_pointer(globalOutputQueue, NULL)); + + // Start an output thread: + pthread_t outputThread; + pthread_create(&outputThread, NULL, outputThreadHandler, (void *)globalOutputQueue); // Start a REPL thread: //pthread_t schemeREPLThread; @@ -236,28 +240,6 @@ int main (int argc, char ** argv) } } } - - // Output the queue: - struct OutputMessage * currentMessage = NULL; - while (globalOutputQueue->count != 0) - { - currentMessage = popOutputMessage(globalOutputQueue); - struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; - - while (currentPlayerNode != NULL) - { - gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, - currentMessage->message, sizeof(struct ServerToClientMessage)); - currentPlayerNode = currentPlayerNode->next; - } - - if (currentMessage->deallocatePlayerList == true) - { - deallocatePlayerList(¤tMessage->recepients); - } - - deallocateOutputMessage(¤tMessage); - } } // Wait for all other threads to terminate: diff --git a/source/server/output-queue.c b/source/server/output-queue.c index 906638f..5e565f9 100644 --- a/source/server/output-queue.c +++ b/source/server/output-queue.c @@ -6,8 +6,39 @@ #include #include #include +#include "player-data.h" #include "output-queue.h" +// A thread handler for constantly outputting messages from an output queue: +void * outputThreadHandler(void * outputQueue) +{ + struct OutputQueue * queue = (struct OutputQueue *)outputQueue; + struct OutputMessage * currentMessage = NULL; + + while (true) + { + currentMessage = popOutputMessage(queue); + if (currentMessage != NULL) + { + struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; + + while (currentPlayerNode != NULL) + { + gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, + currentMessage->message, sizeof(struct ServerToClientMessage)); + currentPlayerNode = currentPlayerNode->next; + } + + if (currentMessage->deallocatePlayerList == true) + { + deallocatePlayerList(¤tMessage->recepients); + } + + deallocateOutputMessage(¤tMessage); + } + } +} + struct OutputQueue * const createOutputQueue() { // Allocate a new queue: diff --git a/source/server/output-queue.h b/source/server/output-queue.h index b988ba8..ebe7d51 100644 --- a/source/server/output-queue.h +++ b/source/server/output-queue.h @@ -28,6 +28,8 @@ struct OutputQueue struct OutputMessage * front, * back; }; +void * outputThreadHandler(void * outputQueue); + struct OutputQueue * const createOutputQueue(); size_t pushOutputMessage(struct OutputQueue * const queue, From 3309c034c4cd2088c18d5a9483cfb8f8656d98f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Mon, 20 Nov 2023 21:26:21 +0000 Subject: [PATCH 4/9] Update output queue to not pin the CPU (proper waiting) --- source/server/output-queue.c | 42 +++++++++++++++++++++--------------- source/server/output-queue.h | 2 ++ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/source/server/output-queue.c b/source/server/output-queue.c index 5e565f9..ba97b69 100644 --- a/source/server/output-queue.c +++ b/source/server/output-queue.c @@ -17,25 +17,30 @@ void * outputThreadHandler(void * outputQueue) while (true) { - currentMessage = popOutputMessage(queue); - if (currentMessage != NULL) + if (queue->count == 0) + { + pthread_cond_wait(&queue->updated, &queue->waitMutex); + } + + currentMessage = popOutputMessage(queue); + if (currentMessage != NULL) + { + struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; + + while (currentPlayerNode != NULL) { - struct PlayerListNode * currentPlayerNode = currentMessage->recepients->head; - - while (currentPlayerNode != NULL) - { - gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, - currentMessage->message, sizeof(struct ServerToClientMessage)); - currentPlayerNode = currentPlayerNode->next; - } - - if (currentMessage->deallocatePlayerList == true) - { - deallocatePlayerList(¤tMessage->recepients); - } - - deallocateOutputMessage(¤tMessage); + gnutls_record_send(*currentPlayerNode->player->connection->tlsSession, + currentMessage->message, sizeof(struct ServerToClientMessage)); + currentPlayerNode = currentPlayerNode->next; } + + if (currentMessage->deallocatePlayerList == true) + { + deallocatePlayerList(¤tMessage->recepients); + } + + deallocateOutputMessage(¤tMessage); + } } } @@ -46,6 +51,7 @@ struct OutputQueue * const createOutputQueue() // Initialize it: pthread_mutex_init(&newQueue->mutex, NULL); + pthread_cond_init(&newQueue->updated, NULL); newQueue->count = 0; newQueue->front = NULL; newQueue->back = NULL; @@ -101,6 +107,8 @@ size_t pushOutputMessage(struct OutputQueue * const queue, // Leaving critical section - Unlock the queue: pthread_mutex_unlock(&queue->mutex); + pthread_cond_signal(&queue->updated); + return queue->count; } diff --git a/source/server/output-queue.h b/source/server/output-queue.h index ebe7d51..2af4768 100644 --- a/source/server/output-queue.h +++ b/source/server/output-queue.h @@ -24,6 +24,8 @@ struct OutputMessage struct OutputQueue { pthread_mutex_t mutex; + pthread_mutex_t waitMutex; + pthread_cond_t updated; size_t count; struct OutputMessage * front, * back; }; From 4fa677c09a7925f54d06f4363c17ad1d165211ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Mon, 20 Nov 2023 21:30:30 +0000 Subject: [PATCH 5/9] Update C Codebase to use modules for Scheme primitives --- source/server/main.c | 20 ++++++++++++-------- source/server/scheme-integration.c | 12 +++++++++++- source/server/scheme-integration.h | 8 ++++++++ 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/source/server/main.c b/source/server/main.c index 50ca891..a7b5d5f 100644 --- a/source/server/main.c +++ b/source/server/main.c @@ -37,11 +37,6 @@ int main (int argc, char ** argv) // Initialize Scheme: scm_init_guile(); - // Start the REPL server on a UNIX socket: - scm_c_eval_string("(begin (use-modules (system repl server))" - "(if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))" - "(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); - // Create a socket to listen for connections on: int masterSocket = socket(AF_INET, SOCK_STREAM, 0); if (masterSocket < 0) @@ -107,10 +102,19 @@ int main (int argc, char ** argv) struct PlayerList * globalPlayerList = createPlayerList(); struct OutputQueue * globalOutputQueue = createOutputQueue(); - scm_c_define_gsubr("push-output-message", 6, 0, 0, &push_output_message); - scm_c_define("*globalPlayerList*", scm_from_pointer(globalPlayerList, NULL)); - scm_c_define("*globalOutputQueue*", scm_from_pointer(globalOutputQueue, NULL)); + // Define a module for use in the REPL containing our needed primitives: + SchemeModulePointers schemePointers; + schemePointers.globalPlayerList = globalPlayerList; + schemePointers.globalOutputQueue = globalOutputQueue; + + scm_c_define_module("silvermud primitives", initialize_silvermud_primitives, &schemePointers); + scm_c_use_module("silvermud primitives"); + // Start the REPL server on a UNIX socket: + scm_c_eval_string("(begin (use-modules (system repl server))" + "(if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))" + "(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); + // Start an output thread: pthread_t outputThread; pthread_create(&outputThread, NULL, outputThreadHandler, (void *)globalOutputQueue); diff --git a/source/server/scheme-integration.c b/source/server/scheme-integration.c index ce61391..de95aa2 100644 --- a/source/server/scheme-integration.c +++ b/source/server/scheme-integration.c @@ -8,8 +8,18 @@ #include "../messages.h" #include "output-queue.h" +#include "scheme-integration.h" -SCM scheme_get_player_by_name(SCM name, SCM queue); +void initialize_silvermud_primitives (void * gameState) +{ + SchemeModulePointers * pointers = (SchemeModulePointers *)gameState; + scm_c_define_gsubr("push-output-message", 6, 0, 0, &push_output_message); + scm_c_define("*global-player-list*", scm_from_pointer(pointers->globalPlayerList, NULL)); + scm_c_define("*global-output-queue*", scm_from_pointer(pointers->globalOutputQueue, NULL)); + scm_c_export("push-output-message", "*global-player-list*", "*global-output-queue*", NULL); +} + +//SCM scheme_get_player_by_name(SCM name, SCM queue) SCM push_output_message(SCM queue, SCM deallocate_list, SCM recepients, SCM type, SCM name, SCM content) { diff --git a/source/server/scheme-integration.h b/source/server/scheme-integration.h index 41accfc..64d396a 100644 --- a/source/server/scheme-integration.h +++ b/source/server/scheme-integration.h @@ -6,6 +6,14 @@ #ifndef SCHEME_INTEGRATION_H #define SCHEME_INTEGRATION_H +typedef struct SchemeModulePointers +{ + struct PlayerList * globalPlayerList; + struct OutputQueue * globalOutputQueue; +} SchemeModulePointers; + +void initialize_silvermud_primitives (void * gameState); + SCM scheme_get_player_by_name(SCM name); SCM push_output_message(SCM queue, SCM deallocate_list, SCM recepients, SCM type, SCM name, SCM content); From 3873192547f6544d9d58d143d62d7c0735401116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Mon, 20 Nov 2023 21:32:53 +0000 Subject: [PATCH 6/9] Basic Scheme implementations of player structures and messaging. --- lisp/messaging.scm | 18 ++++++++++++ lisp/structures.scm | 67 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 lisp/messaging.scm create mode 100644 lisp/structures.scm diff --git a/lisp/messaging.scm b/lisp/messaging.scm new file mode 100644 index 0000000..fdf7dc4 --- /dev/null +++ b/lisp/messaging.scm @@ -0,0 +1,18 @@ +;;;; This file is part of SilverMUD. +;;;; structures.scm defines various variables and functions used for interacting with the player's +;;;; via chat output from the Scheme enviroment of SilverMUD. +(define-module (silvermud messaging)) +(use-modules (silvermud primitives)) + +(define message-everyone (lambda (name content) + (push-output-message *global-output-queue* #f *global-player-list* + 8 name content))) +(define system-message (lambda (contennt) + (push-output-message *global-output-queue* #f *global-player-list* + 0 "" content))) + +(define message-expression (lambda (expression) + (system-message (format #f "~a" expression)))) + +;; Export everything! +(export message-everyone system-message message-expression) diff --git a/lisp/structures.scm b/lisp/structures.scm new file mode 100644 index 0000000..cc45cc1 --- /dev/null +++ b/lisp/structures.scm @@ -0,0 +1,67 @@ +;;;; This file is part of SilverMUD. +;;;; structures.scm defines various variables and functions used for interacting with C structures +;;;; from the Scheme enviroment of SilverMUD. +(define-module (silvermud structures)) +(use-modules (system foreign) + (silvermud primitives)) + +;;; struct PlayerList: + +;; The layout of the struct PlayerList: +(define *player-list-structure* (list size_t '* '*)) + +;; Pretty-format the player list header: +(define (player-list-header->string player-list-pointer) + "Format a struct PlayerList pointer into a string." + (if (not (null-pointer? player-list-pointer)) + (let ((structure (parse-c-struct player-list-pointer *player-list-structure*))) + (format #f + "Players in list: ~d.\nHead: ~a. \nTail: ~a.\n" + (list-ref structure 0) (list-ref structure 1) (list-ref structure 2))))) + +;; Create a list of strings representing all players in a list: +(define (list-players player-list-pointer) + "List all players in a given C PlayerList as a list of strings." + (if (not (null-pointer? player-list-pointer)) ; Check we're not dereferencing a null pointer. + (build-list-players + (list-ref (parse-c-struct player-list-pointer *player-list-node-structure*) 2)))) + +(define (build-list-players pointer) + (if (not (null-pointer? pointer)) + (let* ((node (parse-c-struct pointer *player-list-node-structure*)) + (player (parse-c-struct (list-ref node 0) *player-structure*))) + (cons (pointer->string (list-ref player 1)) (build-list-players (list-ref node 1)))) + '())) + +;;; struct PlayerListNode: + +;; Used to interact with struct PlayerListNode: +(define *player-list-node-structure* (list '* '* '*)) + +;; Pretty-format the player list node: +(define (player-list-node->string player-list-node-pointer) + "Format a struct PlayerListNode pointer into a string." + (if (not (null-pointer? player-list-node-pointer)) + (let ((structure (parse-c-struct player-list-node-pointer *player-list-node-structure*))) + (format #f + "Player pointer: ~a.\nNext: ~a. \nPrevious: ~a.\n" + (list-ref structure 0) (list-ref structure 1) (list-ref structure 2))))) + +;;; struct Player: + +; Used to interact with struct Player: +(define *player-structure* (list '* '*)) + +(define (player->string player-info-pointer) + "Format a struct Player pointer into a string." + (if (not (null-pointer? player-info-pointer)) + (let ((structure (parse-c-struct player-info-pointer *player-structure*))) + (display (null-pointer? (list-ref structure 1))) + (format #f + "Player Name: ~a\n" (if (null-pointer? (list-ref structure 1)) + (pointer->bytevector (list-ref structure 1) 64) + #f))))) + +;; Export everything! +(export *player-list-structure* *player-list-node-structure* *player-structure* + player->string player-list-header->string player-list-node->string list-players) From 30e63d36b5911b20723410001ff4d7d34b6ccce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Mon, 20 Nov 2023 21:33:45 +0000 Subject: [PATCH 7/9] Changed player names to allocated strings to be friendly to Scheme! --- source/server/player-data.c | 2 ++ source/server/player-data.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/source/server/player-data.c b/source/server/player-data.c index c3d530d..923546d 100644 --- a/source/server/player-data.c +++ b/source/server/player-data.c @@ -31,6 +31,7 @@ struct Player * createNewPlayer(struct ClientConnection * connection) { struct Player * newPlayer = calloc(1, sizeof(struct Player)); newPlayer->connection = connection; + newPlayer->name = calloc(PLAYER_NAME_LENGTH, sizeof(char)); return newPlayer; } @@ -38,6 +39,7 @@ struct Player * createNewPlayer(struct ClientConnection * connection) // Deallocates a player: void deallocatePlayer(struct Player ** player) { + free((*player)->name); free(*player); *player = NULL; } diff --git a/source/server/player-data.h b/source/server/player-data.h index d908e5e..7547512 100644 --- a/source/server/player-data.h +++ b/source/server/player-data.h @@ -5,6 +5,7 @@ // ========================================= #ifndef PLAYER_DATA_H #define PLAYER_DATA_H +#define PLAYER_NAME_LENGTH 64 #include #include "connections.h" @@ -14,7 +15,7 @@ struct Player { struct ClientConnection * connection; - char name[64]; + char * name; }; // Functions: From 3c8ed9994bf68b8ebd547165e2f0f923122e9c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Mon, 20 Nov 2023 21:34:06 +0000 Subject: [PATCH 8/9] Removed now unneeded placeholder. --- lisp/.placeholder | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lisp/.placeholder diff --git a/lisp/.placeholder b/lisp/.placeholder deleted file mode 100644 index e69de29..0000000 From 7fa1fadc12f792f7218694f471f12581d8762b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barra=20=C3=93=20Cath=C3=A1in?= Date: Tue, 21 Nov 2023 10:51:38 +0000 Subject: [PATCH 9/9] Accidental typo while moving around. --- lisp/messaging.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/messaging.scm b/lisp/messaging.scm index fdf7dc4..2085bd8 100644 --- a/lisp/messaging.scm +++ b/lisp/messaging.scm @@ -7,7 +7,7 @@ (define message-everyone (lambda (name content) (push-output-message *global-output-queue* #f *global-player-list* 8 name content))) -(define system-message (lambda (contennt) +(define system-message (lambda (content) (push-output-message *global-output-queue* #f *global-player-list* 0 "" content)))