]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/sandbox.qc
Read-only mode. When enabled, no sandbox commands can be used, and objects cannot...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / sandbox.qc
index 2de78651f2cb173bdf2d37d83233a9058083ad32..264aa83a7d9e62c9a827431e9e47f4fcfe556716 100644 (file)
@@ -1,5 +1,6 @@
 const float MAX_STORAGE_ATTACHMENTS = 16;
 float object_count;
+.float object_flood;
 .entity object_attach;
 .string material;
 
@@ -29,6 +30,34 @@ void sandbox_ObjectFunction_Touch()
        pointparticles(particleeffectnum(strcat("impact_", self.material)), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10
 }
 
+void sandbox_ObjectFunction_Think()
+{
+       entity e;
+
+       // decide if and how this object can be grabbed
+       if(autocvar_g_sandbox_readonly)
+               self.grab = 0; // no grabbing
+       else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp)
+               self.grab = 1; // owner only
+       else
+               self.grab = 3; // anyone
+
+       // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server).
+       // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this,
+       // since if the owning player disconnects, the object's owner should also be reset.
+       FOR_EACH_REALPLAYER(e) // bots can't have objects
+       {
+               if(self.crypto_idfp == e.crypto_idfp)
+               {
+                       self.realowner = e;
+                       break;
+               }
+               self.realowner = world;
+       }
+
+       self.nextthink = time;
+}
+
 entity sandbox_ObjectEdit_Get(float permissions)
 {
        // returns the traced entity if the player can edit it, and world if not
@@ -44,7 +73,7 @@ entity sandbox_ObjectEdit_Get(float permissions)
                return trace_ent; // don't check permissions, anyone can edit this object
        if(!trace_ent.crypto_idfp)
                return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it
-       if not(trace_ent.crypto_idfp != self.crypto_idfp && autocvar_g_sandbox_editor_free < 2)
+       if not(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)
                return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server
        return world;
 }
@@ -117,6 +146,8 @@ entity sandbox_ObjectSpawn(float database)
        e.skin = 0;
        e.material = string_null;
        e.touch = sandbox_ObjectFunction_Touch;
+       e.think = sandbox_ObjectFunction_Think;
+       e.nextthink = time;
        //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects?
 
        if(!database)
@@ -130,7 +161,8 @@ entity sandbox_ObjectSpawn(float database)
 
                // set public object information
                e.netname = strzone(self.netname); // name of the owner
-               e.message = e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // creation and edit time
+               e.message = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // creation time
+               e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S")); // last editing time
 
                // set origin and direction based on player position and view angle
                makevectors(self.v_angle);
@@ -370,9 +402,14 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                return FALSE;
        if(cmd_name == "g_sandbox")
        {
+               if(autocvar_g_sandbox_readonly)
+               {
+                       print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used");
+                       return TRUE;
+               }
                if(cmd_argc < 2)
                {
-                       print_to(self, "Sandbox mode is active. For usage information, type 'sandbox help'");
+                       print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'");
                        return TRUE;
                }
 
@@ -388,16 +425,13 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model");
                                print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects");
                                print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original");
-                               print_to(self, "^Properties for ^2object_duplicate^7:");
                                print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar");
                                print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\"");
                                print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects");
-                               print_to(self, "^7Properties for ^2object_attach^7:");
                                print_to(self, "^3get ^7- selects the object you are facing as the object to be attached");
                                print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone");
                                print_to(self, "^3remove ^7- detaches all objects from the object you are facing");
                                print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects");
-                               print_to(self, "^7Properties for ^2object_edit^7:");
                                print_to(self, "^3skin value ^7- changes the skin of the object");
                                print_to(self, "^3alpha value ^7- sets object transparency");
                                print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color");
@@ -408,12 +442,21 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                print_to(self, "^3force value ^7- amount of force applied to objects that are shot");
                                print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh");
                                print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it");
-                               print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object. 'mesh' shows model information");
+                               print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object");
+                               print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date");
+                               print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones");
+                               print_to(self, "^3attachments ^7- prints information about the object's attachments");
                                print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects");
                                return TRUE;
 
                        // ---------------- COMMAND: OBJECT, SPAWN ----------------
                        case "object_spawn":
