Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement qextensions #119

Closed
wants to merge 13 commits into from
Closed
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
15 changes: 15 additions & 0 deletions Misc/qext_hello/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
id1/
hello/

qsrc/progs.dat
qsrc/files.dat
qsrc/progdefs.h

src/progdefs.h
src/*.o

*.so
*.dll
*.obj
*.exp
*.lib
25 changes: 25 additions & 0 deletions Misc/qext_hello/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.PHONY: clean run qsrc src hello

hello: qsrc src
mkdir -p hello
cp qsrc/progs.dat hello/progs.dat
cp src/hello_x86_64.so hello/hello_x86_64.so
cp src/hello_x86_64.dll hello/hello_x86_64.dll

qsrc:
make -C qsrc

src: qsrc
cp qsrc/progdefs.h src/progdefs.h
cd src; make -f Makefile.linux64
cd src; make -f Makefile.w64

clean:
rm -rf hello/*
make clean -C qsrc
make clean -C src

run: hello
quakespasm --args -nolauncher -basedir $(PWD) -game hello \
+extensions 1 +developer 1 +map start \
-width 1024 -height 768 -bpp 32 -window -nosound
156 changes: 156 additions & 0 deletions Misc/qext_hello/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
Quake Extensions

______________________________________________________________________

1. About

Quake Extensions (qextensions) are Dynamic Link Library (DLL) files on
Windows and Shared Object (SO) files on Linux. They allows external
code to be executed within QC programs.

In theory it works on macOS using the Dynamic Librariy (dylib), but I
do not have a Mac to test with.

2. Usage

The `extensions` cvar must be set to a non-zero value to enable
qextensions. If it is set through the command line `+extensions 1`
must come before `+map`.

In order to use qextensions the new built-in functions must be defined.
This usually happens in `defs.qc`:

...
float(string path) OpenExtension = #93;
string(float id, string cmd, string arg) CallExtensionString = #94;
float(float id, string cmd, float arg) CallExtensionNumber = #95;
vector(float id, string cmd, vector arg) CallExtensionVector = #96;
float(float id, string cmd, entity arg) CallExtensionEntity = #97;
...

To use a specific extension e.g. `hello`, the following steps needs to
be made:

1, Open/load the extension. This needs to be done only once.
The `void() worldspawn` is a perfect place to do that:

....
float hello_ext;
...
void() worldspawn =
{
...
hello_ext = OpenExtension("hello");
...
}
...

Note that the value returned from `OpenExtension` is the
extension descriptor. It needs to be saved globally, since
it will be used for calling the extension.

2, Call the extension functions.

...
num = CallExtensionNumber(hello_ext, "hello", num);
...

This call is blocking, meaning that the game will wait for the
extension to return before continuing. This may cause FPS drop
if extension is not optimised. If extension takes too long,
consider making asynchronous extension, where the result of
the work of the extension is collected in a separate call.

There are 4 function that can be called:

+ string(float id, string cmd, string arg) CallExtensionString;
+ float(float id, string cmd, float arg) CallExtensionNumber;
+ vector(float id, string cmd, vector arg) CallExtensionVector;
+ float(float id, string cmd, entity arg) CallExtensionEntity;

The functions can not modify their arguments.
More then one qextension can be used simultaneously.

3. Creating

qextensions can be created using any programming language that can
output native .DLL/.SO files. This example code is written in C.

The qextension must export exactly 5 functions:

+ int qextension_version (void)
+ const char* qextension_string (const char *cmd, const char *arg)
+ float qextension_number (const char *cmd, float arg)
+ float* qextension_vector (const char *cmd, vec3_t arg)
+ float qextension_entity (const char *cmd, edict_t *arg)

In Windows the function must be preceded with `__declspec (dllexport)`
if MSVC is used.

The `qextension_version` must return 1. It is the version of the
qextension system, not the specific extension.

Since the qextension is a native file, different platforms require
different library files. OpenExtension("hello") will try to find
the underlying library file depending on the platform quakespasm
is running on:

+ hello_x86.dll on 32 bit Windows systems
+ hello_x86_64.dll on 64 bit Windows systems
+ hello_x86.so on 32 bit Linux systems
+ hello_x86_64.so on 64 bit Linux systems

If support of multiple platforms is required, each library file must
be installed. This requires cross-compilation or different build
systems.

4. Installation

qextensions use the filesystem of Quake. They can be placed into the
game directory or embedded into a .PAK file, e.g.:

id1/hello_x86.dll -> OpenExtension("hello");
id1/pak0.pak/hello_x86_64.dll -> OpenExtension("hello");
id1/ext/hello_x86.dll -> OpenExtension("ext/hello");

mymod/hello_x86_64.so -> OpenExtension("hello");
(only if `-game mymod` was used)


5. qext_hello

This is an example qextension. It supports 64 bit Linux and Windows
systems.

src/ contains the source code of the qextension
qsrc/ contains the source code of the mod uses the qextension

5.1. Building

qcc is required to be in the $PATH/%PATH% in order to compile progs.dat.

5.2. Linux

gcc is required to compile the qextension. Simply type:

make

A Win64 version also can be compiled, it requires the gcc-mingw-w64 toolchain.
If it is not present remove the following line from Makefile.

cd src; make -f Makefile.w64

5.3. Windows

Open an `x64 Native Tools Command Prompt for VS X` ant type:

build.bat

5.4. Running

To run the hello qextension copy the generated hello folder to the folder of
quakespasm (where id1 lives) then type:

quakespasm --args -game hello +extensions 1 +developer 1 +map start

If you see the outside of the start map the extension is working.
21 changes: 21 additions & 0 deletions Misc/qext_hello/build.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
echo off

mkdir hello 2> NUL

cd qsrc
qcc || goto :error
copy "progdefs.h" "..\src\progdefs.h" || goto :error
copy "progs.dat" "..\hello\progs.dat" || goto :error
cd ..

cd src
cl.exe /DWIN32 /D_MSVC_VER hello.c /link /DLL /OUT:hello_x86_64.dll
copy "hello_x86_64.dll" "..\hello\hello_x86_64.dll" || goto :error
cd ..

goto :EOF

:error
echo ---------- ERROR ----------
cd ..
exit /b 1
11 changes: 11 additions & 0 deletions Misc/qext_hello/qsrc/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CC := qcc

SRC := $(wildcard *.qc)

progs.dat: $(SRC)
$(CC)

clean:
rm -rf progs.dat
rm -rf files.dat
rm -rf progdefs.h
Loading
Loading