Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions LINUX_BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Meridian 59 Server - Linux Build Instructions

## Prerequisites (Ubuntu/Debian)

```bash
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install make gcc-multilib g++-multilib flex bison \
libjansson-dev libjansson-dev:i386 \
libpq-dev libpq-dev:i386 \
python3
```

## Build

Build everything (server, compiler, KOD, resources, utilities):

```bash
make -f makefile.linux
```

This builds:
- `blakserv` - the game server (copied to `run/server/`)
- `bc` - the KOD compiler (copied to `bin/`)
- `rscmerge` - resource merge tool (copied to `bin/`)
- All `.bof`, `.rsc`, `.rsb` files (copied to `run/server/rsc/` and `run/server/memmap/`)
- Room files (copied to `run/server/rooms/`)

To clean all build artifacts:

```bash
make -f makefile.linux clean
```

## Configuration

```bash
cd run/server
cp blakserv.cfg-linux blakserv.cfg
```

Edit `blakserv.cfg` as needed. Key settings:

```ini
[Socket]
Port 5959
MaintenancePort 9998
MaintenanceMask ::ffff:127.0.0.1;::1

[Resource]
Language 1

[MySQL]
Enabled No
```

Set `[MySQL] Enabled Yes` if using a database (see Database section below).

## Run

```bash
cd run/server
./blakserv # foreground (Ctrl+C to stop)
./blakserv & # background
```

Default ports: **5959** (game), **9998** (admin maintenance)

## Admin Commands

Via the admin script:

```bash
./blakadmin.sh show status
./blakadmin.sh show accounts
./blakadmin.sh who
./blakadmin.sh save game
./blakadmin.sh garbage
./blakadmin.sh "send o 0 updatedatabase"
./blakadmin.sh create account admin username password email
./blakadmin.sh shutdown
```

Interactive mode:

```bash
./blakadmin.sh
blakadm> show status
blakadm> bye
```

Or via telnet/netcat directly:

```bash
telnet 127.0.0.1 9998
echo "show status" | nc 127.0.0.1 9998
```

## Logs

```bash
tail -f run/server/channel/*.txt
```

Files: `debug.txt`, `error.txt`, `log.txt`, `god.txt`, `admin.txt`

## Database (Optional)

The server can optionally log game statistics (player logins, deaths, damage, money,
wiki data, etc.) to a PostgreSQL database. This is not required for the server to run.

To enable, install PostgreSQL and configure:

```ini
[MySQL]
Enabled Yes
Host 127.0.0.1
Port 5432
Username blakserv
Password your_password
Database meridian59
```

Note: The config section is named `[MySQL]` for compatibility with the Windows version,
but on Linux it connects to PostgreSQL via libpq.

The server creates all required tables automatically on first connect.

## Architecture

Single-threaded epoll main loop with a separate PostgreSQL writer thread
for async database operations.

Key Linux-specific files:
- `blakserv/osd_linux.c/h` - OS-dependent types and stubs
- `blakserv/osd_epoll.c` - Main loop (epoll socket multiplexing + timer wakeup via eventfd)
- `blakserv/database_pg.c/h` - PostgreSQL database layer (replaces MySQL)
- `blakserv/main.c` - Unified Windows/Linux main with `#ifdef`
- `util/rscmerge.c` - Resource merge with deterministic file sorting
4 changes: 4 additions & 0 deletions blakcomp/blakcomp.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@

#ifdef BLAK_PLATFORM_LINUX
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#define stricmp strcasecmp
#define O_BINARY 0
#define _MAX_PATH PATH_MAX
#define _mkdir(d) mkdir((d), 0755)
#endif

#include <stdio.h>
Expand Down
11 changes: 9 additions & 2 deletions blakcomp/blakcomp.l
Original file line number Diff line number Diff line change
Expand Up @@ -606,8 +606,13 @@ void add_include_directory(char *dirname)
*/
void check_output_directory(char *dirname)
{
#ifdef BLAK_PLATFORM_LINUX
sprintf(bof_output_dir, "%s/loadkod", dirname);
sprintf(rsc_output_dir, "%s/rsc", dirname);
#else
sprintf(bof_output_dir, "%s\\loadkod", dirname);
sprintf(rsc_output_dir, "%s\\rsc", dirname);
#endif

if (access(dirname, 4) != 0)
{
Expand Down Expand Up @@ -780,12 +785,14 @@ int main(int argc, char **argv)
}
}

#ifdef BLAK_PLATFORM_WINDOWS
if (directory_mode)
compile_directory_mode();
else
#endif
#ifdef BLAK_PLATFORM_LINUX
compile_file_list("./", file_list);
#else
compile_file_list(".\\", file_list);
#endif