+                               if(time < self.object_flood)
+                               {
+                                       print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+                                       return TRUE;
+                               }
+                               self.object_flood = time + autocvar_g_sandbox_editor_flood;
                                if(object_count >= autocvar_g_sandbox_editor_maxobjects)
                                {
                                        print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time"));
@@ -424,7 +467,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                        print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command");
                                        return TRUE;
                                }
-                               else if not(fexists(argv(2)))
+                               if not(fexists(argv(2)))
                                {
                                        print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct");
                                        return TRUE;
@@ -472,6 +515,12 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
 
                                        case "paste":
                                                // spawns a new object using the properties in the player's clipboard cvar
+                                               if(time < self.object_flood)
+                                               {
+                                                       print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object"));
+                                                       return TRUE;
+                                               }
+                                               self.object_flood = time + autocvar_g_sandbox_editor_flood;
                                                if(!argv(3)) // no object in clipboard
                                                {
                                                        print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it");
@@ -608,7 +657,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                                        return TRUE;
                                        }
 
-                                       // update last edit time
+                                       // update last editing time
                                        if(e.message2)  strunzone(e.message2);
                                        e.message2 = strzone(strftime(TRUE, "%d-%m-%Y %H:%M:%S"));
 
@@ -663,18 +712,35 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
                                {
                                        switch(argv(2))
                                        {
+                                               case "object":
+                                                       print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\""));
+                                                       return TRUE;
                                                case "mesh":
-                                                       string tags;
                                                        for(i = 1; gettaginfo(e, i); i++)
-                                                               tags = strcat(tags, "^5", gettaginfo_name, "^7, ");
-                                                       print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is ^3", e.model, "^7 at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", tags));
+                                                               s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", ");
+                                                       print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s));
                                                        return TRUE;
-                                               case "object":
-                                                       print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by ^7", e.netname, "^7, created ^3", e.message, "^7, last edited ^3", e.message2));
+                                               case "attachments":
+                                                       // this should show the same info as 'mesh' but for attachments
+                                                       entity head;
+                                                       for(head = world; (head = find(head, classname, "object")); )
+                                                       {
+                                                               if(head.owner == e)
+                                                               {
+                                                                       ++i; // start from 1
+                                                                       gettaginfo(e, head.tag_index);
+                                                                       s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame));
+                                                                       s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", ");
+                                                               }
+                                                       }
+                                                       if(i) // object contains attachments
+                                                               print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s));
+                                                       else
+                                                               print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments");
                                                        return TRUE;
                                        }
                                }
-                               print_to(self, "^1SANDBOX - WARNING: ^7No information could not be found. Make sure you are facing an object");
+                               print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object");
                                return TRUE;
 
                        // ---------------- COMMAND: DEFAULT ----------------
@@ -686,31 +752,6 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand)
        return FALSE;
 }
 
-MUTATOR_HOOKFUNCTION(sandbox_PlayerPreThink)
-{
-       // if the player is close enough to their object, they can drag it
-
-       if(autocvar_sv_cheats)
-               return FALSE; // cheat dragging is used instead
-
-       // grab is TRUE if the object can be picked up. While an object is being carried, the Drag() function
-       // must execute for it either way, otherwise it would cause bugs if it went out of the player's trace.
-       // This also makes sure that an object can only pe picked up if in range, but does not get dropped if
-       // it goes out of range while slinging it around.
-
-       entity e;
-       float grab;
-
-       e = sandbox_ObjectEdit_Get(TRUE);
-       if(e != world && vlen(e.origin - self.origin) <= autocvar_g_sandbox_editor_distance_edit)
-               grab = TRUE;
-
-       if(Drag(e, grab)) // execute dragging
-               return TRUE;
-
-       return FALSE;
-}
-
 float autosave_time;
 MUTATOR_HOOKFUNCTION(sandbox_StartFrame)
 {
@@ -729,7 +770,6 @@ MUTATOR_DEFINITION(sandbox)
 {
        MUTATOR_HOOK(SV_ParseClientCommand, sandbox_PlayerCommand, CBC_ORDER_ANY);
        MUTATOR_HOOK(SV_StartFrame, sandbox_StartFrame, CBC_ORDER_ANY);
-       MUTATOR_HOOK(PlayerPreThink, sandbox_PlayerPreThink, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {