Wren testing
This commit is contained in:
+3
-4
@@ -90,11 +90,10 @@ void KeyDown(kbutton_t *b) {
|
||||
|
||||
void KeyUp(kbutton_t *b) {
|
||||
int k;
|
||||
const char *c;
|
||||
|
||||
c = command::argv(1)->c_str();
|
||||
if (c[0])
|
||||
k = atoi(c);
|
||||
auto c = command::argv(1);
|
||||
if (c.has_value())
|
||||
k = atoi(c->c_str());
|
||||
else {
|
||||
// typed manually at the console, assume for unsticking, so clear all
|
||||
b->down[0] = b->down[1] = 0;
|
||||
|
||||
+6
-2
@@ -446,14 +446,18 @@ namespace command {
|
||||
return; // not really connected
|
||||
|
||||
cls.message.write_byte(clc_stringcmd);
|
||||
if (std::is_eq(common::strcasecmp(argv(0).value_or(""), "cmd"))) {
|
||||
if (std::is_neq(common::strcasecmp(argv(0).value_or(""), "cmd"))) {
|
||||
if (argc() > 1) {
|
||||
cls.message.write_string(std::format("{} {}", argv(0).value(), args()));
|
||||
} else {
|
||||
cls.message.write_string(std::format("{} \n", argv(0).value()));
|
||||
}
|
||||
} else {
|
||||
cls.message.write_string("\n");
|
||||
if (argc() > 1) {
|
||||
cls.message.write_string(std::format("{}", args()));
|
||||
} else {
|
||||
cls.message.write_string("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1257,9 +1257,9 @@ static pack_t *COM_LoadPackFile(const char *packfile) {
|
||||
Sys_Error("Error reading %s", packfile);
|
||||
|
||||
// crc the directory to check for modifications
|
||||
CRC_Init(&crc);
|
||||
crc::init(&crc);
|
||||
for (i = 0; i < header.dirlen; i++)
|
||||
CRC_ProcessByte(&crc, ((byte *) info)[i]);
|
||||
crc::process_byte(&crc, ((byte *) info)[i]);
|
||||
if (crc != PAK0_CRC_V106 && crc != PAK0_CRC_V101 && crc != PAK0_CRC_V100)
|
||||
com_modified = true;
|
||||
|
||||
|
||||
+1
-1
@@ -893,7 +893,7 @@ static void Mod_LoadEntities (lump_t *l)
|
||||
|
||||
mark = Hunk_LowMark();
|
||||
if (l->filelen > 0) {
|
||||
crc = CRC_Block(mod_base + l->fileofs, l->filelen - 1);
|
||||
crc = crc::block(mod_base + l->fileofs, l->filelen - 1);
|
||||
}
|
||||
|
||||
q_strlcpy(basemapname, loadmodel->name, sizeof(basemapname));
|
||||
|
||||
+4
-4
@@ -1057,7 +1057,7 @@ static void TexMgr_LoadImage8(gltexture_t *glt, byte *data) {
|
||||
// HACK HACK HACK -- taken from tomazquake
|
||||
if (strstr(glt->name, "shot1sid") &&
|
||||
glt->width == 32 && glt->height == 32 &&
|
||||
CRC_Block(data, 1024) == 65393) {
|
||||
crc::block(data, 1024) == 65393) {
|
||||
// This texture in b_shell1.bsp has some of the first 32 pixels painted white.
|
||||
// They are invisible in software, but look really ugly in GL. So we just copy
|
||||
// 32 pixels from the bottom to make it look nice.
|
||||
@@ -1160,13 +1160,13 @@ gltexture_t *TexMgr_LoadImage(qmodel_t *owner, const char *name, int width, int
|
||||
// cache check
|
||||
switch (format) {
|
||||
case SRC_INDEXED:
|
||||
crc = CRC_Block(data, width * height);
|
||||
crc = crc::block(data, width * height);
|
||||
break;
|
||||
case SRC_LIGHTMAP:
|
||||
crc = CRC_Block(data, width * height * lightmap_bytes);
|
||||
crc = crc::block(data, width * height * lightmap_bytes);
|
||||
break;
|
||||
case SRC_RGBA:
|
||||
crc = CRC_Block(data, width * height * 4);
|
||||
crc = crc::block(data, width * height * 4);
|
||||
break;
|
||||
default: /* not reachable but avoids compiler warnings */
|
||||
crc = 0;
|
||||
|
||||
@@ -1335,6 +1335,7 @@ void GL_EndRendering(void) {
|
||||
#if defined(USE_SDL2)
|
||||
SDL_GL_SwapWindow(draw_context);
|
||||
#else
|
||||
|
||||
SDL_GL_SwapBuffers();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#include "bgmusic.hpp"
|
||||
#include <csetjmp>
|
||||
|
||||
#include "pr_wren.hpp"
|
||||
|
||||
/*
|
||||
|
||||
A server can allways be started, even if the system started out as a client
|
||||
@@ -792,6 +794,7 @@ void Host_Init(void) {
|
||||
console::init();
|
||||
}
|
||||
PR_Init();
|
||||
wren::init();
|
||||
Mod_Init();
|
||||
NET_Init();
|
||||
SV_Init();
|
||||
|
||||
+15
-3
@@ -21,9 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
// sv_edict.c -- entity dictionary
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "pr_wren.hpp"
|
||||
#include "quakedef.hpp"
|
||||
#include "wren.hpp"
|
||||
|
||||
dprograms_t *progs;
|
||||
dfunction_t *pr_functions;
|
||||
@@ -1006,6 +1009,7 @@ void ED_LoadFromFile (const char *data)
|
||||
pr_global_struct->time = sv.time;
|
||||
|
||||
std::istringstream ss{data};
|
||||
sv.wentities = std::map<int, WrenHandle*>{};
|
||||
|
||||
|
||||
// parse ents
|
||||
@@ -1022,8 +1026,12 @@ void ED_LoadFromFile (const char *data)
|
||||
ent = EDICT_NUM(0);
|
||||
else
|
||||
ent = ED_Alloc ();
|
||||
std::istringstream copy{data};
|
||||
copy.seekg(ss.tellg());
|
||||
ED_ParseEdict (ss, ent);
|
||||
|
||||
auto vars = wren::parse_fields(copy);
|
||||
|
||||
// remove things from different skill levels or deathmatch
|
||||
if (deathmatch.value)
|
||||
{
|
||||
@@ -1069,6 +1077,11 @@ void ED_LoadFromFile (const char *data)
|
||||
|
||||
pr_global_struct->self = EDICT_TO_PROG(ent);
|
||||
PR_ExecuteProgram (func - pr_functions);
|
||||
auto e = wren::create_entity(PR_GetString(ent->v.classname), vars);
|
||||
if (e.has_value()) {
|
||||
ent->wid = e->first;
|
||||
sv.wentities[e->first] = e->second;
|
||||
}
|
||||
}
|
||||
|
||||
console::debug ("%i entities inhibited\n", inhibit);
|
||||
@@ -1175,7 +1188,7 @@ void PR_LoadProgs (void)
|
||||
for (i = 0; i < GEFV_CACHESIZE; i++)
|
||||
gefvCache[i].field[0] = 0;
|
||||
|
||||
CRC_Init (&pr_crc);
|
||||
crc::init (&pr_crc);
|
||||
|
||||
progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat", NULL);
|
||||
if (!progs)
|
||||
@@ -1183,7 +1196,7 @@ void PR_LoadProgs (void)
|
||||
console::debug ("Programs occupy %iK.\n", com_filesize/1024);
|
||||
|
||||
for (i = 0; i < com_filesize; i++)
|
||||
CRC_ProcessByte (&pr_crc, ((byte *)progs)[i]);
|
||||
crc::process_byte (&pr_crc, ((byte *)progs)[i]);
|
||||
|
||||
// byte swap the header
|
||||
for (i = 0; i < (int) sizeof(*progs) / 4; i++)
|
||||
@@ -1296,7 +1309,6 @@ void PR_Init (void)
|
||||
saved4.inscribe();
|
||||
}
|
||||
|
||||
|
||||
edict_t *EDICT_NUM(int n)
|
||||
{
|
||||
if (n < 0 || n >= sv.max_edicts)
|
||||
|
||||
@@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "pr_wren.hpp"
|
||||
#include "quakedef.hpp"
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
#include "pr_wren.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iosfwd>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "quakedef.hpp"
|
||||
#include "q_stdinc.hpp"
|
||||
#include "console.hpp"
|
||||
#include "sys.hpp"
|
||||
#include "vec.hpp"
|
||||
#include "../wren/vm/wren_debug.h"
|
||||
|
||||
|
||||
namespace {
|
||||
WrenVM *_vm;
|
||||
int entity_counter{0};
|
||||
std::map<std::string, WrenHandle *> class_handles{};
|
||||
std::map<std::string, WrenHandle *> function_handles{};
|
||||
std::map<int, wren::entvars *> entity_vars{};
|
||||
bool silentErrors{false};
|
||||
|
||||
void writeFn(WrenVM *vm, const char *text) {
|
||||
console::info("%s", text);
|
||||
}
|
||||
|
||||
void errorFn(WrenVM *vm, WrenErrorType type, const char *module, int line,
|
||||
const char *message) {
|
||||
Host_Error("%s:%d - %s", module, line, message);
|
||||
}
|
||||
|
||||
void _set_size(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 7);
|
||||
auto id = static_cast<int>(wrenGetSlotDouble(vm, 1));
|
||||
auto vars = entity_vars[id];
|
||||
wrenGetListElement(vm, 2, 0, 4);
|
||||
wrenGetListElement(vm, 2, 1, 5);
|
||||
wrenGetListElement(vm, 2, 2, 6);
|
||||
const auto mins = math::vec3f{
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 4)), static_cast<float>(wrenGetSlotDouble(vm, 5)),
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 6))
|
||||
};
|
||||
wrenGetListElement(vm, 3, 0, 4);
|
||||
wrenGetListElement(vm, 3, 1, 5);
|
||||
wrenGetListElement(vm, 3, 2, 6);
|
||||
const auto maxs = math::vec3f{
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 4)), static_cast<float>(wrenGetSlotDouble(vm, 5)),
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 6))
|
||||
};
|
||||
|
||||
vars->mins = mins;
|
||||
vars->maxs = maxs;
|
||||
}
|
||||
|
||||
// error(err)
|
||||
void _error(WrenVM *vm) {
|
||||
const std::string err = wrenGetSlotString(vm, 1);
|
||||
|
||||
console::info("WREN ERROR: %s\n", err.c_str());
|
||||
// TODO: see if there's literally any way to get the call stack
|
||||
Host_Error("Program error");
|
||||
}
|
||||
|
||||
// normalize(vec)
|
||||
void _normalize(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 5);
|
||||
wrenGetListElement(vm, 1, 0, 2);
|
||||
wrenGetListElement(vm, 1, 1, 3);
|
||||
wrenGetListElement(vm, 1, 2, 4);
|
||||
auto vec = math::vec3f{
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 2)), static_cast<float>(wrenGetSlotDouble(vm, 3)),
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 4))
|
||||
};
|
||||
vec = vec.normalize();
|
||||
wrenEnsureSlots(vm, 4);
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
wrenSetSlotDouble(vm, 1, vec.x);
|
||||
wrenSetSlotDouble(vm, 2, vec.y);
|
||||
wrenSetSlotDouble(vm, 3, vec.z);
|
||||
wrenInsertInList(vm, 0, 0, 1);
|
||||
wrenInsertInList(vm, 0, 1, 2);
|
||||
wrenInsertInList(vm, 0, 2, 3);
|
||||
}
|
||||
|
||||
void _remove(WrenVM *vm) {
|
||||
const auto id = static_cast<int>(wrenGetSlotDouble(vm, 1));
|
||||
const auto handle = sv.wentities[id];
|
||||
sv.wentities[id] = nullptr;
|
||||
entity_vars[id]->free = true;
|
||||
if (handle != nullptr) {
|
||||
wrenReleaseHandle(vm, handle);
|
||||
}
|
||||
}
|
||||
|
||||
void _precache_model(WrenVM *vm) {
|
||||
const auto model = wrenGetSlotString(vm, 1);
|
||||
|
||||
for (auto i = 0; i < MAX_MODELS; i++) {
|
||||
if (sv.model_precache[i] == nullptr) {
|
||||
sv.model_precache[i] = model;
|
||||
sv.models[i] = Mod_ForName(model, true);
|
||||
return;
|
||||
}
|
||||
if (std::strcmp(sv.model_precache[i], model) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// preache_sound(sound)
|
||||
void _precache_sound(WrenVM *vm) {
|
||||
const auto sample = wrenGetSlotString(vm, 1);
|
||||
|
||||
if (sv.state != ss_loading) {
|
||||
console::info("Precache can only be done in spawn functions");
|
||||
Host_Error("Wren Error");
|
||||
}
|
||||
|
||||
for (auto &i: sv.sound_precache) {
|
||||
if (i == nullptr) {
|
||||
i = sample;
|
||||
break;
|
||||
}
|
||||
if (std::strcmp(i, sample) == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _find_radius(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 5);
|
||||
auto radius = static_cast<float>(wrenGetSlotDouble(vm, 2));
|
||||
radius *= radius;
|
||||
wrenGetListElement(vm, 1, 0, 2);
|
||||
wrenGetListElement(vm, 1, 1, 3);
|
||||
wrenGetListElement(vm, 1, 2, 4);
|
||||
auto origin = math::vec3f{
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 2)), static_cast<float>(wrenGetSlotDouble(vm, 3)),
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 4))
|
||||
};
|
||||
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
auto count = 0;
|
||||
for (auto [id, ent]: sv.wentities) {
|
||||
float d;
|
||||
//
|
||||
// if (ent->v.solid == SOLID_NOT)
|
||||
// continue;
|
||||
auto vars = entity_vars[id];
|
||||
|
||||
d = origin.x - (vars->origin.x + (vars->mins.x + vars->maxs.x) * 0.5f);
|
||||
float lensq = d * d;
|
||||
if (lensq > radius)
|
||||
continue;
|
||||
d = origin.y - (vars->origin.y + (vars->mins.y + vars->maxs.y) * 0.5f);
|
||||
lensq += d * d;
|
||||
if (lensq > radius)
|
||||
continue;
|
||||
d = origin.z - (vars->origin.z + (vars->mins.z + vars->maxs.z) * 0.5f);
|
||||
lensq += d * d;
|
||||
if (lensq > radius)
|
||||
continue;
|
||||
|
||||
wrenSetSlotHandle(vm, 1, ent);
|
||||
wrenInsertInList(vm, 0, count++, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// sound(entity, channel, sample, volume, attenuation)
|
||||
void _sound(WrenVM *vm) {
|
||||
const auto entity = static_cast<int>(wrenGetSlotDouble(vm, 1));
|
||||
const auto channel = static_cast<int>(wrenGetSlotDouble(vm, 2));
|
||||
std::string sample{};
|
||||
if (wrenGetSlotType(vm, 3) == WREN_TYPE_STRING) {
|
||||
sample = wrenGetSlotString(vm, 3);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
const auto volume = static_cast<int>(wrenGetSlotDouble(vm, 4) * 255);
|
||||
const auto attenuation = static_cast<float>(wrenGetSlotDouble(vm, 5));
|
||||
|
||||
SV_StartSound(&sv.edicts[entity], channel, sample.c_str(), volume, attenuation);
|
||||
}
|
||||
|
||||
net::msg *_write_destination(const int dest) {
|
||||
switch (dest) {
|
||||
case 0: // MSG_BROADCAST
|
||||
return &sv.datagram;
|
||||
|
||||
case 1: {
|
||||
// MSG_ONE
|
||||
auto ent = PROG_TO_EDICT(pr_global_struct->msg_entity);
|
||||
auto entnum = NUM_FOR_EDICT(ent);
|
||||
if (entnum < 1 || entnum > svs.maxclients)
|
||||
PR_RunError("WriteDest: not a client");
|
||||
return &svs.clients[entnum - 1].message;
|
||||
}
|
||||
|
||||
case 2: // MSG_ALL
|
||||
return &sv.reliable_datagram;
|
||||
|
||||
case 3: // MSG_INIT
|
||||
return &sv.signons[sv.current_signon];
|
||||
|
||||
default:
|
||||
PR_RunError("_write_destination: bad destination");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _write_byte(WrenVM *vm) {
|
||||
auto dest = _write_destination(static_cast<int>(wrenGetSlotDouble(vm, 1)));
|
||||
const auto value = static_cast<byte>(wrenGetSlotDouble(vm, 2));
|
||||
|
||||
dest->write_byte(value);
|
||||
}
|
||||
|
||||
void _write_char(WrenVM *vm) {
|
||||
auto dest = _write_destination(static_cast<int>(wrenGetSlotDouble(vm, 1)));
|
||||
const auto value = static_cast<char>(wrenGetSlotDouble(vm, 2));
|
||||
|
||||
dest->write_char(value);
|
||||
}
|
||||
|
||||
void _write_coord(WrenVM *vm) {
|
||||
auto dest = _write_destination(static_cast<int>(wrenGetSlotDouble(vm, 1)));
|
||||
const auto value = static_cast<float>(wrenGetSlotDouble(vm, 2));
|
||||
|
||||
dest->write_coord(value, static_cast<net::rmq_flags>(sv.protocolflags));
|
||||
}
|
||||
|
||||
void _time(WrenVM *vm) {
|
||||
wrenSetSlotDouble(vm, 0, sv.time);
|
||||
}
|
||||
|
||||
void _deathmatch(WrenVM *vm) {
|
||||
wrenSetSlotBool(vm, 0, deathmatch.value != 0.0);
|
||||
}
|
||||
|
||||
void _entvars_allocate(WrenVM *vm) {
|
||||
auto vars = static_cast<wren::entvars *>(wrenSetSlotNewForeign(vm, 0, 0, sizeof(wren::entvars)));
|
||||
vars->id = entity_counter++;
|
||||
vars->origin = math::ORIGIN;
|
||||
vars->next_think = 0;
|
||||
entity_vars[vars->id] = vars;
|
||||
}
|
||||
|
||||
void _entvars_id(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
|
||||
wrenSetSlotDouble(vm, 0, vars->id);
|
||||
}
|
||||
|
||||
void _entvars_origin(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 4);
|
||||
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
|
||||
wrenSetSlotNewList(vm, 0);
|
||||
wrenSetSlotDouble(vm, 1, vars->origin.x);
|
||||
wrenSetSlotDouble(vm, 2, vars->origin.y);
|
||||
wrenSetSlotDouble(vm, 3, vars->origin.z);
|
||||
wrenInsertInList(vm, 0, 0, 1);
|
||||
wrenInsertInList(vm, 0, 1, 2);
|
||||
wrenInsertInList(vm, 0, 2, 3);
|
||||
}
|
||||
|
||||
void _entvars_set_origin(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 5);
|
||||
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
|
||||
wrenGetListElement(vm, 1, 0, 2);
|
||||
wrenGetListElement(vm, 1, 1, 3);
|
||||
wrenGetListElement(vm, 1, 2, 4);
|
||||
const auto origin = math::vec3f{
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 2)), static_cast<float>(wrenGetSlotDouble(vm, 3)),
|
||||
static_cast<float>(wrenGetSlotDouble(vm, 4))
|
||||
};
|
||||
|
||||
vars->origin = origin;
|
||||
}
|
||||
|
||||
void _entvars_next_think(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
wrenEnsureSlots(vm, 1);
|
||||
wrenSetSlotDouble(vm, 0, vars->next_think);
|
||||
}
|
||||
|
||||
void _entvars_set_next_think(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
const auto next_think = static_cast<float>(wrenGetSlotDouble(vm, 1));
|
||||
vars->next_think = next_think;
|
||||
}
|
||||
|
||||
void _entvars_target_name(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
if (!vars->target_name.empty()) {
|
||||
wrenSetSlotString(vm, 0, vars->target_name.c_str());
|
||||
} else {
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void _entvars_class_name(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
if (!vars->class_name.empty()) {
|
||||
wrenSetSlotString(vm, 0, vars->class_name.c_str());
|
||||
} else {
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void _entvars_spawn_flags(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
wrenSetSlotDouble(vm, 0, vars->spawn_flags);
|
||||
}
|
||||
|
||||
void _entvars_set_spawn_flags(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
const auto spawn_flags = static_cast<int>(wrenGetSlotDouble(vm, 1));
|
||||
vars->spawn_flags = spawn_flags;
|
||||
}
|
||||
|
||||
void _entvars_sounds(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
wrenSetSlotDouble(vm, 0, vars->sounds);
|
||||
}
|
||||
|
||||
void _entvars_noise(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
if (!vars->noise.empty()) {
|
||||
wrenSetSlotString(vm, 0, vars->noise.c_str());
|
||||
} else {
|
||||
wrenSetSlotNull(vm, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void _entvars_set_noise(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
vars->noise = wrenGetSlotString(vm, 1);
|
||||
}
|
||||
|
||||
std::map<std::string, wren::typing> _entvars_type_map{
|
||||
{"targetname", wren::typing::STRING},
|
||||
{"classname", wren::typing::STRING},
|
||||
{"origin", wren::typing::VECTOR},
|
||||
{"spawnflags", wren::typing::INT},
|
||||
{"sounds", wren::typing::FLOAT}
|
||||
};
|
||||
|
||||
|
||||
void _entvars_field_setter(WrenVM *vm) {
|
||||
wrenEnsureSlots(vm, 9);
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
std::string name = wrenGetSlotString(vm, 1);
|
||||
if (name == "classname") {
|
||||
vars->class_name = wrenGetSlotString(vm, 2);
|
||||
return;
|
||||
}
|
||||
if (name == "targetname") {
|
||||
vars->target_name = wrenGetSlotString(vm, 2);
|
||||
return;
|
||||
}
|
||||
if (name == "origin") {
|
||||
vars->origin = math::vec3f{};
|
||||
wrenGetListElement(vm, 2, 0, 3);
|
||||
wrenGetListElement(vm, 2, 1, 4);
|
||||
wrenGetListElement(vm, 2, 2, 5);
|
||||
vars->origin.x = static_cast<float>(wrenGetSlotDouble(vm, 3));
|
||||
vars->origin.y = static_cast<float>(wrenGetSlotDouble(vm, 4));
|
||||
vars->origin.z = static_cast<float>(wrenGetSlotDouble(vm, 5));
|
||||
return;
|
||||
}
|
||||
if (name == "spawnflags") {
|
||||
vars->spawn_flags = static_cast<int>(wrenGetSlotDouble(vm, 2));
|
||||
return;
|
||||
}
|
||||
if (name == "sounds") {
|
||||
vars->sounds = static_cast<float>(wrenGetSlotDouble(vm, 2));
|
||||
return;
|
||||
}
|
||||
Host_Error("_entvars_field_setter: tried to set %s, but no setter defined", name.c_str());
|
||||
}
|
||||
|
||||
void _entvars_field_type(WrenVM *vm) {
|
||||
const auto vars = static_cast<wren::entvars *>(wrenGetSlotForeign(vm, 0));
|
||||
const std::string name = wrenGetSlotString(vm, 1);
|
||||
auto kind = wren::typing::UNDEFINED;
|
||||
if (_entvars_type_map.contains(name)) {
|
||||
kind = _entvars_type_map[name];
|
||||
}
|
||||
wrenSetSlotDouble(vm, 0, static_cast<int>(kind));
|
||||
}
|
||||
|
||||
WrenForeignClassMethods bindForeignClassFn(
|
||||
WrenVM *vm, const char *module, const char *className) {
|
||||
const std::string modname{module};
|
||||
const std::string classname{className};
|
||||
WrenForeignClassMethods methods{.allocate = nullptr, .finalize = nullptr};
|
||||
|
||||
if (classname == "EntityVariables") {
|
||||
methods.allocate = _entvars_allocate;
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
|
||||
std::map<std::string, std::map<std::string, WrenForeignMethodFn> > exported_functions{
|
||||
{
|
||||
"EntityVariables",
|
||||
{
|
||||
{"id", _entvars_id},
|
||||
{"origin", _entvars_origin},
|
||||
{"origin=(_)", _entvars_set_origin},
|
||||
{"next_think", _entvars_next_think},
|
||||
{"next_think=(_)", _entvars_set_next_think},
|
||||
{"target_name", _entvars_target_name},
|
||||
{"class_name", _entvars_class_name},
|
||||
{"field_setter(_,_)", _entvars_field_setter},
|
||||
{"field_type(_)", _entvars_field_type},
|
||||
{"spawn_flags", _entvars_spawn_flags},
|
||||
{"spawn_flags=(_)", _entvars_set_spawn_flags},
|
||||
{"sounds", _entvars_sounds},
|
||||
{"noise", _entvars_noise},
|
||||
{"noise=(_)", _entvars_set_noise},
|
||||
},
|
||||
},
|
||||
{
|
||||
"Quake",
|
||||
{
|
||||
{"set_size(_,_,_)", _set_size},
|
||||
{"error(_)", _error},
|
||||
{"normalize(_)", _normalize},
|
||||
{"remove(_)", _remove},
|
||||
{"precache_model(_)", _precache_model},
|
||||
{"precache_sound(_)", _precache_sound},
|
||||
{"find_radius(_,_)", _find_radius},
|
||||
{"sound(_,_,_,_,_)", _sound},
|
||||
{"write_byte(_,_)", _write_byte},
|
||||
{"write_char(_,_)", _write_char},
|
||||
{"write_coord(_,_)", _write_coord},
|
||||
{"time", _time},
|
||||
{"deathmatch", _deathmatch},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
WrenForeignMethodFn bindForeignMethodFn(WrenVM *vm, const char *module, const char *className, bool isStatic,
|
||||
const char *signature) {
|
||||
const std::string modname{module};
|
||||
const std::string classname{className};
|
||||
const std::string sig{signature};
|
||||
|
||||
if (modname == "quake") {
|
||||
if (exported_functions.contains(classname)) {
|
||||
if (auto &clazz = exported_functions[classname]; clazz.contains(sig)) {
|
||||
return clazz[sig];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void wren::init() {
|
||||
WrenConfiguration config;
|
||||
wrenInitConfiguration(&config);
|
||||
config.writeFn = writeFn;
|
||||
config.errorFn = errorFn;
|
||||
config.bindForeignMethodFn = bindForeignMethodFn;
|
||||
config.bindForeignClassFn = bindForeignClassFn;
|
||||
_vm = wrenNewVM(&config);
|
||||
function_handles["vars"] = nullptr;
|
||||
function_handles["new()"] = nullptr;
|
||||
function_handles["spawn()"] = nullptr;
|
||||
function_handles["think()"] = nullptr;
|
||||
function_handles["field_type(_)"] = nullptr;
|
||||
function_handles["field_setter(_,_)"] = nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::pair<int, WrenHandle *> > wren::create_entity(const std::string &classname,
|
||||
std::map<std::string, std::string> pairs) {
|
||||
if (!wrenHasVariable(_vm, "quake", classname.c_str())) {
|
||||
console::safe_print("No Wren spawn function for %s\n", classname.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
wrenEnsureSlots(_vm, 2);
|
||||
if (!class_handles.contains(classname)) {
|
||||
wrenEnsureSlots(_vm, 1);
|
||||
wrenGetVariable(_vm, "quake", classname.c_str(), 0);
|
||||
class_handles[classname.c_str()] = wrenGetSlotHandle(_vm, 0);
|
||||
}
|
||||
|
||||
wrenSetSlotHandle(_vm, 0, class_handles[classname]);
|
||||
wrenCall(_vm, function_handles["new()"]);
|
||||
const auto handle = wrenGetSlotHandle(_vm, 0);
|
||||
|
||||
for (const auto &[key, value]: pairs) {
|
||||
set_field(handle, key, value);
|
||||
}
|
||||
|
||||
entity_vars[entity_counter - 1]->next_think = sv.time;
|
||||
|
||||
wrenSetSlotHandle(_vm, 0, handle);
|
||||
wrenCall(_vm, function_handles["spawn()"]);
|
||||
if (entity_vars[entity_counter - 1]->free) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
console::info("loaded %s\n", classname.c_str());
|
||||
|
||||
return std::pair{entity_counter - 1, handle};
|
||||
}
|
||||
|
||||
|
||||
void wren::load_progs() {
|
||||
wrenNukeModules(_vm);
|
||||
std::filesystem::directory_iterator dir{"id1/wprog"};
|
||||
for (auto f: dir) {
|
||||
if (f.path().extension() == ".wren") {
|
||||
std::ifstream ifs{f.path()};
|
||||
std::stringstream ss{};
|
||||
ss << ifs.rdbuf();
|
||||
wrenInterpret(_vm, "quake", ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [name, handle] : class_handles) {
|
||||
if (handle) wrenReleaseHandle(_vm, handle);
|
||||
wrenEnsureSlots(_vm, 1);
|
||||
wrenGetVariable(_vm, "quake", name.c_str(), 0);
|
||||
class_handles[name] = wrenGetSlotHandle(_vm, 0);
|
||||
}
|
||||
|
||||
for (const auto &[signature, handle]: function_handles) {
|
||||
if (handle) wrenReleaseHandle(_vm, handle);
|
||||
function_handles[signature] = wrenMakeCallHandle(_vm, signature.c_str());
|
||||
}
|
||||
|
||||
entity_vars.clear();
|
||||
entity_counter = 0;
|
||||
|
||||
wrenEnsureSlots(_vm, 1);
|
||||
wrenGetVariable(_vm, "quake", "EntityVariables", 0);
|
||||
class_handles["EntityVariables"] = wrenGetSlotHandle(_vm, 0);
|
||||
wrenEnsureSlots(_vm, 1);
|
||||
wrenGetVariable(_vm, "quake", "Typing", 0);
|
||||
class_handles["Typing"] = wrenGetSlotHandle(_vm, 0);
|
||||
}
|
||||
|
||||
void wren::run_think(const int index) {
|
||||
const auto ent = sv.wentities[index];
|
||||
if (ent == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto vars = entity_vars[index];
|
||||
|
||||
auto thinktime = vars->next_think;
|
||||
if (thinktime <= 0 || thinktime > sv.time + host_frametime)
|
||||
return;
|
||||
|
||||
if (thinktime < sv.time)
|
||||
thinktime = sv.time;
|
||||
|
||||
// ent->oldthinktime = thinktime;
|
||||
// ent->oldframe = ent->v.frame; //johnfitz
|
||||
|
||||
vars->next_think = 0;
|
||||
|
||||
wrenEnsureSlots(_vm, 1);
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenCall(_vm, function_handles["think()"]);
|
||||
}
|
||||
|
||||
void wren::set_field(WrenHandle *ent, std::string key, std::string value) {
|
||||
wrenEnsureSlots(_vm, 9);
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenSetSlotString(_vm, 1, key.c_str());
|
||||
wrenCall(_vm, function_handles["field_type(_)"]);
|
||||
auto kind = static_cast<typing>(wrenGetSlotDouble(_vm, 0));
|
||||
switch (kind) {
|
||||
case typing::UNDEFINED:
|
||||
console::info("FIELD %s IS UNDEFINED\n", key.c_str());
|
||||
break;
|
||||
case typing::STRING:
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenSetSlotString(_vm, 1, key.c_str());
|
||||
wrenSetSlotString(_vm, 2, value.c_str());
|
||||
wrenCall(_vm, function_handles["field_setter(_,_)"]);
|
||||
break;
|
||||
case typing::INT: {
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenSetSlotString(_vm, 1, key.c_str());
|
||||
auto i = std::atoi(value.c_str());
|
||||
wrenSetSlotDouble(_vm, 2, i);
|
||||
wrenCall(_vm, function_handles["field_setter(_,_)"]);
|
||||
break;
|
||||
}
|
||||
case typing::FLOAT: {
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenSetSlotString(_vm, 1, key.c_str());
|
||||
auto i = static_cast<float>(std::atof(value.c_str()));
|
||||
wrenSetSlotDouble(_vm, 2, i);
|
||||
wrenCall(_vm, function_handles["field_setter(_,_)"]);
|
||||
break;
|
||||
}
|
||||
case typing::VECTOR: {
|
||||
math::vec3f res{};
|
||||
std::sscanf(value.c_str(), "%f %f %f", &res.x, &res.y, &res.z);
|
||||
wrenSetSlotHandle(_vm, 0, ent);
|
||||
wrenSetSlotString(_vm, 1, key.c_str());
|
||||
wrenSetSlotNewList(_vm, 2);
|
||||
wrenSetSlotDouble(_vm, 3, res.x);
|
||||
wrenSetSlotDouble(_vm, 4, res.y);
|
||||
wrenSetSlotDouble(_vm, 5, res.z);
|
||||
wrenInsertInList(_vm, 2, 0, 3);
|
||||
wrenInsertInList(_vm, 2, 1, 4);
|
||||
wrenInsertInList(_vm, 2, 2, 5);
|
||||
wrenCall(_vm, function_handles["field_setter(_,_)"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> wren::parse_fields(std::istringstream &ss) {
|
||||
std::map<std::string, std::string> vars{};
|
||||
bool anglehack{false};
|
||||
|
||||
|
||||
while (true) {
|
||||
auto token = common::parse_token(ss);
|
||||
if (token.has_value() && token->front() == '}') {
|
||||
break;
|
||||
}
|
||||
if (ss.eof()) {
|
||||
Host_Error("wren::parse_fields: EOF without closing brace");
|
||||
}
|
||||
|
||||
// anglehack is to allow QuakeEd to write single scalar angles
|
||||
// and allow them to be turned into vectors. (FIXME...)
|
||||
if (token == "angle") {
|
||||
token = "angles";
|
||||
anglehack = true;
|
||||
} else {
|
||||
anglehack = false;
|
||||
}
|
||||
|
||||
// FIXME: change light to _light to get rid of this hack
|
||||
if (token == "light") {
|
||||
token = "light_maplev";
|
||||
}
|
||||
|
||||
auto keyname = *token;
|
||||
|
||||
// another hack to fix keynames with trailing spaces
|
||||
while (!keyname.empty() && keyname.back() == ' ') {
|
||||
keyname.pop_back();
|
||||
}
|
||||
|
||||
// parse value
|
||||
// HACK: we allow truncation when reading the wad field,
|
||||
// otherwise maps using lots of wads with absolute paths
|
||||
// could cause a parse error
|
||||
token = common::parse_token(ss);
|
||||
if (ss.eof()) {
|
||||
Host_Error("wren::parse_fields: EOF without closing brace");
|
||||
}
|
||||
|
||||
if (!token->empty() && token->front() == '}') {
|
||||
Host_Error("wren::parse_fields: closing brace without data");
|
||||
}
|
||||
|
||||
|
||||
// keynames with a leading underscore are used for utility comments,
|
||||
// and are immediately discarded by quake
|
||||
if (keyname[0] == '_') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (anglehack) {
|
||||
token = std::format("0 {} 0", *token);
|
||||
}
|
||||
|
||||
vars[keyname] = *token;
|
||||
}
|
||||
|
||||
return vars;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "vec.hpp"
|
||||
#include "wren.hpp"
|
||||
|
||||
namespace wren {
|
||||
struct entvars {
|
||||
int id{-1};
|
||||
math::vec3f origin{};
|
||||
float next_think{0.0f};
|
||||
int spawn_flags{0};
|
||||
bool free{false};
|
||||
math::vec3f velocity{};
|
||||
math::vec3f angles{};
|
||||
math::vec3f mins{};
|
||||
math::vec3f maxs{};
|
||||
std::string class_name{};
|
||||
std::string target_name{};
|
||||
float sounds{0};
|
||||
std::string noise{};
|
||||
std::string noise1{};
|
||||
std::string noise2{};
|
||||
std::string noise3{};
|
||||
};
|
||||
|
||||
enum class typing {
|
||||
UNDEFINED = -1,
|
||||
INT = 1,
|
||||
FLOAT = 2,
|
||||
VECTOR = 3,
|
||||
STRING = 4
|
||||
};
|
||||
|
||||
void init();
|
||||
void load_progs();
|
||||
|
||||
std::optional<std::pair<int, WrenHandle*>> create_entity(const std::string &classname, std::map<std::string, std::string> pairs);
|
||||
void run_think(int index);
|
||||
void set_field(WrenHandle *ent, std::string key, std::string value);
|
||||
std::map<std::string, std::string> parse_fields(std::istringstream &ss);
|
||||
}
|
||||
@@ -42,6 +42,7 @@ typedef struct edict_s
|
||||
bool free;
|
||||
link_t area; /* linked to a division node or leaf */
|
||||
|
||||
int wid;
|
||||
int num_leafs;
|
||||
int leafnums[MAX_ENT_LEAFS];
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef QUAKE_SERVER_H
|
||||
#define QUAKE_SERVER_H
|
||||
#include "../wren/vm/wren_vm.h"
|
||||
|
||||
// server.h
|
||||
|
||||
@@ -61,6 +62,7 @@ typedef struct
|
||||
const char *lightstyles[MAX_LIGHTSTYLES];
|
||||
int num_edicts;
|
||||
int max_edicts;
|
||||
std::map<int, WrenHandle *> wentities;
|
||||
edict_t *edicts; // can NOT be array indexed, because
|
||||
// edict_t is variable sized, but can
|
||||
// be used to reference the world ent
|
||||
|
||||
@@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "pr_wren.hpp"
|
||||
#include "quakedef.hpp"
|
||||
|
||||
server_t sv;
|
||||
@@ -1404,6 +1405,7 @@ void SV_SpawnServer(const char *server) {
|
||||
|
||||
// load progs to get entity field count
|
||||
PR_LoadProgs();
|
||||
wren::load_progs();
|
||||
|
||||
// allocate server memory
|
||||
/* Host_ClearMemory() called above already cleared the whole sv structure */
|
||||
|
||||
@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
// sv_phys.c
|
||||
|
||||
#include "pr_wren.hpp"
|
||||
#include "quakedef.hpp"
|
||||
|
||||
/*
|
||||
@@ -1182,6 +1183,14 @@ void SV_Physics (void)
|
||||
else
|
||||
entity_cap = sv.num_edicts;
|
||||
|
||||
|
||||
for (const auto i: sv.wentities | std::views::keys) {
|
||||
if (ent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
wren::run_think(i);
|
||||
}
|
||||
|
||||
//for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
|
||||
for (i=0 ; i<entity_cap ; i++, ent = NEXT_EDICT(ent))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user