#include "sv_sandbox.qh"
+#include <server/intermission.qh>
+
+string autocvar_g_sandbox;
int autocvar_g_sandbox_info;
bool autocvar_g_sandbox_readonly;
string autocvar_g_sandbox_storage_name;
float autocvar_g_sandbox_object_material_velocity_min;
float autocvar_g_sandbox_object_material_velocity_factor;
+IntrusiveList g_sandbox_objects;
float autosave_time;
void sandbox_Database_Load();
-REGISTER_MUTATOR(sandbox, cvar("g_sandbox"))
+REGISTER_MUTATOR(sandbox, expr_evaluate(autocvar_g_sandbox))
{
MUTATOR_ONADD
{
+ g_sandbox_objects = IL_NEW();
autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame
if(autocvar_g_sandbox_storage_autoload)
sandbox_Database_Load();
{
e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max);
_setmodel(e, e.model); // reset mins and maxs based on mesh
- setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size
+ // apply object scaling and prevent any float precision issues like #2742
+ setsize(e, RoundPerfectVector(e.mins * e.scale), RoundPerfectVector(e.maxs * e.scale));
}
}
// if the object being removed has been selected for attachment by a player, unset it
FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, { it.object_attach = NULL; });
- if(e.material) { strunzone(e.material); e.material = string_null; }
- if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; }
- if(e.netname) { strunzone(e.netname); e.netname = string_null; }
- if(e.message) { strunzone(e.message); e.message = string_null; }
- if(e.message2) { strunzone(e.message2); e.message2 = string_null; }
+ strfree(e.material);
+ strfree(e.crypto_idfp);
+ strfree(e.netname);
+ strfree(e.message);
+ strfree(e.message2);
delete(e);
e = NULL;
entity sandbox_ObjectPort_Load(entity this, string s, float database)
{
// load object properties, and spawn a new object with them
- float n, i;
+ int n, i;
entity e = NULL, parent = NULL;
+ string arg = string_null;
// separate objects between the ; symbols
n = tokenizebyseparator(s, "; ");
// now separate and apply the properties of each object
for(i = 0; i < n; ++i)
{
- float argv_num;
+ #define SANDBOX_GETARG arg = argv(++argv_num);
+ int argv_num = -1; // starts at -1 so I don't need postincrement
+
string tagname = string_null;
- argv_num = 0;
tokenize_console(port_string[i]);
e = sandbox_ObjectSpawn(this, database);
if(i)
{
// properties stored only for child objects
- if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num;
+ SANDBOX_GETARG; tagname = (arg != "") ? arg : string_null;
}
else
{
// properties stored only for parent objects
if(database)
{
- setorigin(e, stov(argv(argv_num))); ++argv_num;
- e.angles = stov(argv(argv_num)); ++argv_num;
+ SANDBOX_GETARG; setorigin(e, stov(arg));
+ SANDBOX_GETARG; e.angles = stov(arg);
}
parent = e; // mark parent objects as such
}
// properties stored for all objects
- _setmodel(e, argv(argv_num)); ++argv_num;
- e.skin = stof(argv(argv_num)); ++argv_num;
- e.alpha = stof(argv(argv_num)); ++argv_num;
- e.colormod = stov(argv(argv_num)); ++argv_num;
- e.glowmod = stov(argv(argv_num)); ++argv_num;
- e.frame = stof(argv(argv_num)); ++argv_num;
- sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num;
- e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num;
- e.old_movetype = stof(argv(argv_num)); ++argv_num;
+ SANDBOX_GETARG; _setmodel(e, arg);
+ SANDBOX_GETARG; e.skin = stof(arg);
+ SANDBOX_GETARG; e.alpha = stof(arg);
+ SANDBOX_GETARG; e.colormod = stov(arg);
+ SANDBOX_GETARG; e.glowmod = stov(arg);
+ SANDBOX_GETARG; e.frame = stof(arg);
+ SANDBOX_GETARG; sandbox_ObjectEdit_Scale(e, stof(arg));
+ SANDBOX_GETARG; e.solid = e.old_solid = stof(arg);
+ SANDBOX_GETARG; e.old_movetype = stof(arg);
set_movetype(e, e.old_movetype);
- e.damageforcescale = stof(argv(argv_num)); ++argv_num;
- if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num;
+ SANDBOX_GETARG; e.damageforcescale = stof(arg);
+ strfree(e.material);
+ SANDBOX_GETARG; e.material = (arg != "") ? strzone(arg) : string_null;
if(database)
{
// properties stored only for the database
- if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num;
- if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num;
- if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num;
- if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num;
+ strfree(e.crypto_idfp);
+ SANDBOX_GETARG; e.crypto_idfp = (arg != "") ? strzone(arg) : string_null;
+ SANDBOX_GETARG; strcpy(e.netname, arg);
+ SANDBOX_GETARG; strcpy(e.message, arg);
+ SANDBOX_GETARG; strcpy(e.message2, arg);
}
// attach last
e.damageforcescale = stof(argv(3));
break;
case "material":
- if(e.material) strunzone(e.material);
+ strfree(e.material);
if(argv(3))
{
for (j = 1; j <= 5; j++) // precache material sounds, 5 in total
}
// update last editing time
- if(e.message2) strunzone(e.message2);
- e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S"));
+ strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S"));
if(autocvar_g_sandbox_info > 1)
LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin));
// also update the player's nickname if he changed it (but has the same player UID)
if(e.netname != player.netname)
{
- if(e.netname) strunzone(e.netname);
- e.netname = strzone(player.netname);
+ strcpy(e.netname, player.netname);
print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated");
}
return true;
}
- if(e.crypto_idfp) strunzone(e.crypto_idfp);
- e.crypto_idfp = strzone(player.crypto_idfp);
+ strcpy(e.crypto_idfp, player.crypto_idfp);
print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully");
}