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
13 changes: 13 additions & 0 deletions src/common/IPC/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ namespace IPC {
void Close() const;
};

// Version of the protocol for detecting what ABI version the VM has upon startup
constexpr uint32_t ABI_VERSION_DETECTION_ABI_VERSION = 4;

// Version for the syscall signatures between engine and VM. This must change if the syscall
// IDs, argument types, or return types change, or if serialization procedures change.
// Follows Daemon major versions.
// This should be updated only by update-version-number.py when a "major" release is indicated
constexpr const char* SYSCALL_ABI_VERSION = "0.54.0";

// This should be manually set to true when starting a 'for-X.Y.Z/sync' branch.
// This should be set to false by update-version-number.py when a (major) release is created.
constexpr bool DAEMON_HAS_COMPATIBILITY_BREAKING_SYSCALL_CHANGES = true;

/*
* The messages sent between the VM and the engine are defined by a numerical
* ID used for dispatch, a list of input types and an optional list of return
Expand Down
2 changes: 0 additions & 2 deletions src/engine/client/cg_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "shared/bg_public.h"
#endif

#define CGAME_API_VERSION 3

#define CMD_BACKUP 64
#define CMD_MASK ( CMD_BACKUP - 1 )
// allow a lot of command backups for very fast systems
Expand Down
5 changes: 1 addition & 4 deletions src/engine/client/cl_cgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -963,10 +963,7 @@ CGameVM::CGameVM(): VM::VMBase("cgame", Cvar::CHEAT), services(nullptr), cmdBuff
void CGameVM::Start()
{
services = std::unique_ptr<VM::CommonVMServices>(new VM::CommonVMServices(*this, "CGame", FS::Owner::CGAME, Cmd::CGAME_VM));
uint32_t version = this->Create();
if ( version != CGAME_API_VERSION ) {
Sys::Drop( "CGame ABI mismatch, expected %d, got %d", CGAME_API_VERSION, version );
}
this->Create();
this->CGameStaticInit();
}

Expand Down
33 changes: 29 additions & 4 deletions src/engine/framework/VirtualMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ static IPC::Socket CreateInProcessNativeVM(std::pair<IPC::Socket, IPC::Socket> p
return std::move(pair.first);
}

uint32_t VMBase::Create()
void VMBase::Create()
{
type = static_cast<vmType_t>(params.vmType.Get());

Expand Down Expand Up @@ -419,11 +419,36 @@ uint32_t VMBase::Create()
if (type != TYPE_NATIVE_DLL && !params.debug.Get())
rootChannel.SetRecvTimeout(std::chrono::seconds(2));

// Read the ABI version from the root socket.
// Read the ABI version detection ABI version from the root socket.
// If this fails, we assume the remote process failed to start
Util::Reader reader = rootChannel.RecvMsg();
Log::Notice("Loaded VM module in %d msec", Sys::Milliseconds() - loadStartTime);
return reader.Read<uint32_t>();

// VM version incompatibility detection...

uint32_t magic = reader.Read<uint32_t>();
if (magic != IPC::ABI_VERSION_DETECTION_ABI_VERSION) {
Sys::Drop("Couldn't load the %s gamelogic module: it is built for %s version of Daemon engine",
this->name, magic > IPC::ABI_VERSION_DETECTION_ABI_VERSION ? "a newer" : "an older");
}

std::string vmABI = reader.Read<std::string>();
if (vmABI != IPC::SYSCALL_ABI_VERSION) {
Sys::Drop("Couldn't load the %s gamelogic module: it uses ABI version %s but this Daemon engine uses %s",
vmABI, IPC::SYSCALL_ABI_VERSION);
}

bool vmCompatBreaking = reader.Read<bool>();
if (vmCompatBreaking && !IPC::DAEMON_HAS_COMPATIBILITY_BREAKING_SYSCALL_CHANGES) {
Sys::Drop("Couldn't load the %s gamelogic module: it has compatibility-breaking ABI changes but Daemon engine uses the vanilla %s ABI",
this->name, IPC::SYSCALL_ABI_VERSION);
} else if (!vmCompatBreaking && IPC::DAEMON_HAS_COMPATIBILITY_BREAKING_SYSCALL_CHANGES) {
Sys::Drop("Couldn't load the %s gamelogic module: Daemon has compatibility-breaking ABI changes but the VM uses the vanilla %s ABI",
this->name, IPC::SYSCALL_ABI_VERSION);
} else if (IPC::DAEMON_HAS_COMPATIBILITY_BREAKING_SYSCALL_CHANGES) {
Log::Notice("^6Using %s VM with unreleased ABI changes", this->name);
}

Log::Notice("Loaded %s VM module in %d msec", this->name, Sys::Milliseconds() - loadStartTime);
}

void VMBase::FreeInProcessVM() {
Expand Down
5 changes: 2 additions & 3 deletions src/engine/framework/VirtualMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ class VMBase {
VMBase(std::string name, int vmTypeCvarFlags)
: processHandle(Sys::INVALID_HANDLE), name(name), type(TYPE_NACL), params(name, vmTypeCvarFlags) {}

// Create the VM for the named module. Returns the ABI version reported
// by the module. This will automatically free any existing VM.
uint32_t Create();
// Create the VM for the named module. This will automatically free any existing VM.
void Create();

// Free the VM
void Free();
Expand Down
2 changes: 0 additions & 2 deletions src/engine/server/sg_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

#include "engine/qcommon/q_shared.h"

#define GAME_API_VERSION 3

#define SVF_NOCLIENT 0x00000001
#define SVF_CLIENTMASK 0x00000002
#define SVF_VISDUMMY 0x00000004
Expand Down
6 changes: 1 addition & 5 deletions src/engine/server/sv_sgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,11 +338,7 @@ void GameVM::Start()
{
services = std::unique_ptr<VM::CommonVMServices>(new VM::CommonVMServices(*this, "SGame", FS::Owner::SGAME, Cmd::SGAME_VM));

uint32_t version = this->Create();
if ( version != GAME_API_VERSION ) {
Sys::Drop( "SGame ABI mismatch, expected %d, got %d", GAME_API_VERSION, version );
}

this->Create();
this->GameStaticInit();
}

Expand Down
6 changes: 4 additions & 2 deletions src/shared/VMMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ static void CommonInit(Sys::OSHandle rootSocket)
{
VM::rootChannel = IPC::Channel(IPC::Socket::FromHandle(rootSocket));

// Send syscall ABI version, also acts as a sign that the module loaded
// Send ABI version information, also acts as a sign that the module loaded
Util::Writer writer;
writer.Write<uint32_t>(VM::VM_API_VERSION);
writer.Write<uint32_t>(IPC::ABI_VERSION_DETECTION_ABI_VERSION);
writer.Write<std::string>(IPC::SYSCALL_ABI_VERSION);
writer.Write<bool>(IPC::DAEMON_HAS_COMPATIBILITY_BREAKING_SYSCALL_CHANGES);
VM::rootChannel.SendMsg(writer);

// Start the main loop
Expand Down
1 change: 0 additions & 1 deletion src/shared/VMMain.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ namespace VM {
void VMInit();
void VMHandleSyscall(uint32_t id, Util::Reader reader);
void GetNetcodeTables(NetcodeTable& playerStateTable, int& playerStateSize);
extern int VM_API_VERSION;

// Send a message to the engine
template<typename Msg, typename... Args> void SendMsg(Args&&... args) {
Expand Down