return !generate_code;
}
Expand Down
24 changes: 17 additions & 7 deletions blakcomp/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,12 @@ void codegen_filename(char *filename)

if (directory_mode)
{
char *fname = strrchr(filename, '\\') + 1;
#ifdef BLAK_PLATFORM_LINUX
char *fname = strrchr(filename, '/');
#else
char *fname = strrchr(filename, '\\');
#endif
fname = fname ? fname + 1 : filename;
len = strlen(fname);
memcpy(&(codegen_buffer[codegen_buffer_position]), fname, len);
codegen_buffer_position += len;
Expand Down Expand Up @@ -696,7 +701,7 @@ int codegen_switch(switch_stmt_type s, int numlocals)

/* Backpatch continue statements in loop body */
for (p = current_loop->for_continue_list; p != NULL; p = p->next)
BackpatchGotoUnconditional(outfile, (int)p->data, FileCurPos(outfile));
BackpatchGotoUnconditional(outfile, (intptr_t)p->data, FileCurPos(outfile));

/* Go back and fill in destination address for unconditional goto */
if (!default_stmt)
Expand Down Expand Up @@ -739,12 +744,12 @@ void codegen_exit_loop(void)

/* Backpatch break statements to jump to end of loop */
for (p = current_loop->break_list; p != NULL; p = p->next)
BackpatchGotoUnconditional(outfile, (int) p->data, FileCurPos(outfile));
BackpatchGotoUnconditional(outfile, (intptr_t) p->data, FileCurPos(outfile));
current_loop->break_list = list_delete(current_loop->break_list);

/* Backpatch conditional goto statements to jump to end of loop */
for (p = current_loop->conditional_goto_list; p != NULL; p = p->next)
BackpatchGotoConditional(outfile, (int)p->data, FileCurPos(outfile));
BackpatchGotoConditional(outfile, (intptr_t)p->data, FileCurPos(outfile));
current_loop->conditional_goto_list = list_delete(current_loop->conditional_goto_list);

/* Remove current list from loop "stack" */
Expand Down Expand Up @@ -818,7 +823,7 @@ int codegen_dowhile(while_stmt_type s, int numlocals)

/* Backpatch continue statements in loop body */
for (p = current_loop->for_continue_list; p != NULL; p = p->next)
BackpatchGotoUnconditional(outfile, (int)p->data, FileCurPos(outfile));
BackpatchGotoUnconditional(outfile, (intptr_t)p->data, FileCurPos(outfile));

numtemps = codegen_conditional_goto(s->condition, numlocals, &sourceval);
if (numtemps > our_maxlocal)
Expand Down Expand Up @@ -891,7 +896,7 @@ int codegen_for(for_stmt_type s, int numlocals)

/* Backpatch continue statements in loop body */
for (p = current_loop->for_continue_list; p != NULL; p = p->next)
BackpatchGotoUnconditional(outfile, (int)p->data, FileCurPos(outfile));
BackpatchGotoUnconditional(outfile, (intptr_t)p->data, FileCurPos(outfile));

/* Step 4: Execute statements from assign list (iterators) */
/* If no iteration, list will be NULL. */
Expand Down Expand Up @@ -985,7 +990,7 @@ int codegen_foreach(foreach_stmt_type s, int numlocals)

/* Backpatch continue statements in loop body */
for (p = current_loop->for_continue_list; p != NULL; p = p->next)
BackpatchGotoUnconditional(outfile, (int)p->data, FileCurPos(outfile));
BackpatchGotoUnconditional(outfile, (intptr_t)p->data, FileCurPos(outfile));

/**** Statement #4: temp = Rest(temp) ****/
/* Can reuse temp_expr from statement #3 above */
Expand Down Expand Up @@ -1429,8 +1434,13 @@ void codegen(char *kod_fname, char *bof_fname)
write_resources(temp);

// Copy bof to output dir (like old instbofrsc.bat).
#ifdef BLAK_PLATFORM_LINUX
dircompile_copy_files(bof_fname, temp, strrchr(bof_fname, '/'),
strrchr(temp, '/'));
#else
dircompile_copy_files(bof_fname, temp, strrchr(bof_fname, '\\'),
strrchr(temp, '\\'));
#endif
}

if (!directory_mode)
Expand Down
49 changes: 38 additions & 11 deletions blakcomp/dircompile.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,38 @@
*/

#include "blakcomp.h"

#ifdef BLAK_PLATFORM_WINDOWS
#include "Windows.h"
#include "psapi.h"

#if defined(WIN32) || defined(WIN64)
// Copied from linux libc sys/stat.h:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

#ifdef BLAK_PLATFORM_LINUX
#include <sys/stat.h>
#include <libgen.h>
#define _fullpath(abs, rel, maxlen) realpath((rel), (abs))
#define DIRSEP '/'
#define DIRSEPSTR "/"
#define CopyFile(src, dst, fail_if_exists) \
do { \
FILE *_in = fopen((src), "rb"); \
FILE *_out = fopen((dst), "wb"); \
if (_in && _out) { \
char _buf[4096]; size_t _n; \
while ((_n = fread(_buf, 1, sizeof(_buf), _in)) > 0) \
fwrite(_buf, 1, _n, _out); \
} \
if (_in) fclose(_in); \
if (_out) fclose(_out); \
} while(0)
#else
#define DIRSEP '\\'
#define DIRSEPSTR "\\"
#endif

extern list_type directory_list;
extern list_type file_list;
extern int codegen_ok;
Expand Down Expand Up @@ -74,7 +97,7 @@ void compile_directory_mode()

// Remove trailing \ if we have one.
int len = strlen(full_path) - 1;
if (len > 1 && full_path[len] == '\\')
if (len > 1 && full_path[len] == DIRSEP)
full_path[len] = 0;

// Remove directory (should now be null).
Expand Down Expand Up @@ -117,7 +140,7 @@ void compile_directory_mode()
if (compile)
{
if (d->dir_name)
printf("Building %s\n", strrchr(d->dir_name, '\\') + 1);
printf("Building %s\n", strrchr(d->dir_name, DIRSEP) + 1);

compile_file_list(d->dir_name, d->dir_file_list);

Expand Down Expand Up @@ -188,10 +211,12 @@ void compile_directory_mode()
timeEnd = time(NULL);
printf("Elapsed time: %lld seconds\n", timeEnd - timeStart);

#ifdef BLAK_PLATFORM_WINDOWS
PROCESS_MEMORY_COUNTERS pmc;
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
printf("Peak mem: %i bytes\n", (int)pmc.PeakWorkingSetSize);
printf("Current mem: %i bytes\n",(int) pmc.WorkingSetSize);
#endif

return;
}
Expand All @@ -210,7 +235,7 @@ void fill_lists_from_makefile(char *full_path, int recompiled_parent)
char *tmpptr;
int done = False;

sprintf(makefile_path, "%s\\makefile", full_path);
sprintf(makefile_path, "%s" DIRSEPSTR "makefile", full_path);
makefile = fopen(makefile_path, "r");
if (makefile == NULL)
{
Expand All @@ -235,6 +260,8 @@ void fill_lists_from_makefile(char *full_path, int recompiled_parent)
int at_bofs = False;
while (fgets(temp, 256, makefile) != NULL)
{
temp[strcspn(temp, "\r\n")] = '\0';

// Our line must be greater than 6 characters
if (strlen(temp) <= 6 || temp[0] == '#')
continue;
Expand All @@ -260,7 +287,7 @@ void fill_lists_from_makefile(char *full_path, int recompiled_parent)
// Handle file.
file_name[filelen] = 0;

sprintf(dir_name, "%s\\%s", full_path, file_name);
sprintf(dir_name, "%s" DIRSEPSTR "%s", full_path, file_name);
int retval = recompile_check(dir_name, d, recompiled_parent);
// Returns > 0 if we should check for a directory.
if (retval)
Expand All @@ -276,12 +303,12 @@ void fill_lists_from_makefile(char *full_path, int recompiled_parent)
// Skip bof.
tmpptr += 3;
}
else if (*tmpptr == '\\')
else if (*tmpptr == DIRSEP || *tmpptr == '\\')
{
// Next line.
break;
}
else if (*tmpptr == '\n')
else if (*tmpptr == '\n' || *tmpptr == '\0')
{
done = True;
break;
Expand Down Expand Up @@ -323,7 +350,7 @@ int recompile_check(char *name, dir_data d, int recompiled_parent)
// Kod must exist, others don't have to.
if (stat(kodname, &time_kod) != 0)
{
simple_error("Found makefile entry with missing kod file %s", strrchr(kodname,'\\') + 1);
simple_error("Found makefile entry with missing kod file %s", strrchr(kodname, DIRSEP) + 1);
return False;
}

Expand Down Expand Up @@ -363,9 +390,9 @@ void dircompile_copy_files(char *bof_source, char *rsc_source, char *bofname, ch
{
char combine[_MAX_PATH];

sprintf(combine, "%s\\%s", bof_output_dir, bofname);
sprintf(combine, "%s" DIRSEPSTR "%s", bof_output_dir, bofname);
CopyFile(bof_source, combine, FALSE);

sprintf(combine, "%s\\%s", rsc_output_dir, rscname);
sprintf(combine, "%s" DIRSEPSTR "%s", rsc_output_dir, rscname);
CopyFile(rsc_source, combine, FALSE);
}
Loading
Loading