OnlineCTR: buildable server in linux & etc. (#119)

* fix: high cpu usage on gameTick

* feat: port argument on start

* feat: buildable on linux

* fix: there's not bool on C

* fix: wrong function, using snprintf instead

* PR review stuff

* GNU99-compatible STATIC_ASSERT

* OnlineCTR: add Nix support

* ci: add server; add ARM builds

* Network_PC: revive Nix in README

* style: changed from spaces to tabs in new code

* Merge branch 'main' of https://github.com/ClaudioBo/CTR-ModSDK

* fix: changed macros based on new commit

* fix(cmake): unnecesary unrelated linux options

---------

Co-authored-by: PedroHLC <root@pedrohlc.com>
This commit is contained in:
Claudio Bo 2024-05-18 14:22:45 -07:00 committed by GitHub
parent ed4d4d3f3a
commit 86b383ede6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 287 additions and 80 deletions

View File

@ -3,7 +3,7 @@ name: Build main
on:
push:
branches:
- main
- '**'
permissions:
contents: read
@ -28,5 +28,9 @@ jobs:
run: nix build -L --no-link --keep-going '.?submodules=1#decomp.debug.mingw32.gcc'
- name: Build Decomp Debug Mingw32 Clang
run: nix build -L --no-link --keep-going '.?submodules=1#decomp.debug.mingw32.clang'
- name: Build Retail Release Mingw32 Clang
run: nix build -L --no-link --keep-going '.?submodules=1#retail.release.mingw32.clang'
- name: Build Retail Release Linux32 GCC
run: nix build -L --no-link --keep-going '.?submodules=1#retail.release.native32.gcc'
- name: Build Server Release Linux32 GCC
run: nix build -L --no-link --keep-going '.?submodules=1#online-server.release.native32.gcc'
- name: Build Server Release ARM32 GCC
run: nix build -L --no-link --keep-going '.?submodules=1#online-server.release.arm32.gcc'

View File

@ -3,6 +3,13 @@
#include <common.h>
#endif
#ifdef __GNUC__
#define STATIC_ASSERT2(test_for_true) \
_Static_assert((test_for_true), "(" #test_for_true ") failed")
#else
#define STATIC_ASSERT2 static_assert
#endif
enum ServerList
{
EUR_LOOPER_1,
@ -37,29 +44,29 @@ struct OnlineCTR
// 0x0
int CurrState;
int IsBootedPS1;
// 0x8
char PageNumber; // allow negative
unsigned char CountPressX;
unsigned char NumDrivers;
unsigned char DriverID;
// 0xc
unsigned char boolLockedInTrack;
unsigned char boolLockedInCharacter;
unsigned char boolLockedInLap;
unsigned char lapID;
// 0x10
unsigned char serverCountry;
unsigned char serverRoom;
unsigned char serverLockIn1;
unsigned char serverLockIn2;
// determines if client and
// determines if client and
// emulator are still connected
char time[8];
char boolLockedInCharacters[8];
char nameBuffer[0xC*8];
@ -73,7 +80,7 @@ struct OnlineCTR
// 0xc8
// function pointers MUST come last,
// cause windows thinks pointers are
// cause windows thinks pointers are
// 8 bytes, while PSX thinks 4 bytes
void (*funcs[NUM_STATES]) ();
};
@ -97,7 +104,7 @@ enum ServerGiveMessageType
SG_ENDRACE,
SG_SERVERCLOSED,
SG_COUNT
};
@ -141,7 +148,7 @@ struct SG_MessageTrack
// 15 types, 15 bytes max
unsigned char type : 4;
unsigned char size : 4;
unsigned char trackID : 5;
unsigned char lapID : 2;
unsigned char boolAllowWeapons : 1;
@ -153,7 +160,7 @@ struct SG_MessageCharacter
// 15 types, 15 bytes max
unsigned char type : 4;
unsigned char size : 4;
// index 0 - 7
// boolLockedIn 0/1
// character 0 - 15
@ -201,14 +208,12 @@ struct SG_MessageEndRace
unsigned char time[3];
};
static_assert(sizeof(struct SG_Header) == 1);
static_assert(sizeof(struct SG_MessageName) == 14);
static_assert(sizeof(struct SG_MessageCharacter) == 2);
static_assert(sizeof(struct SG_MessageTrack) == 2);
static_assert(sizeof(struct SG_EverythingKart) == 13);
static_assert(sizeof(struct SG_MessageEndRace) == 5);
STATIC_ASSERT2(sizeof(struct SG_Header) == 1);
STATIC_ASSERT2(sizeof(struct SG_MessageName) == 14);
STATIC_ASSERT2(sizeof(struct SG_MessageCharacter) == 2);
STATIC_ASSERT2(sizeof(struct SG_MessageTrack) == 2);
STATIC_ASSERT2(sizeof(struct SG_EverythingKart) == 13);
STATIC_ASSERT2(sizeof(struct SG_MessageEndRace) == 5);
enum ClientGiveMessageType
{
@ -247,7 +252,7 @@ struct CG_MessageTrack
// 15 types, 15 bytes max
unsigned char type : 4;
unsigned char size : 4;
unsigned char trackID : 5;
unsigned char lapID : 2;
unsigned char boolAllowWeapons : 1;
@ -301,12 +306,12 @@ struct CG_MessageEndRace
unsigned char time[3];
};
static_assert(sizeof(struct CG_Header) == 1);
static_assert(sizeof(struct CG_MessageName) == 13);
static_assert(sizeof(struct CG_MessageCharacter) == 2);
static_assert(sizeof(struct CG_MessageTrack) == 2);
static_assert(sizeof(struct CG_EverythingKart) == 13);
static_assert(sizeof(struct CG_MessageEndRace) == 4);
STATIC_ASSERT2(sizeof(struct CG_Header) == 1);
STATIC_ASSERT2(sizeof(struct CG_MessageName) == 13);
STATIC_ASSERT2(sizeof(struct CG_MessageCharacter) == 2);
STATIC_ASSERT2(sizeof(struct CG_MessageTrack) == 2);
STATIC_ASSERT2(sizeof(struct CG_EverythingKart) == 13);
STATIC_ASSERT2(sizeof(struct CG_MessageEndRace) == 4);
// my functions
void StatePC_Launch_EnterPID();
@ -351,4 +356,4 @@ void StatePS1_Game_WaitForRace();
void StatePS1_Game_StartRace();
void StatePS1_Game_EndRace();
#endif
#endif

