Compare commits
53 Commits
Author | SHA1 | Date |
---|---|---|
Barra Ó Catháin | 19fa527d73 | |
Barra Ó Catháin | bbcde81eaf | |
Barra Ó Catháin | ea76226760 | |
Barra Ó Catháin | 9b88d092c3 | |
Barra Ó Catháin | 80dce61058 | |
Barra Ó Catháin | ef3cfb69c2 | |
Barra Ó Catháin | 06b7f66f20 | |
Barra Ó Catháin | cbec016039 | |
Barra Ó Catháin | 9562cbfd21 | |
Barra Ó Catháin | ce9f197a83 | |
Barra Ó Catháin | 060c637c08 | |
Barra Ó Catháin | c282fb20ad | |
Barra Ó Catháin | aa382fefc2 | |
Barra Ó Catháin | fa3df0cc69 | |
Barra Ó Catháin | 4d13547ae6 | |
Barra Ó Catháin | 751a734016 | |
Barra Ó Catháin | 258fd49653 | |
Barra Ó Catháin | 89d520dc15 | |
Barra Ó Catháin | 6fc2e4d2b9 | |
Barra Ó Catháin | 0a2d03fdaa | |
Barra Ó Catháin | 9660fd4c60 | |
Barra Ó Catháin | 2d6b194c26 | |
Barra Ó Catháin | 24f8e2688a | |
Barra Ó Catháin | 03ea201716 | |
Barra Ó Catháin | 7fa1fadc12 | |
Barra Ó Catháin | 3c8ed9994b | |
Barra Ó Catháin | 30e63d36b5 | |
Barra Ó Catháin | 3873192547 | |
Barra Ó Catháin | 4fa677c09a | |
Barra Ó Catháin | 3309c034c4 | |
Barra Ó Catháin | 2e813ae29c | |
Barra Ó Catháin | e11a7b3a76 | |
Barra Ó Catháin | 2acbe5e19b | |
Barra Ó Catháin | 442a9319e8 | |
Barra Ó Catháin | a66a07c897 | |
Barra Ó Catháin | 81fc72a1d7 | |
Barra Ó Catháin | a1b1b80449 | |
Barra Ó Catháin | 54b613befe | |
Barra Ó Catháin | 5a53e9f197 | |
Barra Ó Catháin | 3fc75ef30f | |
Barra Ó Catháin | b292966588 | |
Barra Ó Catháin | 50dcddfc56 | |
Barra Ó Catháin | c043da64a2 | |
Barra Ó Catháin | 0104a11a7e | |
Barra Ó Catháin | 080e46fe99 | |
Barra Ó Catháin | 0814e437cd | |
Barra Ó Catháin | 9801be3622 | |
Barra Ó Catháin | e2ef744e87 | |
Barra Ó Catháin | 8b0920c35d | |
Barra Ó Catháin | 6ed532c368 | |
Barra Ó Catháin | 48f0858735 | |
Barra Ó Catháin | 32503cdbca | |
Barra Ó Catháin | 2b488477f5 |
|
@ -49,4 +49,69 @@
|
|||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
dkms.conf
|
||||
|
||||
# http://www.gnu.org/software/automake
|
||||
|
||||
Makefile.in
|
||||
/ar-lib
|
||||
/mdate-sh
|
||||
/py-compile
|
||||
/test-driver
|
||||
/ylwrap
|
||||
.deps/
|
||||
.dirstamp
|
||||
|
||||
# http://www.gnu.org/software/autoconf
|
||||
|
||||
autom4te.cache
|
||||
/autoscan.log
|
||||
/autoscan-*.log
|
||||
/aclocal.m4
|
||||
/compile
|
||||
/config.cache
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.log
|
||||
/config.status
|
||||
/config.sub
|
||||
/configure
|
||||
/configure.scan
|
||||
/depcomp
|
||||
/install-sh
|
||||
/missing
|
||||
/stamp-h1
|
||||
|
||||
# https://www.gnu.org/software/libtool/
|
||||
|
||||
/ltmain.sh
|
||||
|
||||
# http://www.gnu.org/software/texinfo
|
||||
|
||||
/texinfo.tex
|
||||
|
||||
# http://www.gnu.org/software/m4/
|
||||
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
|
||||
# Generated Makefile
|
||||
# (meta build system like autotools,
|
||||
# can automatically generate from config.status script
|
||||
# (which is called by configure script))
|
||||
Makefile
|
||||
|
||||
SilverMUDServer
|
||||
SilverMUDClient
|
||||
config.h
|
||||
config.h.in
|
||||
stamp-h1
|
||||
|
||||
build/
|
||||
|
||||
# Tests:
|
||||
*.log
|
||||
*_test*
|
|
@ -0,0 +1,27 @@
|
|||
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)
|
||||
|
||||
SilverMUDServer_SOURCES = \
|
||||
source/messages.c \
|
||||
source/server/player-data.c \
|
||||
source/server/connections.c \
|
||||
source/server/scheme-integration.c \
|
||||
source/server/output-queue.c \
|
||||
source/server/main.c
|
||||
|
||||
SilverMUDClient_SOURCES = \
|
||||
source/messages.c \
|
||||
source/client/client-drawing.c \
|
||||
source/client/receiving-thread.c \
|
||||
source/client/main.c
|
||||
|
||||
check_PROGRAMS = lists_test
|
||||
lists_test_SOURCES = \
|
||||
source/tests/lists-test.c \
|
||||
source/server/lists.c
|
||||
|
||||
TESTS = $(check_PROGRAMS)
|
|
@ -0,0 +1,7 @@
|
|||
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])
|
||||
PKG_CHECK_MODULES([GUILE], [guile-3.0])
|
||||
AC_OUTPUT
|
|
@ -0,0 +1,9 @@
|
|||
#+TITLE: SilverMUD: Gamemaster's Guidebook
|
||||
#+SUBTITLE: SilverMUD First Edition
|
||||
#+AUTHOR: Barra Ó Catháin
|
||||
|
||||
* How To Run SilverMUD, The Software:
|
||||
|
||||
* How To Run SilverMUD, The Game:
|
||||
|
||||
* Creating Content:
|
|
@ -0,0 +1,15 @@
|
|||
#+TITLE: SilverMUD: Player's Guidebook
|
||||
#+SUBTITLE: SilverMUD First Edition
|
||||
#+AUTHOR: Barra Ó Catháin
|
||||
|
||||
* Getting Started:
|
||||
|
||||
* Basic Commands:
|
||||
|
||||
* Interacting With The World:
|
||||
|
||||
* The Character System:
|
||||
|
||||
* The Combat System:
|
||||
|
||||
* Commands In Depth:
|
|
@ -0,0 +1,10 @@
|
|||
#+TITLE: SilverMUD: Programmer's Guidebook
|
||||
#+SUBTITLE: SilverMUD First Edition
|
||||
#+AUTHOR: Barra Ó Catháin
|
||||
|
||||
* The Scheme Programming Language:
|
||||
|
||||
* The Basic Concepts Of SilverMUD Programming:
|
||||
|
||||
* Concepts In Detail:
|
||||
|
|
@ -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 (content)
|
||||
(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)
|
|
@ -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)
|
|
@ -0,0 +1,28 @@
|
|||
#+TITLE: SilverMUD Implementation Document
|
||||
This document contains information about various implementation details of
|
||||
SilverMUD, as a scratchpad for decisions before implementation.
|
||||
|
||||
* Structures:
|
||||
** Server->Client Message Format (1409 bytes total):
|
||||
*** Message Type - Unsigned 8 Bit Integer:
|
||||
|-------+------------------------|
|
||||
| Value | Purpose |
|
||||
|-------+------------------------|
|
||||
| 0 | System Message |
|
||||
| 1 | Client Setting Message |
|
||||
| 2 | Command Output Message |
|
||||
| 3 | Local Chat Message |
|
||||
| 4 | Player Chat Message |
|
||||
| 5 | Party Chat Message |
|
||||
| 6 | Player Emote Message |
|
||||
|-------+------------------------|
|
||||
|
||||
Further values remain open for possible additional message types.
|
||||
|
||||
*** Sender Name - 128 Character String.
|
||||
This field may be repurposed for message types without a need for a name.
|
||||
|
||||
*** Message Content - 1024 Character String.
|
||||
|
||||
** Client->Server Message Format (1024 bytes total):
|
||||
*** Message Content - 1024 Character String.
|
|
@ -0,0 +1,61 @@
|
|||
// ==========================================
|
||||
// | SilverMUD Client - client-drawing.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ==========================================
|
||||
#include <config.h>
|
||||
#include "client-drawing.h"
|
||||
|
||||
void redrawClientLayout(WINDOW * gameWindow, WINDOW * chatWindow, WINDOW * inputWindow)
|
||||
{
|
||||
int height, width;
|
||||
getmaxyx(stdscr, height, width);
|
||||
|
||||
// Draw the lines that will seperate windows:
|
||||
wborder(stdscr, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
|
||||
attron(A_REVERSE);
|
||||
mvwhline(stdscr, 0, 0, '=', width);
|
||||
mvwhline(stdscr, height / 2, 0, '=', width);
|
||||
mvwhline(stdscr, height - 2, 0, '=', width);
|
||||
|
||||
// Write the labels for windows:
|
||||
attron(COLOR_PAIR(1));
|
||||
mvwprintw(stdscr, 0, 1, " SilverMUD | Version %s ", PACKAGE_VERSION);
|
||||
mvwprintw(stdscr, height / 2, 1, " Chat ");
|
||||
mvwprintw(stdscr, height - 2, 1, " Input ");
|
||||
attroff(COLOR_PAIR(1));
|
||||
attroff(A_REVERSE);
|
||||
|
||||
// Move the windows into place:
|
||||
mvwin(gameWindow, 1, 1);
|
||||
mvwin(chatWindow, (height / 2) + 1 , 1);
|
||||
mvwin(inputWindow, height - 1, 1);
|
||||
|
||||
// Resize the windows:
|
||||
wresize(gameWindow, (height - 2) / 2, width - 2);
|
||||
wresize(chatWindow, ((height - 4) / 2) - (1 - (height % 2)), width - 2);
|
||||
wresize(inputWindow, 1, width - 2);
|
||||
|
||||
// Refresh every window:
|
||||
wrefresh(stdscr);
|
||||
wrefresh(gameWindow);
|
||||
wrefresh(chatWindow);
|
||||
wrefresh(inputWindow);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// | End of client-drawing.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,28 @@
|
|||
// ==========================================
|
||||
// | SilverMUD Client - client-drawing.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ==========================================
|
||||
#ifndef CLIENT_DRAWING_H
|
||||
#define CLIENT_DRAWING_H
|
||||
#include <ncurses.h>
|
||||
|
||||
void redrawClientLayout(WINDOW * gameWindow, WINDOW * chatWindow, WINDOW * inputWindow);
|
||||
|
||||
#endif
|
||||
// ========================================================
|
||||
// | End of client-drawing.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,206 @@
|
|||
// =========================================
|
||||
// | SilverMUD Client - main.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <ncurses.h>
|
||||
#include <stdbool.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../messages.h"
|
||||
#include "client-drawing.h"
|
||||
#include "receiving-thread.h"
|
||||
|
||||
static char serverPort[HOST_NAME_MAX] = "5050";
|
||||
static char serverHostname[HOST_NAME_MAX] = "127.0.0.1";
|
||||
static bool hostSpecified = false, portSpecified = false;
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
// Print a welcome message:
|
||||
printf("SilverMUD Client - Starting Now.\n"
|
||||
"================================\n");
|
||||
|
||||
struct addrinfo * serverInformation;
|
||||
|
||||
// Configure command-line options:
|
||||
static struct option longOptions[] =
|
||||
{
|
||||
{"host", required_argument, 0, 'h' },
|
||||
{"port", required_argument, 0, 'p' }
|
||||
};
|
||||
|
||||
// Parse command-line options:
|
||||
int selectedOption = 0, optionIndex = 0;
|
||||
|
||||
while ((selectedOption = getopt_long(argc, argv, "h:p:", longOptions, &optionIndex)) != -1)
|
||||
{
|
||||
switch (selectedOption)
|
||||
{
|
||||
case 'h':
|
||||
{
|
||||
printf("Connecting to host: %s\n", optarg);
|
||||
hostSpecified = true;
|
||||
strncpy(serverHostname, optarg, HOST_NAME_MAX);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
printf("Connecting to port: %s\n", optarg);
|
||||
portSpecified = true;
|
||||
strncpy(serverPort, optarg, HOST_NAME_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a socket for communicating with the server:
|
||||
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (serverSocket == -1)
|
||||
{
|
||||
printf("Socket creation failed. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Set up the server address structure to point to the server:
|
||||
if (getaddrinfo(serverHostname, serverPort, NULL, &serverInformation) != 0)
|
||||
{
|
||||
printf("Server lookup failed. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Connect to the server, iterating through addresses until we get SilverMUD:
|
||||
struct addrinfo * currentAddress;
|
||||
for (currentAddress = serverInformation; currentAddress != NULL; currentAddress = currentAddress->ai_next)
|
||||
{
|
||||
if (connect(serverSocket, serverInformation->ai_addr, serverInformation->ai_addrlen) != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (currentAddress == NULL)
|
||||
{
|
||||
printf("Failed to connect to the server. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
freeaddrinfo(serverInformation);
|
||||
|
||||
// Set up a GnuTLS session and handshake with the server:
|
||||
gnutls_session_t tlsSession = NULL;
|
||||
if (gnutls_init(&tlsSession, GNUTLS_CLIENT) < 0)
|
||||
{
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
gnutls_anon_client_credentials_t clientKey = NULL;
|
||||
gnutls_anon_allocate_client_credentials(&clientKey);
|
||||
gnutls_transport_set_int(tlsSession, serverSocket);
|
||||
gnutls_credentials_set(tlsSession, GNUTLS_CRD_ANON, &clientKey);
|
||||
gnutls_handshake_set_timeout(tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
gnutls_priority_set_direct(tlsSession, "PERFORMANCE:+ANON-ECDH:+ANON-DH", NULL);
|
||||
gnutls_server_name_set(tlsSession, GNUTLS_NAME_DNS, serverHostname, strlen(serverHostname));
|
||||
|
||||
int returnValue = -1, connectionAttempts = 0;
|
||||
do
|
||||
{
|
||||
returnValue = gnutls_handshake(tlsSession);
|
||||
connectionAttempts++;
|
||||
if (connectionAttempts == 50)
|
||||
{
|
||||
printf("Failed to establish a TLS session. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
while (returnValue < 0 && gnutls_error_is_fatal(returnValue) == 0);
|
||||
|
||||
if (returnValue < 0)
|
||||
{
|
||||
printf("Failed to establish a TLS session. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Initialize ncurses:
|
||||
initscr();
|
||||
keypad(stdscr, TRUE);
|
||||
|
||||
if (!has_colors())
|
||||
{
|
||||
endwin();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Enable colours:
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_GREEN, -1);
|
||||
init_pair(2, COLOR_YELLOW, -1);
|
||||
init_pair(3, COLOR_RED, -1);
|
||||
init_pair(4, COLOR_BLUE, -1);
|
||||
init_pair(5, COLOR_CYAN, -1);
|
||||
init_pair(6, COLOR_MAGENTA, -1);
|
||||
|
||||
// Variables needed for the main loop:
|
||||
int height, width;
|
||||
getmaxyx(stdscr, height, width);
|
||||
struct ClientToServerMessage message;
|
||||
|
||||
WINDOW * chatWindow, * gameWindow, * inputWindow;
|
||||
inputWindow = newwin(1, width - 2, height - 1, 1);
|
||||
gameWindow = newwin((height / 2) - 1, width - 2, 1, 1);
|
||||
chatWindow = newwin((height / 2) - 3, width - 2, (height / 2) + 1, 1);
|
||||
|
||||
scrollok(gameWindow, TRUE);
|
||||
scrollok(chatWindow, TRUE);
|
||||
scrollok(inputWindow, TRUE);
|
||||
|
||||
redrawClientLayout(gameWindow, chatWindow, inputWindow);
|
||||
|
||||
struct ReceivingThreadArguments receivingThreadArguments;
|
||||
receivingThreadArguments.chatWindow = chatWindow;
|
||||
receivingThreadArguments.gameWindow = gameWindow;
|
||||
receivingThreadArguments.inputWindow = inputWindow;
|
||||
receivingThreadArguments.session = tlsSession;
|
||||
|
||||
pthread_t receivingThread;
|
||||
pthread_create(&receivingThread, NULL, receivingThreadHandler, &receivingThreadArguments);
|
||||
|
||||
while (true)
|
||||
{
|
||||
wgetnstr(inputWindow, message.content, 1024);
|
||||
|
||||
if (message.content[0] != '\0')
|
||||
{
|
||||
gnutls_record_send(tlsSession, &message, 1024);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a successful status code to the operating system:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// | End of main.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,93 @@
|
|||
// ==========================================
|
||||
// | SilverMUD Client - receiving-thread.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ==========================================
|
||||
#include <ncurses.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "../messages.h"
|
||||
#include "client-drawing.h"
|
||||
#include "receiving-thread.h"
|
||||
|
||||
void * receivingThreadHandler(void * threadArguments)
|
||||
{
|
||||
start_color();
|
||||
use_default_colors();
|
||||
init_pair(1, COLOR_GREEN, -1);
|
||||
init_pair(2, COLOR_YELLOW, -1);
|
||||
init_pair(3, COLOR_RED, -1);
|
||||
init_pair(4, COLOR_BLUE, -1);
|
||||
init_pair(5, COLOR_CYAN, -1);
|
||||
init_pair(6, COLOR_MAGENTA, -1);
|
||||
|
||||
// Unpack the thread's arguments:
|
||||
gnutls_session_t session = ((struct ReceivingThreadArguments *)threadArguments)->session;
|
||||
WINDOW * chatWindow = ((struct ReceivingThreadArguments *)threadArguments)->chatWindow,
|
||||
* gameWindow = ((struct ReceivingThreadArguments *)threadArguments)->gameWindow,
|
||||
* inputWindow = ((struct ReceivingThreadArguments *)threadArguments)->inputWindow;
|
||||
|
||||
// Print a message into the game window:
|
||||
wprintw(gameWindow, "Connection successful. Welcome to ");
|
||||
wattrset(gameWindow, COLOR_PAIR(2));
|
||||
wprintw(gameWindow, "SilverMUD!\n");
|
||||
wattrset(gameWindow, A_NORMAL);
|
||||
|
||||
struct ServerToClientMessage currentMessage;
|
||||
int returnValue = 0;
|
||||
while (true)
|
||||
{
|
||||
returnValue = gnutls_record_recv(session, ¤tMessage, sizeof(struct ServerToClientMessage));
|
||||
|
||||
if (gnutls_error_is_fatal(returnValue))
|
||||
{
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
switch (currentMessage.type)
|
||||
{
|
||||
case SYSTEM:
|
||||
{
|
||||
wattrset(gameWindow, A_BOLD);
|
||||
wprintw(gameWindow, "%s\n", currentMessage.content);
|
||||
wattrset(gameWindow, A_NORMAL);
|
||||
break;
|
||||
}
|
||||
case LOCAL_CHAT:
|
||||
{
|
||||
wattrset(chatWindow, A_BOLD);
|
||||
wprintw(chatWindow, "<%s>: ", currentMessage.name);
|
||||
wattrset(chatWindow, A_NORMAL);
|
||||
wprintw(chatWindow, "%s\n", currentMessage.content);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
wattrset(chatWindow, A_BOLD);
|
||||
wprintw(chatWindow, "<%s>: ", currentMessage.name);
|
||||
wattrset(chatWindow, A_NORMAL);
|
||||
wprintw(chatWindow, "%s\n", currentMessage.content);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
redrawClientLayout(gameWindow, chatWindow, inputWindow);
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// | End of receiving-thread.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,35 @@
|
|||
// ==========================================
|
||||
// | SilverMUD Client - receiving-thread.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ==========================================
|
||||
#ifndef RECEIVING_THREAD_H
|
||||
#define RECEIVING_THREAD_H
|
||||
#include <ncurses.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
struct ReceivingThreadArguments
|
||||
{
|
||||
WINDOW * chatWindow, * gameWindow, * inputWindow;
|
||||
gnutls_session_t session;
|
||||
};
|
||||
|
||||
void * receivingThreadHandler(void * threadArguments);
|
||||
|
||||
#endif
|
||||
// ========================================================
|
||||
// | End of receiving-thread.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,113 @@
|
|||
// =========================================
|
||||
// | SilverMUD - messages.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libguile.h>
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
// Allocate and initialize a client to server message using the passed values:
|
||||
struct ClientToServerMessage * createClientToServerMessage(char * content)
|
||||
{
|
||||
// Allocate the needed memory for the message:
|
||||
struct ClientToServerMessage * newMessage = calloc(1, sizeof(struct ClientToServerMessage));
|
||||
|
||||
// Copy the string and terminate it:
|
||||
strncpy(newMessage->content, content, MESSAGE_CONTENT_LENGTH - 1);
|
||||
newMessage->content[MESSAGE_CONTENT_LENGTH - 1] = '\0';
|
||||
|
||||
// Return the pointer:
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
// A Scheme wrapper for creating client to server messages:
|
||||
SCM scheme_createClientToServerMessage(SCM content)
|
||||
{
|
||||
// Check that we have been provided the right Scheme type:
|
||||
if (scm_string_p(content))
|
||||
{
|
||||
// Convert the Scheme string to a C string:
|
||||
char * contentString = scm_to_locale_stringn(content, NULL);
|
||||
|
||||
// Create the message:
|
||||
struct ClientToServerMessage * message = createClientToServerMessage(contentString);
|
||||
|
||||
// Free the converted string:
|
||||
free(contentString);
|
||||
|
||||
// Return the pointer as a Scheme object:
|
||||
return scm_from_pointer(message, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate and initialize a server to client message using the passed values:
|
||||
struct ServerToClientMessage * createServerToClientMessage(uint8_t type, char * name, char * content)
|
||||
{
|
||||
// Allocate the needed memory for the message:
|
||||
struct ServerToClientMessage * newMessage = calloc(1, sizeof(struct ServerToClientMessage));
|
||||
|
||||
// Copy the type:
|
||||
newMessage->type = type;
|
||||
|
||||
// Copy the strings and terminate them:
|
||||
strncpy(newMessage->name, name, MESSAGE_NAME_LENGTH - 1);
|
||||
newMessage->name[MESSAGE_NAME_LENGTH - 1] = '\0';
|
||||
strncpy(newMessage->content, content, MESSAGE_CONTENT_LENGTH - 1);
|
||||
newMessage->content[MESSAGE_CONTENT_LENGTH - 1] = '\0';
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
// A Scheme wrapper for creating server to client messages:
|
||||
SCM scheme_createServerToClientMessage(SCM type, SCM name, SCM content)
|
||||
{
|
||||
// Check that we have been provided the right Scheme type:
|
||||
if (scm_integer_p(type) && scm_string_p(name) && scm_string_p(content))
|
||||
{
|
||||
// Convert the integer to a C integer:
|
||||
uint8_t typeInteger = scm_to_uint8(type);
|
||||
|
||||
// Convert the Scheme strings to C strings:
|
||||
char * nameString = scm_to_locale_stringn(name, NULL);
|
||||
char * contentString = scm_to_locale_stringn(content, NULL);
|
||||
|
||||
// Create the message:
|
||||
struct ServerToClientMessage * message =
|
||||
createServerToClientMessage(typeInteger, nameString, contentString);
|
||||
|
||||
// Free the converted string:
|
||||
free(nameString);
|
||||
free(contentString);
|
||||
|
||||
// Return the pointer as a Scheme object:
|
||||
return scm_from_pointer(message, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SCM_BOOL_F;
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================
|
||||
// | End of messages.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,58 @@
|
|||
// =========================================
|
||||
// | SilverMUD - messages.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef MESSAGES_H
|
||||
#define MESSAGES_H
|
||||
#include <libguile.h>
|
||||
#define MESSAGE_NAME_LENGTH 128
|
||||
#define MESSAGE_CONTENT_LENGTH 1024
|
||||
|
||||
enum MessageTypes
|
||||
{
|
||||
SYSTEM,
|
||||
CLIENT_SETTING,
|
||||
COMMAND_OUTPUT,
|
||||
LOCAL_CHAT,
|
||||
PLAYER_CHAT,
|
||||
PARTY_CHAT
|
||||
};
|
||||
|
||||
struct ClientToServerMessage
|
||||
{
|
||||
char content[MESSAGE_CONTENT_LENGTH];
|
||||
};
|
||||
|
||||
struct ServerToClientMessage
|
||||
{
|
||||
uint8_t type;
|
||||
char name[MESSAGE_NAME_LENGTH];
|
||||
char content[MESSAGE_CONTENT_LENGTH];
|
||||
};
|
||||
|
||||
// Allocate and initialize a client to server message using the passed values:
|
||||
struct ClientToServerMessage * createClientToServerMessage(char * content);
|
||||
SCM scheme_createClientToServerMessage(SCM content);
|
||||
|
||||
// Allocate and initialize a server to client message using the passed values:
|
||||
struct ServerToClientMessage * createServerToClientMessage(uint8_t type, char * name, char * content);
|
||||
SCM scheme_createServerToClientMessage(SCM type, SCM name, SCM content);
|
||||
|
||||
#endif
|
||||
// ================================================
|
||||
// | End of messages.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,205 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - connections.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <stdlib.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "connections.h"
|
||||
|
||||
static struct ClientConnectionNode * findMiddle(struct ClientConnectionNode * start, struct ClientConnectionNode * end)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
start = start->next;
|
||||
if(start == end)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
end = end->previous;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor)
|
||||
{
|
||||
struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end);
|
||||
while (start != end)
|
||||
{
|
||||
if (middle->connection->fileDescriptor == fileDescriptor)
|
||||
{
|
||||
return middle->connection;
|
||||
}
|
||||
else if (middle->connection->fileDescriptor > fileDescriptor)
|
||||
{
|
||||
end = middle->previous;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = middle->next;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
}
|
||||
if (start->connection->fileDescriptor == fileDescriptor)
|
||||
{
|
||||
return start->connection;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor)
|
||||
{
|
||||
struct ClientConnectionNode * start = list->head, * end = list->tail, * middle = findMiddle(start, end), * toDelete = NULL;
|
||||
|
||||
// Find the node that is to be deleted:
|
||||
while (start != end && toDelete == NULL)
|
||||
{
|
||||
if (middle->connection->fileDescriptor == fileDescriptor)
|
||||
{
|
||||
toDelete = middle;
|
||||
}
|
||||
else if (middle->connection->fileDescriptor > fileDescriptor)
|
||||
{
|
||||
end = middle->previous;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = middle->next;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
}
|
||||
if (start->connection->fileDescriptor == fileDescriptor)
|
||||
{
|
||||
toDelete = start;
|
||||
}
|
||||
|
||||
if (toDelete == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set the appropriate pointers on other nodes:
|
||||
if (toDelete->previous != NULL)
|
||||
{
|
||||
toDelete->previous->next = toDelete->next;
|
||||
}
|
||||
if (toDelete->next != NULL)
|
||||
{
|
||||
toDelete->next->previous = toDelete->previous;
|
||||
}
|
||||
|
||||
// Set the appropriate pointers on the list:
|
||||
if (list->head == toDelete)
|
||||
{
|
||||
list->head = toDelete->next;
|
||||
}
|
||||
if (list->tail == toDelete)
|
||||
{
|
||||
list->tail = toDelete->previous;
|
||||
}
|
||||
|
||||
|
||||
list->clientCount--;
|
||||
|
||||
// Free the connection:
|
||||
free(toDelete->connection->tlsSession);
|
||||
free(toDelete->connection);
|
||||
free(toDelete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ClientConnection * addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession)
|
||||
{
|
||||
// Allocate memory for the structures:
|
||||
struct ClientConnectionNode * newConnectionNode = calloc(1, sizeof(struct ClientConnectionNode));
|
||||
newConnectionNode->connection = calloc(1, sizeof(struct ClientConnection));
|
||||
|
||||
// Set the appropriate data in the structure:
|
||||
newConnectionNode->next = NULL;
|
||||
newConnectionNode->previous = NULL;
|
||||
newConnectionNode->connection->tlsSession = tlsSession;
|
||||
newConnectionNode->connection->fileDescriptor = fileDescriptor;
|
||||
|
||||
// If it's the first node in the list:
|
||||
if (list->head == NULL && list->tail == NULL)
|
||||
{
|
||||
list->head = newConnectionNode;
|
||||
list->tail = newConnectionNode;
|
||||
|
||||
list->clientCount++;
|
||||
|
||||
return newConnectionNode->connection;
|
||||
}
|
||||
|
||||
// Insert it in the appropriate place in the list:
|
||||
else
|
||||
{
|
||||
struct ClientConnectionNode * currentNode = list->head;
|
||||
|
||||
// Seek through the list until we find the appropriate spot to insert the new connection:
|
||||
while (currentNode->connection->fileDescriptor < fileDescriptor)
|
||||
{
|
||||
// If we've reached the end of the list:
|
||||
if (currentNode->next == NULL)
|
||||
{
|
||||
currentNode->next = newConnectionNode;
|
||||
newConnectionNode->previous = currentNode;
|
||||
list->tail = newConnectionNode;
|
||||
|
||||
list->clientCount++;
|
||||
|
||||
return newConnectionNode->connection;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
newConnectionNode->previous = currentNode->previous;
|
||||
newConnectionNode->next = currentNode;
|
||||
currentNode->previous = newConnectionNode;
|
||||
|
||||
if (newConnectionNode->previous == NULL)
|
||||
{
|
||||
list->head = newConnectionNode;
|
||||
}
|
||||
if (newConnectionNode->next == NULL)
|
||||
{
|
||||
list->tail = newConnectionNode;
|
||||
}
|
||||
list->clientCount++;
|
||||
|
||||
return newConnectionNode->connection;
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// | End of connections.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,56 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - connections.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef CONNECTIONS_H
|
||||
#define CONNECTIONS_H
|
||||
#include <stddef.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
struct ClientConnection
|
||||
{
|
||||
gnutls_session_t * tlsSession;
|
||||
struct Player * player;
|
||||
int fileDescriptor;
|
||||
};
|
||||
|
||||
struct ClientConnectionNode
|
||||
{
|
||||
struct ClientConnection * connection;
|
||||
struct ClientConnectionNode * next;
|
||||
struct ClientConnectionNode * previous;
|
||||
};
|
||||
|
||||
struct ClientConnectionList
|
||||
{
|
||||
size_t clientCount;
|
||||
struct ClientConnectionNode * head;
|
||||
struct ClientConnectionNode * tail;
|
||||
};
|
||||
|
||||
|
||||
//struct ClientConnection * findConnectionByPlayer(struct ClientConnectionList * list);
|
||||
struct ClientConnection * findConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor);
|
||||
struct ClientConnection * findConnectionByTlsSession(struct ClientConnectionList * list, gnutls_session_t * tlsSession);
|
||||
|
||||
int removeConnectionByFileDescriptor(struct ClientConnectionList * list, int fileDescriptor);
|
||||
struct ClientConnection * addNewConnection(struct ClientConnectionList * list, int fileDescriptor, gnutls_session_t * tlsSession);
|
||||
|
||||
#endif
|
||||
// ===================================================
|
||||
// | End of connections.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,32 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - data-type.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef DATATYPE_H
|
||||
#define DATATYPE_H
|
||||
|
||||
enum DataType
|
||||
{
|
||||
AREA,
|
||||
PLAYER,
|
||||
CONNECTION
|
||||
};
|
||||
|
||||
#endif
|
||||
// =================================================
|
||||
// | End of data-type.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,230 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - lists.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lists.h"
|
||||
|
||||
// Functions:
|
||||
// ==========
|
||||
struct List * createList(enum DataType type)
|
||||
{
|
||||
struct List * newList = calloc(1, sizeof(struct List));
|
||||
newList->itemCount = 0;
|
||||
newList->head = NULL;
|
||||
newList->tail = NULL;
|
||||
newList->type = type;
|
||||
return newList;
|
||||
}
|
||||
|
||||
size_t appendToList(enum DataType type, struct List * list, void * data)
|
||||
{
|
||||
// First check that you're adding the correct type:
|
||||
assert(type == list->type);
|
||||
|
||||
struct ListNode * newListNode = calloc(1, sizeof(struct ListNode));
|
||||
newListNode->next = NULL;
|
||||
newListNode->previous = list->tail;
|
||||
newListNode->data = data;
|
||||
|
||||
if (list->itemCount == 0)
|
||||
{
|
||||
list->head = newListNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
list->tail->next = newListNode;
|
||||
}
|
||||
|
||||
list->tail = newListNode;
|
||||
list->itemCount++;
|
||||
|
||||
return list->itemCount;
|
||||
}
|
||||
|
||||
void * deleteListNodeFromList(size_t index, struct List * list)
|
||||
{
|
||||
void * toReturn;
|
||||
|
||||
if ((list->itemCount - 1) < index)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ListNode * currentListNode = NULL;
|
||||
|
||||
if (index < (list->itemCount / 2))
|
||||
{
|
||||
currentListNode = list->head;
|
||||
|
||||
// Get to the correct point in the linked list:
|
||||
for (int currentIndex = 0; currentIndex < index; currentIndex++)
|
||||
{
|
||||
currentListNode = currentListNode->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
currentListNode = list->tail;
|
||||
|
||||
// Get to the correct point in the linked list:
|
||||
for (int currentIndex = list->itemCount - 1; currentIndex > index; currentIndex--)
|
||||
{
|
||||
currentListNode = currentListNode->previous;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentListNode == list->head)
|
||||
{
|
||||
list->head = list->head->next;
|
||||
if (list->head)
|
||||
{
|
||||
list->head->previous = NULL;
|
||||
}
|
||||
}
|
||||
if (currentListNode == list->tail)
|
||||
{
|
||||
list->tail = list->tail->previous;
|
||||
if (list->tail)
|
||||
{
|
||||
list->tail->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentListNode->next != NULL)
|
||||
{
|
||||
currentListNode->next->previous = currentListNode->previous;
|
||||
}
|
||||
|
||||
if (currentListNode->previous != NULL)
|
||||
{
|
||||
currentListNode->previous->next = currentListNode->next;
|
||||
}
|
||||
|
||||
toReturn = currentListNode->data;
|
||||
free(currentListNode);
|
||||
|
||||
list->itemCount--;
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
ssize_t indexOfFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
||||
{
|
||||
size_t index = 0;
|
||||
if (list->head == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ListNode * currentListNode = list->head;
|
||||
do
|
||||
{
|
||||
if (comparisonFunction(currentListNode->data, data) == true)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
index++;
|
||||
currentListNode = currentListNode->next;
|
||||
}
|
||||
while (currentListNode != NULL);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void * getFirstFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
||||
{
|
||||
size_t index = 0;
|
||||
if (list->head == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ListNode * currentListNode = list->head;
|
||||
do
|
||||
{
|
||||
if (comparisonFunction(currentListNode->data, data) == true)
|
||||
{
|
||||
return currentListNode;
|
||||
}
|
||||
index++;
|
||||
currentListNode = currentListNode->next;
|
||||
}
|
||||
while (currentListNode != NULL);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list)
|
||||
{
|
||||
if (list->head == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ListNode * currentListNode = list->head;
|
||||
do
|
||||
{
|
||||
if (comparisonFunction(currentListNode->data, data) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
currentListNode = currentListNode->next;
|
||||
}
|
||||
while (currentListNode != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool isPointerInList(void * data, struct List * list)
|
||||
{
|
||||
if (list->head == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct ListNode * currentListNode = list->head;
|
||||
do
|
||||
{
|
||||
if (currentListNode->data == data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
currentListNode = currentListNode->next;
|
||||
}
|
||||
while (currentListNode != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// | End of lists.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 <https://www.gnu.org/licenses/>.
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - lists.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef LISTS_H
|
||||
#define LISTS_H
|
||||
#include "data-type.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
struct List
|
||||
{
|
||||
size_t itemCount;
|
||||
enum DataType type;
|
||||
struct ListNode * head;
|
||||
struct ListNode * tail;
|
||||
};
|
||||
|
||||
struct ListNode
|
||||
{
|
||||
struct ListNode * next;
|
||||
struct ListNode * previous;
|
||||
void * data;
|
||||
};
|
||||
|
||||
|
||||
// Functions:
|
||||
// ==========
|
||||
|
||||
struct List * createList(enum DataType type);
|
||||
size_t appendToList(enum DataType type, struct List * list, void * data);
|
||||
void * deleteListNodeFromList(size_t index, struct List * list);
|
||||
ssize_t indexOfFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
||||
void * getFirstFromList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
||||
bool isInList(bool (*comparisonFunction)(void *, void *), void * data, struct List * list);
|
||||
bool isPointerInList(void * data, struct List * list);
|
||||
|
||||
#endif
|
||||
// =============================================
|
||||
// | End of lists.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,372 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - main.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <libguile.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
#include "output-queue.h"
|
||||
#include "player-data.h"
|
||||
#include "connections.h"
|
||||
#include "../messages.h"
|
||||
#include "scheme-integration.h"
|
||||
|
||||
static const int CONCURRENT_PLAYER_COUNT = 256;
|
||||
static char serverPort[HOST_NAME_MAX] = "5050";
|
||||
static char serverHostname[HOST_NAME_MAX] = "";
|
||||
static char serverInterface[HOST_NAME_MAX] = "";
|
||||
static char clientRequestedHost[HOST_NAME_MAX] = "";
|
||||
static size_t clientRequestedHostLength = HOST_NAME_MAX;
|
||||
static bool portSpecified = false, hostSpecified = false, interfaceSpecified = false;
|
||||
|
||||
// Check what the client intends to connect to:
|
||||
int checkRequestedHostname(gnutls_session_t session)
|
||||
{
|
||||
// Get the hostname the client is using to connect:
|
||||
clientRequestedHostLength = HOST_NAME_MAX;
|
||||
gnutls_server_name_get(session, (void *)clientRequestedHost, &clientRequestedHostLength, &(unsigned int){GNUTLS_NAME_DNS}, 0);
|
||||
clientRequestedHost[HOST_NAME_MAX - 1] = '\0';
|
||||
printf("Client is connecting to: %s\n", clientRequestedHost);
|
||||
|
||||
// Check that it's a valid hostname for SilverMUD:
|
||||
if (hostSpecified == true && strncmp(serverHostname, clientRequestedHost, HOST_NAME_MAX) != 0)
|
||||
{
|
||||
return GNUTLS_E_UNRECOGNIZED_NAME;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
// Print a welcome message:
|
||||
printf("SilverMUD Server - Starting Now.\n"
|
||||
"================================\n");
|
||||
|
||||
// Configure command-line options:
|
||||
static struct option longOptions[] =
|
||||
{
|
||||
{"port", required_argument, 0, 'p' },
|
||||
{"host", required_argument, 0, 'h' },
|
||||
{"interface", required_argument, 0, 'i' }
|
||||
};
|
||||
|
||||
// Check environment variables:
|
||||
if (getenv("SILVERMUD_SERVER_PORT") != NULL)
|
||||
{
|
||||
portSpecified = true;
|
||||
strncpy(serverPort, getenv("SILVERMUD_SERVER_HOST"), HOST_NAME_MAX);
|
||||
}
|
||||
if (getenv("SILVERMUD_SERVER_HOST") != NULL)
|
||||
{
|
||||
hostSpecified = true;
|
||||
strncpy(serverHostname, getenv("SILVERMUD_SERVER_HOST"), HOST_NAME_MAX);
|
||||
}
|
||||
if (getenv("SILVERMUD_SERVER_INTERFACE") != NULL)
|
||||
{
|
||||
interfaceSpecified = true;
|
||||
strncpy(serverInterface, getenv("SILVERMUD_SERVER_INTERFACE"), HOST_NAME_MAX);
|
||||
}
|
||||
|
||||
// Parse command-line options:
|
||||
int selectedOption = 0, optionIndex = 0;
|
||||
while ((selectedOption = getopt_long(argc, argv, "p:h:i:", longOptions, &optionIndex)) != -1)
|
||||
{
|
||||
switch (selectedOption)
|
||||
{
|
||||
case 'p':
|
||||
{
|
||||
portSpecified = true;
|
||||
strncpy(serverPort, optarg, HOST_NAME_MAX);
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
{
|
||||
|
||||
hostSpecified = true;
|
||||
strncpy(serverHostname, optarg, HOST_NAME_MAX);
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
{
|
||||
printf("Using interface address: %s\n", optarg);
|
||||
interfaceSpecified = true;
|
||||
strncpy(serverInterface, optarg, HOST_NAME_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (portSpecified)
|
||||
{
|
||||
printf("Using port: %s\n", serverPort);
|
||||
}
|
||||
|
||||
if (hostSpecified)
|
||||
{
|
||||
printf("Using hostname: %s\n", serverHostname);
|
||||
}
|
||||
|
||||
if (interfaceSpecified)
|
||||
{
|
||||
printf("Using interface: %s\n", serverInterface);
|
||||
}
|
||||
|
||||
// Initialize Scheme:
|
||||
scm_init_guile();
|
||||
|
||||
// Create a socket to listen for connections on:
|
||||
int masterSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (masterSocket < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to create socket. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Allow reusing the address so that quick re-launching doesn't fail:
|
||||
setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
|
||||
setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int));
|
||||
|
||||
// Setup the server address struct to bind the master socket to:
|
||||
struct sockaddr_in serverAddress;
|
||||
memset(&serverAddress, 0, sizeof(struct sockaddr_in));
|
||||
|
||||
// Assign the IP address and port to the server address struct:
|
||||
serverAddress.sin_family = AF_INET;
|
||||
serverAddress.sin_addr.s_addr = (interfaceSpecified) ?
|
||||
inet_addr(serverInterface) : htonl(INADDR_ANY);
|
||||
serverAddress.sin_port = (portSpecified) ?
|
||||
htons(atoi(serverPort)) : htons(5050);
|
||||
|
||||
// Bind the master socket to the server address:
|
||||
if ((bind(masterSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in))) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to bind socket. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Begin listening:
|
||||
if ((listen(masterSocket, CONCURRENT_PLAYER_COUNT)) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to begin listening on the master socket. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Create an epoll instance for managing connections, and add the master socket to it:
|
||||
int connectedClients = epoll_create(CONCURRENT_PLAYER_COUNT);
|
||||
if (connectedClients < 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to create epoll instance. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Setup the epoll events we want to watch for:
|
||||
struct epoll_event watchedEvents;
|
||||
watchedEvents.events = EPOLLIN;
|
||||
watchedEvents.data.fd = masterSocket;
|
||||
epoll_ctl(connectedClients, EPOLL_CTL_ADD, masterSocket, &watchedEvents);
|
||||
|
||||
int eventsCount = 0;
|
||||
struct epoll_event events[1024];
|
||||
|
||||
// Setup the needed anonymous certificate for TLS:
|
||||
gnutls_global_init();
|
||||
gnutls_anon_server_credentials_t serverKey;
|
||||
gnutls_anon_allocate_server_credentials(&serverKey);
|
||||
gnutls_anon_set_server_known_dh_params(serverKey, GNUTLS_SEC_PARAM_MEDIUM);
|
||||
|
||||
// Create a client connection list to allow us to associate TLS sessions and sockets and players:
|
||||
struct ClientConnectionList clientConnections;
|
||||
|
||||
// Create some structures needed to store global state:
|
||||
struct PlayerList * globalPlayerList = createPlayerList();
|
||||
struct OutputQueue * globalOutputQueue = createOutputQueue();
|
||||
|
||||
// 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);
|
||||
|
||||
// Start a REPL thread:
|
||||
//pthread_t schemeREPLThread;
|
||||
//pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL);
|
||||
|
||||
size_t * clientRequestedHostLength = calloc(1, sizeof(size_t));
|
||||
|
||||
while (true)
|
||||
{
|
||||
do
|
||||
{
|
||||
eventsCount = epoll_wait(connectedClients, events, 1024, -1);
|
||||
} while (eventsCount < 0 && errno == EINTR);
|
||||
|
||||
if (eventsCount == -1)
|
||||
{
|
||||
fprintf(stderr, "epoll_wait() failed. Aborting.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int index = 0; index < eventsCount; index++)
|
||||
{
|
||||
// If it's the master socket, it's a new client connecting:
|
||||
if (events[index].data.fd == masterSocket)
|
||||
{
|
||||
// Setup a TLS Session:
|
||||
gnutls_session_t * tlsSession = calloc(1, sizeof(gnutls_session_t));
|
||||
gnutls_init(tlsSession, GNUTLS_SERVER);
|
||||
gnutls_priority_set_direct(*tlsSession, "NORMAL:+ANON-ECDH:+ANON-DH", NULL);
|
||||
gnutls_credentials_set(*tlsSession, GNUTLS_CRD_ANON, serverKey);
|
||||
gnutls_handshake_set_timeout(*tlsSession, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
gnutls_handshake_set_post_client_hello_function(*tlsSession, checkRequestedHostname);
|
||||
|
||||
// Accept the connection:
|
||||
int newSocket = accept(masterSocket, NULL, NULL);
|
||||
gnutls_transport_set_int(*tlsSession, newSocket);
|
||||
|
||||
// Perform a TLS handshake:
|
||||
int handshakeReturnValue = 0;
|
||||
do
|
||||
{
|
||||
handshakeReturnValue = gnutls_handshake(*tlsSession);
|
||||
} while (handshakeReturnValue < 0 && gnutls_error_is_fatal(handshakeReturnValue) == 0);
|
||||
|
||||
// If the handshake was unsuccessful, close the connection:
|
||||
if (handshakeReturnValue < 0)
|
||||
{
|
||||
fprintf(stderr, "TLS Failure: %d\n", handshakeReturnValue);
|
||||
fflush(stdout);
|
||||
gnutls_bye(*tlsSession, 2);
|
||||
shutdown(newSocket, 2);
|
||||
close(newSocket);
|
||||
break;
|
||||
}
|
||||
|
||||
// Setup the epoll events we want to watch for:
|
||||
watchedEvents.events = EPOLLIN;
|
||||
watchedEvents.data.fd = newSocket;
|
||||
epoll_ctl(connectedClients, EPOLL_CTL_ADD, newSocket, &watchedEvents);
|
||||
|
||||
// Add the connection to the list:
|
||||
struct ClientConnection * newConnection = addNewConnection(&clientConnections, newSocket, tlsSession);
|
||||
newConnection->player = createNewPlayer(newConnection);
|
||||
sprintf(newConnection->player->name, "Player %02d", globalPlayerList->count + 1);
|
||||
addToPlayerList(newConnection->player, globalPlayerList);
|
||||
|
||||
// Prepare a welcome message:
|
||||
struct ServerToClientMessage welcomeMessage;
|
||||
welcomeMessage.type = SYSTEM;
|
||||
sprintf(welcomeMessage.content,
|
||||
(clientConnections.clientCount > 1) ?
|
||||
"Welcome to the server. There are %d players connected." :
|
||||
"Welcome to the server. There is %d player connected.",
|
||||
clientConnections.clientCount);
|
||||
|
||||
// Send the welcome message:
|
||||
gnutls_record_send(*tlsSession, &welcomeMessage, sizeof(struct ServerToClientMessage));
|
||||
|
||||
// Report the new connection on the server:
|
||||
printf("New connection established. %d client(s), session ID %u.\n",
|
||||
clientConnections.clientCount, tlsSession);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the corresponding TLS session:
|
||||
struct ClientConnection * connection = findConnectionByFileDescriptor(&clientConnections, events[index].data.fd);
|
||||
if (connection != NULL)
|
||||
{
|
||||
// Read the data from the TLS session:
|
||||
struct ClientToServerMessage message;
|
||||
int returnValue = gnutls_record_recv(*connection->tlsSession, &message, sizeof(struct ClientToServerMessage));
|
||||
|
||||
if (returnValue == 0 || returnValue == -10)
|
||||
{
|
||||
epoll_ctl(connectedClients, EPOLL_CTL_DEL, connection->fileDescriptor, &watchedEvents);
|
||||
shutdown(connection->fileDescriptor, 2);
|
||||
close(connection->fileDescriptor);
|
||||
removeFromPlayerList(connection->player, globalPlayerList);
|
||||
deallocatePlayer(&connection->player);
|
||||
removeConnectionByFileDescriptor(&clientConnections, connection->fileDescriptor);
|
||||
continue;
|
||||
}
|
||||
else if (returnValue == sizeof(struct ClientToServerMessage))
|
||||
{
|
||||
// TODO: BEGIN ACTUAL COMMAND INTERPRETER
|
||||
// ONLY FOR DEMO
|
||||
if (strncmp(message.content, "NAME ", 5) == 0 && message.content[5] != '\0')
|
||||
{
|
||||
strncpy(connection->player->name, &message.content[5], 64);
|
||||
continue;
|
||||
}
|
||||
// ONLY FOR DEMO
|
||||
|
||||
pushOutputMessage(globalOutputQueue, false, globalPlayerList, LOCAL_CHAT,
|
||||
connection->player->name, message.content,
|
||||
MESSAGE_NAME_LENGTH, MESSAGE_CONTENT_LENGTH);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Didn't find associated TLS Session!\n");
|
||||
fflush(stdout);
|
||||
// Remove the file descriptor from our watched set and close it:
|
||||
epoll_ctl(connectedClients, EPOLL_CTL_DEL, events[index].data.fd, &watchedEvents);
|
||||
close(events[index].data.fd);
|
||||
removeConnectionByFileDescriptor(&clientConnections, events[index].data.fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for all other threads to terminate:
|
||||
//pthread_join(schemeREPLThread, NULL);
|
||||
|
||||
// Deallocate the global state structures:
|
||||
deallocatePlayerList(&globalPlayerList);
|
||||
|
||||
// Return a successful status code to the operating system:
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// | End of main.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,168 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - output-queue.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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:
|
||||
struct OutputQueue * const newQueue = calloc(1, sizeof(struct OutputQueue));
|
||||
|
||||
// Initialize it:
|
||||
pthread_mutex_init(&newQueue->mutex, NULL);
|
||||
pthread_cond_init(&newQueue->updated, 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);
|
||||
|
||||
pthread_cond_signal(&queue->updated);
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
// =========================================
|
||||
// | 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 <stdbool.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#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;
|
||||
pthread_mutex_t waitMutex;
|
||||
pthread_cond_t updated;
|
||||
size_t count;
|
||||
struct OutputMessage * front, * back;
|
||||
};
|
||||
|
||||
void * outputThreadHandler(void * outputQueue);
|
||||
|
||||
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(struct OutputQueue * queue);
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
||||
|
|
@ -0,0 +1,252 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - player-data.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "player-data.h"
|
||||
|
||||
// Internal Functions:
|
||||
// ===================
|
||||
static struct PlayerListNode * findMiddle(struct PlayerListNode * start, struct PlayerListNode * end)
|
||||
{
|
||||
while (start != end)
|
||||
{
|
||||
start = start->next;
|
||||
if(start == end)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
end = end->previous;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
// ====================
|
||||
|
||||
// Allocates and sets up a new player according to the world's starter character sheet:
|
||||
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;
|
||||
}
|
||||
|
||||
// Deallocates a player:
|
||||
void deallocatePlayer(struct Player ** player)
|
||||
{
|
||||
free((*player)->name);
|
||||
free(*player);
|
||||
*player = NULL;
|
||||
}
|
||||
|
||||
struct PlayerList * createPlayerList()
|
||||
{
|
||||
struct PlayerList * newPlayerList = calloc(1, sizeof(struct PlayerList));
|
||||
newPlayerList->count = 0;
|
||||
newPlayerList->head = NULL;
|
||||
newPlayerList->tail = NULL;
|
||||
|
||||
return newPlayerList;
|
||||
}
|
||||
|
||||
void deallocatePlayerList(struct PlayerList ** playerList)
|
||||
{
|
||||
struct PlayerListNode * node = (*playerList)->head, * nextNode;
|
||||
|
||||
// Deallocate all nodes in the list:
|
||||
while (node != NULL)
|
||||
{
|
||||
nextNode = node->next;
|
||||
free(node);
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
// Deallocate the list itself:
|
||||
free(*playerList);
|
||||
|
||||
// Set the pointer to null:
|
||||
playerList = NULL;
|
||||
}
|
||||
|
||||
int addToPlayerList(struct Player * player, struct PlayerList * playerList)
|
||||
{
|
||||
// Check that the player isn't already in the list:
|
||||
if (isInPlayerList(player, playerList))
|
||||
{
|
||||
return playerList->count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a node to add to the list:
|
||||
struct PlayerListNode * newNode = calloc(1, sizeof(struct PlayerListNode));
|
||||
newNode->player = player;
|
||||
newNode->next = NULL;
|
||||
newNode->previous = NULL;
|
||||
|
||||
// Find the position that the new node is to go into:
|
||||
|
||||
// If the list is empty:
|
||||
if (playerList->count == 0)
|
||||
{
|
||||
playerList->head = newNode;
|
||||
playerList->tail = newNode;
|
||||
playerList->count = 1;
|
||||
return playerList->count;
|
||||
}
|
||||
|
||||
struct PlayerListNode * currentNode = playerList->head;
|
||||
while (strncmp(player->name, currentNode->player->name, 64) < 0)
|
||||
{
|
||||
// If we reach the end of the list:
|
||||
if (currentNode->next == NULL)
|
||||
{
|
||||
currentNode->next = newNode;
|
||||
newNode->previous = currentNode;
|
||||
playerList->tail = newNode;
|
||||
playerList->count++;
|
||||
|
||||
return playerList->count;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the appropriate pointers in the new node:
|
||||
newNode->previous = currentNode->previous;
|
||||
currentNode->previous = newNode;
|
||||
newNode->next = currentNode;
|
||||
|
||||
// Set the proper pointers if we're at the ends of the list:
|
||||
if (newNode->previous == NULL)
|
||||
{
|
||||
playerList->head = newNode;
|
||||
}
|
||||
if (newNode->next == NULL)
|
||||
{
|
||||
playerList->tail = newNode;
|
||||
}
|
||||
|
||||
playerList->count++;
|
||||
|
||||
return playerList->count;
|
||||
}
|
||||
}
|
||||
|
||||
int removeFromPlayerList(struct Player * player, struct PlayerList * playerList)
|
||||
{
|
||||
struct PlayerListNode * currentNode = playerList->head;
|
||||
while (currentNode != NULL)
|
||||
{
|
||||
if (currentNode->player == player)
|
||||
{
|
||||
// Adjust the proper pointers:
|
||||
if (currentNode->previous)
|
||||
{
|
||||
currentNode->previous->next = currentNode->next;
|
||||
}
|
||||
if (currentNode->next)
|
||||
{
|
||||
currentNode->next->previous = currentNode->previous;
|
||||
}
|
||||
|
||||
// Handle the special case of the head and tail of the list:
|
||||
if (playerList->head == currentNode)
|
||||
{
|
||||
playerList->head == playerList->head->next;
|
||||
}
|
||||
if (playerList->tail == currentNode)
|
||||
{
|
||||
playerList->tail == playerList->tail->previous;
|
||||
}
|
||||
|
||||
// Handle the special case of an empty list:
|
||||
if (playerList->count - 1 == 0)
|
||||
{
|
||||
playerList->head = NULL;
|
||||
playerList->tail = NULL;
|
||||
}
|
||||
|
||||
// Delete the node:
|
||||
free(currentNode);
|
||||
|
||||
return --(playerList->count);
|
||||
}
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the Player with the given name from a PlayerList, or NULL otherwise:
|
||||
struct Player * getFromPlayerList(char * playerName, struct PlayerList * playerList)
|
||||
{
|
||||
struct PlayerListNode * start = playerList->head, * end = playerList->tail, * middle = findMiddle(start, end);
|
||||
int returnValue = 0;
|
||||
|
||||
while (start != end)
|
||||
{
|
||||
returnValue = strncmp(middle->player->name, playerName, 64);
|
||||
|
||||
if (returnValue < 0)
|
||||
{
|
||||
start = middle->next;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
else if (returnValue > 0)
|
||||
{
|
||||
end = middle->next;
|
||||
middle = findMiddle(start, end);
|
||||
}
|
||||
else if (returnValue == 0)
|
||||
{
|
||||
return middle->player;
|
||||
}
|
||||
}
|
||||
if (strncmp(start->player->name, playerName, 64) == 0)
|
||||
{
|
||||
return start->player;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the given Player is in the given PlayerList:
|
||||
bool isInPlayerList(struct Player * player, struct PlayerList * playerList)
|
||||
{
|
||||
struct PlayerListNode * currentNode = playerList->head;
|
||||
while (currentNode != NULL)
|
||||
{
|
||||
if (currentNode->player == player)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
currentNode = currentNode->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// | End of player-data.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,80 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - player-data.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef PLAYER_DATA_H
|
||||
#define PLAYER_DATA_H
|
||||
#define PLAYER_NAME_LENGTH 64
|
||||
#include <stdbool.h>
|
||||
#include "connections.h"
|
||||
|
||||
// =================================================================
|
||||
// Players - A structure for representing a single player character:
|
||||
// =================================================================
|
||||
struct Player
|
||||
{
|
||||
struct ClientConnection * connection;
|
||||
char * name;
|
||||
};
|
||||
|
||||
// Functions:
|
||||
// ==========
|
||||
|
||||
// Allocates and sets up a new player according to the world's starter character sheet:
|
||||
struct Player * createNewPlayer(struct ClientConnection * connection);
|
||||
|
||||
// Deallocates a player:
|
||||
void deallocatePlayer(struct Player ** player);
|
||||
|
||||
// ========================================================================================
|
||||
// Player Lists - A structure for managing a collection of players in a doubly linked list:
|
||||
// ========================================================================================
|
||||
struct PlayerListNode
|
||||
{
|
||||
struct Player * player;
|
||||
struct PlayerListNode * next, * previous;
|
||||
};
|
||||
|
||||
struct PlayerList
|
||||
{
|
||||
size_t count;
|
||||
struct PlayerListNode * head, * tail;
|
||||
};
|
||||
|
||||
// Functions:
|
||||
// ==========
|
||||
|
||||
struct PlayerList * createPlayerList();
|
||||
void deallocatePlayerList(struct PlayerList ** playerList);
|
||||
|
||||
// Adds a Player into a PlayerList, in a sorted position by character name.
|
||||
// Returns the count of players in the list:
|
||||
int addToPlayerList(struct Player * player, struct PlayerList * playerList);
|
||||
|
||||
// Remove a Player from a PlayerList. Returns the count of players in the list:
|
||||
int removeFromPlayerList(struct Player * player, struct PlayerList * playerList);
|
||||
|
||||
// Returns the Player with the given name from a PlayerList, or NULL otherwise:
|
||||
struct Player * getFromPlayerList(char * playerName, struct PlayerList * playerList);
|
||||
|
||||
// Returns true if the given Player is in the given PlayerList:
|
||||
bool isInPlayerList(struct Player * player, struct PlayerList * playerList);
|
||||
|
||||
#endif
|
||||
// ===================================================
|
||||
// | End of player-data.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,54 @@
|
|||
// =========================================
|
||||
// | SilverMUD Server - queues.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef QUEUES_H
|
||||
#define QUEUES_H
|
||||
#include "data-type.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
struct Queue
|
||||
{
|
||||
size_t itemCount;
|
||||
enum DataType type;
|
||||
struct QueueNode * front;
|
||||
struct QueueNode * back;
|
||||
};
|
||||
|
||||
struct QueueNode
|
||||
{
|
||||
struct QueueNode * next;
|
||||
void * data;
|
||||
};
|
||||
|
||||
|
||||
// Functions:
|
||||
// ==========
|
||||
|
||||
struct Queue * createQueue(enum DataType type);
|
||||
int destroyQueue(struct Queue * queue);
|
||||
int destroyQueueAndContents(void (*deallocationFunction)(void *), struct Queue * queue);
|
||||
|
||||
void * peekFromQueue(struct Queue * queue);
|
||||
size_t popFromQueue(struct Queue * queue);
|
||||
size_t pushToQueue(enum DataType type, void * data, struct Queue * queue);
|
||||
size_t popFromQueueAndDestroy(void (*deallocationFunction)(void *), struct Queue * queue);
|
||||
|
||||
#endif
|
||||
// ==============================================
|
||||
// | End of queues.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,63 @@
|
|||
// ===========================================
|
||||
// | SilverMUD Server - scheme-integration.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ===========================================
|
||||
#include <stdbool.h>
|
||||
#include <libguile.h>
|
||||
|
||||
#include "../messages.h"
|
||||
#include "output-queue.h"
|
||||
#include "scheme-integration.h"
|
||||
|
||||
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)
|
||||
{
|
||||
// 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. |
|
||||
// ==========================================================
|
||||
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,37 @@
|
|||
// ===========================================
|
||||
// | SilverMUD Server - scheme-integration.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// ===========================================
|
||||
#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);
|
||||
|
||||
#endif
|
||||
// ==========================================================
|
||||
// | End of scheme-integration.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,172 @@
|
|||
// =========================================
|
||||
// | SilverMUD Tests - lists-test.c |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "tests.h"
|
||||
#include "../server/lists.h"
|
||||
|
||||
SETUP_TESTS;
|
||||
|
||||
bool compareChar (void * a, void * b)
|
||||
{
|
||||
return *(char *)a == *(char *)b;
|
||||
}
|
||||
|
||||
void printListHeader(struct List * list)
|
||||
{
|
||||
printf("Items in list: %zu\nType: %d\nHead: %p\nTail: %p\n\n",
|
||||
list->itemCount, list->type, list->head, list->tail);
|
||||
}
|
||||
|
||||
void printListNode(struct ListNode * node)
|
||||
{
|
||||
printf("%p\nNext: %p\nPrevious: %p\nData: %c\n\n",
|
||||
node, node->next, node->previous, *(char *)(node->data));
|
||||
}
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
char testDataA = 'A', testDataB = 'B', testDataC = 'C';
|
||||
|
||||
// Test 1:
|
||||
struct List * testList = createList(PLAYER);
|
||||
TEST("Creating list",
|
||||
(testList->head == NULL &&
|
||||
testList->tail == NULL &&
|
||||
testList->itemCount == 0));
|
||||
|
||||
// Test 2:
|
||||
appendToList(PLAYER, testList, &testDataA);
|
||||
TEST("Adding a single item to the list",
|
||||
(testList->itemCount == 1 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
testList->tail == testList->head &&
|
||||
*(char *)(testList->head->data) == 'A'));
|
||||
|
||||
// Test 3:
|
||||
deleteListNodeFromList(0, testList);
|
||||
TEST("Deleting a single item from the list",
|
||||
(testList->head == NULL &&
|
||||
testList->tail == NULL &&
|
||||
testList->itemCount == 0));
|
||||
|
||||
// Test 4:
|
||||
appendToList(PLAYER, testList, &testDataA);
|
||||
appendToList(PLAYER, testList, &testDataB);
|
||||
TEST("Adding two items to the list",
|
||||
(testList->itemCount == 2 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
*(char *)testList->head->data == 'A' &&
|
||||
*(char *)testList->tail->data == 'B' &&
|
||||
testList->tail != testList->head &&
|
||||
testList->head->next == testList->tail &&
|
||||
testList->tail->previous == testList->head));
|
||||
|
||||
// Test 5:
|
||||
deleteListNodeFromList(1, testList);
|
||||
TEST("Deleting tail from the list",
|
||||
(testList->itemCount == 1 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
testList->tail == testList->head &&
|
||||
*(char *)(testList->head->data) == 'A'));
|
||||
|
||||
// Test 6:
|
||||
appendToList(PLAYER, testList, &testDataB);
|
||||
deleteListNodeFromList(0, testList);
|
||||
TEST("Deleting head from the list",
|
||||
(testList->itemCount == 1 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
testList->tail == testList->head &&
|
||||
*(char *)(testList->head->data) == 'B'));
|
||||
|
||||
// Test 7:
|
||||
deleteListNodeFromList(0, testList);
|
||||
appendToList(PLAYER, testList, &testDataA);
|
||||
appendToList(PLAYER, testList, &testDataB);
|
||||
appendToList(PLAYER, testList, &testDataC);
|
||||
TEST("Adding three items to the list",
|
||||
(testList->itemCount == 3 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
*(char *)testList->head->data == 'A' &&
|
||||
*(char *)testList->tail->data == 'C' &&
|
||||
*(char *)testList->head->next->data == 'B' &&
|
||||
*(char *)testList->tail->previous->data == 'B' &&
|
||||
testList->tail != testList->head &&
|
||||
testList->tail->previous == testList->head->next));
|
||||
|
||||
// Test 8:
|
||||
deleteListNodeFromList(1, testList);
|
||||
TEST("Deleting an item in middle of the list",
|
||||
(testList->itemCount == 2 &&
|
||||
testList->head != NULL &&
|
||||
testList->tail != NULL &&
|
||||
*(char *)testList->head->data == 'A' &&
|
||||
*(char *)testList->tail->data == 'C' &&
|
||||
testList->tail != testList->head &&
|
||||
testList->head->next == testList->tail &&
|
||||
testList->tail->previous == testList->head));
|
||||
|
||||
// Test 9:
|
||||
deleteListNodeFromList(1, testList);
|
||||
appendToList(PLAYER, testList, &testDataB);
|
||||
appendToList(PLAYER, testList, &testDataC);
|
||||
TEST("Checking for data in the list using comparison function",
|
||||
(isInList(compareChar, &testDataB, testList) == true));
|
||||
|
||||
// Test 10:
|
||||
TEST("Checking for data NOT in the list using comparison function",
|
||||
(isInList(compareChar, &(char){'D'}, testList) == true));
|
||||
|
||||
// Test 11:
|
||||
TEST("Checking for data in the list using pointer",
|
||||
(isPointerInList(&testDataB, testList) == true));
|
||||
|
||||
// Test 12:
|
||||
TEST("Checking for data NOT in the list using pointer",
|
||||
(isPointerInList(&(char){'D'}, testList) == false));
|
||||
|
||||
// Test 13:
|
||||
TEST("Checking index of data in the list",
|
||||
(indexOfFromList(compareChar, &testDataB, testList) == 1));
|
||||
|
||||
// Test 14:
|
||||
TEST("Checking index of data NOT in the list",
|
||||
(indexOfFromList(compareChar, &(char){'D'}, testList) == -1));
|
||||
|
||||
// Test 15:
|
||||
appendToList(PLAYER, testList, &testDataA);
|
||||
TEST("Checking first of data in the list",
|
||||
(getFirstFromList(compareChar, &testDataA, testList) == testList->head));
|
||||
|
||||
// Test 16:
|
||||
TEST("Checking first of data NOT in the list",
|
||||
(getFirstFromList(compareChar, &(char){'D'}, testList) == NULL));
|
||||
|
||||
FINISH_TESTING;
|
||||
}
|
||||
|
||||
// ==================================================
|
||||
// | End of lists-test.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 <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,43 @@
|
|||
// =========================================
|
||||
// | SilverMUD Tests - tests.h |
|
||||
// | Copyright (C) 2023, Barra Ó Catháin |
|
||||
// | See end of file for copyright notice. |
|
||||
// =========================================
|
||||
#ifndef SILVERMUD_TESTS
|
||||
#define SILVERMUD_TESTS
|
||||
|
||||
#define SETUP_TESTS \
|
||||
size_t TEST_SUCCESSES = 0, TEST_FAILURES = 0, TEST_COUNT;
|
||||
|
||||
#define TEST(STRING, CONDITION) \
|
||||
printf("Test %zu: ", ++TEST_COUNT); \
|
||||
printf(STRING); \
|
||||
if (CONDITION) { printf("... SUCCESS!\n"); TEST_SUCCESSES++; } \
|
||||
else { printf("... FAILED.\n"); TEST_FAILURES++; }
|
||||
|
||||
#define FINISH_TESTING \
|
||||
printf("=====\n\nSuccesses: %zu | Failures: %zu\n", TEST_SUCCESSES, TEST_FAILURES); \
|
||||
if (TEST_FAILURES == 0) \
|
||||
{ \
|
||||
printf("All good! We're done here.\n"); \
|
||||
exit(EXIT_SUCCESS); \
|
||||
} \
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
#endif
|
||||
// =============================================
|
||||
// | End of tests.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 <https://www.gnu.org/licenses/>.
|
Loading…
Reference in New Issue