62
flake.lock generated
View File

@ -1,13 +1,26 @@
{
"nodes": {
"flake-schemas": {
"locked": {
"narHash": "sha256-ifw8Td8kD08J8DxFbYjeIx5naHcDLz7s2IFP3X42I/U=",
"rev": "c702cbb663d6d70bbb716584a2ee3aeb35017279",
"revCount": 21,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/DeterminateSystems/flake-schemas/0.1.1/018a4c59-80e1-708a-bb4d-854930c20f72/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/0.1.1.tar.gz"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1712439257,
"narHash": "sha256-aSpiNepFOMk9932HOax0XwNxbA38GOUVOiXfUVPOrck=",
"rev": "ff0dbd94265ac470dda06a657d5fe49de93b4599",
"revCount": 608655,
"lastModified": 1715787315,
"narHash": "sha256-cYApT0NXJfqBkKcci7D9Kr4CBYZKOQKDYA23q8XNuWg=",
"rev": "33d1e753c82ffc557b4a585c77de43d4c922ebb5",
"revCount": 626834,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.608655%2Brev-ff0dbd94265ac470dda06a657d5fe49de93b4599/018eb77a-ddbc-7dfa-9c9c-d5444d2bb52f/source.tar.gz"
"url": "https://api.flakehub.com/f/pinned/NixOS/nixpkgs/0.1.626834%2Brev-33d1e753c82ffc557b4a585c77de43d4c922ebb5/018f8037-433d-77f3-b4bb-e542e48f7fd6/source.tar.gz"
},
"original": {
"type": "tarball",
@ -16,7 +29,44 @@
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"systems": "systems",
"yafas": "yafas"
}
},
"systems": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
},
"yafas": {
"inputs": {
"flake-schemas": "flake-schemas",
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1695926485,
"narHash": "sha256-wNFFnItckgSs8XeYhhv8vlJs2WF09fSQaWgw4xkDqHQ=",
"rev": "7772afd6686458ca0ddbc599a52cf5d337367653",
"revCount": 4,
"type": "tarball",
"url": "https://api.flakehub.com/f/pinned/UbiqueLambda/yafas/0.1.4%2Brev-7772afd6686458ca0ddbc599a52cf5d337367653/018add18-ebb4-72c6-93fe-d1d8da361703/source.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://flakehub.com/f/UbiqueLambda/yafas/0.1.%2A.tar.gz"
}
}
},

View File

@ -1,43 +1,59 @@
{
inputs.nixpkgs.url = https://flakehub.com/f/NixOS/nixpkgs/0.1.*.tar.gz;
outputs = { self, nixpkgs }: with nixpkgs.legacyPackages.x86_64-linux; rec {
packages.x86_64-linux =
let
mkCTR = withDebug: withMods: {
native32 = with pkgsi686Linux; {
gcc = callPackage ./rebuild_PC { ctrModSDK = self; inherit withDebug withMods; };
clang = callPackage ./rebuild_PC { ctrModSDK = self; stdenv = clangStdenv; inherit withDebug withMods; };
};
mingw32 = with pkgsCross.mingw32; {
gcc = callPackage ./rebuild_PC { ctrModSDK = self; inherit withDebug withMods; };
clang = callPackage ./rebuild_PC { ctrModSDK = self; stdenv = clangStdenv; trustCompiler = true; inherit withDebug withMods; };
};
msvc = throw "TODO";
};
in
rec {
retail = {
release = mkCTR false false;
debug = mkCTR true false;
};
decomp = {
release = mkCTR false true;
debug = mkCTR true true;
};
};
# default
defaultPackage.x86_64-linux = stdenvNoCC.mkDerivation {
name = "ctr-join";
dontUnpack = true;
dontBuild = true;
installPhase = with packages.x86_64-linux; ''
mkdir $out
ln -s ${decomp.debug.native32.gcc} $out/native32-gcc
ln -s ${decomp.debug.native32.clang} $out/native32-clang
ln -s ${decomp.debug.mingw32.gcc} $out/mingw32-gcc
ln -s ${decomp.debug.mingw32.clang} $out/mingw32-clang
ln -s ${retail.release.mingw32.clang} $out/retail-release-mingw32-clang
'';
inputs = {
nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.1.*.tar.gz";
systems.url = "github:nix-systems/default-linux";
yafas = {
url = "https://flakehub.com/f/UbiqueLambda/yafas/0.1.*.tar.gz";
inputs.systems.follows = "systems";
};
};
outputs = { self, nixpkgs, yafas, ... }: yafas.withAllSystems nixpkgs
(_: { pkgs, system }: with pkgs; rec {
packages =
let
pkgsARM32 = pkgsCross.armv7l-hf-multiplatform;
pkgs32 = if system == "x86_64-linux" then pkgsi686Linux else pkgsARM32;
mkCTR = withDebug: withMods: {
native32 = with pkgs32; {
gcc = callPackage ./rebuild_PC { ctrModSDK = self; inherit withDebug withMods; };
clang = callPackage ./rebuild_PC { ctrModSDK = self; stdenv = clangStdenv; inherit withDebug withMods; };
};
mingw32 = with pkgsCross.mingw32; {
gcc = callPackage ./rebuild_PC { ctrModSDK = self; inherit withDebug withMods; };
clang = callPackage ./rebuild_PC { ctrModSDK = self; stdenv = clangStdenv; trustCompiler = true; inherit withDebug withMods; };
};
};
mkOnline = withDebug: {
native32 = with pkgs32; {
gcc = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; inherit withDebug; };
clang = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; stdenv = clangStdenv; inherit withDebug; };
};
arm32 = with pkgsARM32; {
gcc = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; inherit withDebug; };
clang = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; stdenv = clangStdenv; inherit withDebug; };
};
mingw32 = with pkgsCross.mingw32; {
gcc = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; inherit withDebug; };
clang = callPackage ./mods/Windows/OnlineCTR/Network_PC/Server { ctrModSDK = self; stdenv = clangStdenv; trustCompiler = true; inherit withDebug; };
};
};
in
rec {
retail = {
release = mkCTR false false;
debug = mkCTR true false;
};
decomp = {
release = mkCTR false true;
debug = mkCTR true true;
};
online-server = {
release = mkOnline false;
debug = mkOnline true;
};
};
})
{ };
}

View File

@ -0,0 +1,7 @@
# CMake stuff
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
# Server compiled binary
ctr_srv

View File

@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.20)
project(OnlineCTR-Server C)
set(CMAKE_C_STANDARD 99)
# Include directories
include_directories(${PROJECT_SOURCE_DIR}/../include)
# Add the path to the enet library
link_directories(${PROJECT_SOURCE_DIR}/../lib)
# Source files
set(SOURCES SV_main.c)
# Create the executable
add_executable(ctr_srv ${SOURCES})
# Link with the enet library
target_link_libraries(ctr_srv enet)
# Compiler options
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(ctr_srv PRIVATE -Wno-int-conversion -Wno-incompatible-function-pointer-types -Wno-implicit-function-declaration -Wno-return-type)
endif()
# Debug options
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(ctr_srv PRIVATE -g -gdwarf-2 -O0)
else()
target_compile_options(ctr_srv PRIVATE -O2)
endif()

View File

@ -0,0 +1,5 @@
### Compiling
For Windows open `SV_main.c` with VisualStudio.
For [Mingw](https://www.mingw-w64.org/)/Linux/Darwin use the `CMakeList.txt`.
[Nix](https://nixos.org/download/#download-nix) users can use `flake.nix`or `./build-native-gcc.nix` instead.

View File

@ -1,6 +1,11 @@
#define _CRT_SECURE_NO_WARNINGS
#ifdef __WINDOWS__
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#endif
#include <stdio.h>
#include <time.h>
#include <enet/enet.h>
@ -120,7 +125,7 @@ void ProcessConnectEvent(ENetPeer* peer) {
peerInfos[id].peer = peer;
clientCount++;
// Debug only, also prints client name from CG_MessageName
// Debug only, also prints client name from CG_MessageName
// printf("Assigned ID %d to peer %s:%u.\n", id, hostname, peer->address.port);
// Send ClientID and clientCount back to all clients
@ -508,17 +513,19 @@ void usleep(__int64 usec)
}
#endif
int main()
int main(int argc, char *argv[])
{
printf(__DATE__);
printf("\n");
printf(__TIME__);
printf("\n\n");
#ifdef __WINDOWS__
HWND console = GetConsoleWindow();
RECT r;
GetWindowRect(console, &r); //stores the console's current dimensions
MoveWindow(console, r.left, r.top, 480, 240 + 35, TRUE);
#endif
//initialize enet
if (enet_initialize() != 0) {
@ -528,9 +535,27 @@ int main()
atexit(enet_deinitialize);
int port;
printf("Enter Port (0-65535): ");
scanf("%d", &port, sizeof(port));
printf("\n");
int boolIsPortArgument = 0;
//port argument reading
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--port") == 0 || strcmp(argv[i], "-p") == 0) {
boolIsPortArgument = 1;
if (i + 1 < argc) {
port = atoi(argv[i + 1]);
i++; //next is port number
} else {
fprintf(stderr, "Error: --port or -p requires a value.\n");
return 1;
}
}
}
if(!boolIsPortArgument){
printf("Enter Port (0-65535): ");
scanf("%d", &port, sizeof(port));
printf("\n");
}
ENetAddress address;
address.host = ENET_HOST_ANY;
@ -555,4 +580,4 @@ int main()
usleep(1);
ServerState_Tick();
}
}
}

View File

@ -0,0 +1,6 @@
#! /usr/bin/env nix-build
{ pkgs ? import <nixpkgs> {}
, pkgsCross ? pkgs.pkgsi686Linux
}:
pkgsCross.callPackage ./default.nix {}

View File

@ -0,0 +1,58 @@
{ lib
, stdenv
, cmake
, pkg-config
, enet
, ctrModSDK ? ./../../../../..
, withDebug ? true
}:
let
isWindows = stdenv.hostPlatform.uname.system == "Windows";
mainProgram = if isWindows then "ctr_srv.exe" else "ctr_srv";
path = "mods/Windows/OnlineCTR/Network_PC/Server";
in
stdenv.mkDerivation (_: {
pname = "CTR-SRV";
version = "0.0.1";
src = ctrModSDK;
sourceRoot =
if ctrModSDK == ./../../../../.. then "CTR-ModSDK/${path}"
else "source/${path}";
nativeBuildInputs = [ cmake pkg-config ];
buildInputs = [ enet ];
# Disables incompatible hardening
hardeningDisable = [ "format" ];
# Config
cmakeFlags = lib.optionals withDebug [ "-DCMAKE_BUILD_TYPE=Debug" ];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp ${mainProgram} $out/bin/
runHook postInstall
'';
# Keep debug symbols
dontStrip = withDebug;
# Shows the proper compile date in the logs
env.SOURCE_DATE_EPOCH = (builtins.currentTime or ctrModSDK.lastModified);
meta = {
description = "CTR Online Companion";
homepage = "https://github.com/CTR-tools/CTR-ModSDK";
license = lib.licenses.publicDomain;
maintainers = with lib.maintainers; [ pedrohlc ];
mainProgram = "ctr_srv";
platforms = lib.platforms.all;
};
})