From fb7b625a2f9482eb9ae538f15d172b2fcb9742dc Mon Sep 17 00:00:00 2001 From: TimePath Date: Sat, 11 Jun 2016 23:55:25 +1000 Subject: [PATCH] Step 6: complete --- qcsrc/client/main.qc | 6 +-- qcsrc/client/view.qc | 3 +- .../gamemodes/gamemode/nexball/nexball.qc | 2 +- .../gamemodes/gamemode/onslaught/onslaught.qc | 8 ++-- qcsrc/common/physics/movetypes/movetypes.qh | 2 +- qcsrc/common/physics/player.qc | 2 +- qcsrc/common/triggers/func/bobbing.qc | 2 +- qcsrc/common/triggers/func/button.qc | 4 +- qcsrc/common/triggers/func/door.qc | 8 ++-- qcsrc/common/triggers/func/door_rotating.qc | 2 +- qcsrc/common/triggers/func/door_secret.qc | 6 +-- qcsrc/common/triggers/func/fourier.qc | 2 +- qcsrc/common/triggers/func/pendulum.qc | 2 +- qcsrc/common/triggers/func/plat.qc | 2 +- qcsrc/common/triggers/func/rotating.qc | 2 +- qcsrc/common/triggers/func/train.qc | 8 ++-- qcsrc/common/triggers/func/vectormamamam.qc | 4 +- qcsrc/common/triggers/misc/follow.qc | 4 +- qcsrc/common/triggers/misc/teleport_dest.qc | 2 +- qcsrc/common/triggers/platforms.qc | 13 +++--- qcsrc/common/triggers/platforms.qh | 2 +- qcsrc/common/triggers/subs.qc | 6 +-- qcsrc/common/triggers/target/speaker.qc | 2 +- qcsrc/common/triggers/teleporters.qc | 2 +- qcsrc/common/triggers/trigger/impulse.qc | 2 +- qcsrc/common/triggers/trigger/jumppads.qc | 2 +- qcsrc/common/triggers/trigger/multi.qc | 2 +- qcsrc/common/triggers/trigger/teleport.qc | 2 +- qcsrc/common/triggers/triggers.qc | 3 +- qcsrc/common/weapons/weapon/porto.qc | 4 +- qcsrc/dpdefs/post.qh | 3 ++ qcsrc/dpdefs/pre.qh | 3 ++ qcsrc/lib/_all.inc | 4 ++ qcsrc/lib/csqcmodel/sv_model.qc | 3 +- qcsrc/lib/net.qh | 5 +-- qcsrc/lib/self.qh | 41 +++++++++++++++---- qcsrc/lib/warpzone/common.qc | 13 +++--- qcsrc/lib/warpzone/server.qc | 9 ++-- qcsrc/server/cheats.qc | 4 +- qcsrc/server/cl_client.qc | 21 ++++------ qcsrc/server/command/cmd.qc | 3 +- qcsrc/server/g_lights.qc | 6 +-- qcsrc/server/g_subs.qc | 2 +- qcsrc/server/item_key.qc | 6 +-- qcsrc/server/miscfunctions.qc | 6 --- qcsrc/server/miscfunctions.qh | 1 - .../mutators/mutator/gamemode_assault.qc | 4 +- .../mutators/mutator/gamemode_domination.qc | 2 +- qcsrc/server/spawnpoints.qc | 4 +- qcsrc/server/sv_main.qc | 2 +- qcsrc/server/sys-post.qh | 1 - qcsrc/server/sys-pre.qh | 1 - qcsrc/server/teamplay.qc | 2 +- 53 files changed, 131 insertions(+), 126 deletions(-) create mode 100644 qcsrc/dpdefs/post.qh create mode 100644 qcsrc/dpdefs/pre.qh diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index a22d164ef..b6cca28b1 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -781,8 +781,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new) // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. void CSQC_Ent_Update(bool isnew) -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); this.sourceLoc = __FILE__ ":" STR(__LINE__); int t = ReadByte(); @@ -862,8 +861,7 @@ void Ent_Remove(entity this) } // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(this) as well. void CSQC_Ent_Remove() -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype); if (wasfreed(this)) { diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 9ba1114b6..a7c4b1b87 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -1370,8 +1370,7 @@ int lasthud; float vh_notice_time; void WaypointSprite_Load(); void CSQC_UpdateView(float w, float h) -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); TC(int, w); TC(int, h); entity e; float fov; diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 1cada9456..2a431c721 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -124,7 +124,7 @@ void relocate_nexball(entity this) vector o; o = this.origin; if(!move_out_of_solid(this)) - objerror("could not get out of solid at all!"); + objerror(this, "could not get out of solid at all!"); LOG_INFO("^1NOTE: this map needs FIXING. ", this.classname, " at ", vtos(o - '0 0 1')); LOG_INFO(" needs to be moved out of solid, e.g. by '", ftos(this.origin.x - o.x)); LOG_INFO(" ", ftos(this.origin.y - o.y)); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index ce31eeced..a5ad94e20 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -418,8 +418,8 @@ void ons_DelayedLinkSetup(entity this) { this.goalentity = find(world, targetname, this.target); this.enemy = find(world, targetname, this.target2); - if(!this.goalentity) { objerror("can not find target\n"); } - if(!this.enemy) { objerror("can not find target2\n"); } + if(!this.goalentity) { objerror(this, "can not find target\n"); } + if(!this.enemy) { objerror(this, "can not find target2\n"); } LOG_DEBUG(strcat(etos(this.goalentity), " linked with ", etos(this.enemy), "\n")); this.SendFlags |= 3; @@ -2203,7 +2203,7 @@ spawnfunc(onslaught_link) if(!g_onslaught) { remove(this); return; } if (this.target == "" || this.target2 == "") - objerror("target and target2 must be set\n"); + objerror(this, "target and target2 must be set\n"); this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist ons_worldlinklist = this; @@ -2242,7 +2242,7 @@ keys: spawnfunc(onslaught_generator) { if(!g_onslaught) { remove(this); return; } - if(!this.team) { objerror("team must be set"); } + if(!this.team) { objerror(this, "team must be set"); } ons_GeneratorSetup(this); } diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh index a9d0c42e9..61b7e94a9 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qh +++ b/qcsrc/common/physics/movetypes/movetypes.qh @@ -8,7 +8,7 @@ .float move_ltime; .void(entity this) move_think; .float move_nextthink; -.void() move_blocked; +.void(entity this) move_blocked; .float move_movetype; .float move_time; diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc index bb81a41b9..5079bd976 100644 --- a/qcsrc/common/physics/player.qc +++ b/qcsrc/common/physics/player.qc @@ -1552,7 +1552,7 @@ void CSQC_ClientMovement_PlayerMove_Frame(entity this) #endif { #ifdef SVQC - SELFPARAM(); // needed for engine functions + ENGINE_EVENT(); #endif PM_Main(this); } diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc index 583efbe25..094673b5b 100644 --- a/qcsrc/common/triggers/func/bobbing.qc +++ b/qcsrc/common/triggers/func/bobbing.qc @@ -48,7 +48,7 @@ spawnfunc(func_bobbing) this.active = ACTIVE_ACTIVE; // damage when blocked - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); if(this.dmg && (this.message == "")) this.message = " was squished"; if(this.dmg && (this.message2 == "")) diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc index d8a267608..a8ec50a86 100644 --- a/qcsrc/common/triggers/func/button.qc +++ b/qcsrc/common/triggers/func/button.qc @@ -28,7 +28,7 @@ void button_return(entity this) } -void button_blocked() +void button_blocked(entity this) { // do nothing, just don't come all the way back out } @@ -119,7 +119,7 @@ spawnfunc(func_button) return; this.effects |= EF_LOWPRECISION; - this.blocked = button_blocked; + setblocked(this, button_blocked); this.use = button_use; // if (this.health == 0) // all buttons are now shootable diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc index c478079f5..86d7cb297 100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@ -25,8 +25,8 @@ void door_go_up(entity this); void door_rotating_go_down(entity this); void door_rotating_go_up(entity this); -void door_blocked() -{SELFPARAM(); +void door_blocked(entity this) +{ if((this.spawnflags & 8) #ifdef SVQC && (other.takedamage != DAMAGE_NO) @@ -209,7 +209,7 @@ bool door_check_keys(entity door, entity player) void door_fire(entity this, entity actor, entity trigger) { if (this.owner != this) - objerror ("door_fire: this.owner != this"); + objerror (this, "door_fire: this.owner != this"); if (this.spawnflags & DOOR_TOGGLE) { @@ -722,7 +722,7 @@ spawnfunc(func_door) precache_sound(this.noise); precache_sound(this.noise3); - this.blocked = door_blocked; + setblocked(this, door_blocked); this.use = door_use; if(this.dmg && (this.message == "")) diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc index 093382a70..31171899f 100644 --- a/qcsrc/common/triggers/func/door_rotating.qc +++ b/qcsrc/common/triggers/func/door_rotating.qc @@ -71,7 +71,7 @@ spawnfunc(func_door_rotating) //this.effects |= EF_LOWPRECISION; this.classname = "door_rotating"; - this.blocked = door_blocked; + setblocked(this, door_blocked); this.use = door_use; if(this.spawnflags & 8) diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc index a2d883ba0..f02fc61f6 100644 --- a/qcsrc/common/triggers/func/door_secret.qc +++ b/qcsrc/common/triggers/func/door_secret.qc @@ -135,8 +135,8 @@ void fd_secret_done(entity this) .float door_finished; -void secret_blocked() -{SELFPARAM(); +void secret_blocked(entity this) +{ if (time < this.door_finished) return; this.door_finished = time + 0.5; @@ -214,7 +214,7 @@ spawnfunc(func_door_secret) precache_sound(this.noise); settouch(this, secret_touch); - this.blocked = secret_blocked; + setblocked(this, secret_blocked); this.speed = 50; this.use = fd_secret_use; IFTARGETED diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc index cde3c5d49..52eab115a 100644 --- a/qcsrc/common/triggers/func/fourier.qc +++ b/qcsrc/common/triggers/func/fourier.qc @@ -55,7 +55,7 @@ spawnfunc(func_fourier) this.destvec = this.origin; this.cnt = 360 / this.speed; - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); if(this.dmg && (this.message == "")) this.message = " was squished"; if(this.dmg && (this.message2 == "")) diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc index f1fffd7b9..05f7b75a6 100644 --- a/qcsrc/common/triggers/func/pendulum.qc +++ b/qcsrc/common/triggers/func/pendulum.qc @@ -46,7 +46,7 @@ spawnfunc(func_pendulum) this.dmgtime = 0.25; this.dmgtime2 = time; - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); this.avelocity_z = 0.0000001; if (!InitMovingBrushTrigger(this)) diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index dea0077c1..6f7ebaad3 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -106,7 +106,7 @@ spawnfunc(func_plat) this.effects |= EF_LOWPRECISION; setsize (this, this.mins , this.maxs); - this.blocked = plat_crush; + setblocked(this, plat_crush); if (!this.speed) this.speed = 150; if (!this.lip) this.lip = 16; diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc index f34d41e2b..22f3dedea 100644 --- a/qcsrc/common/triggers/func/rotating.qc +++ b/qcsrc/common/triggers/func/rotating.qc @@ -65,7 +65,7 @@ spawnfunc(func_rotating) return; // no EF_LOWPRECISION here, as rounding angles is bad - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); // wait for targets to spawn this.SUB_NEXTTHINK = this.SUB_LTIME + 999999999; diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc index 1ec63780f..015668328 100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@ -77,7 +77,7 @@ void train_next(entity this) } } if (this.target == "") - objerror("train_next: no next target"); + objerror(this, "train_next: no next target"); this.wait = targ.wait; if (!this.wait) this.wait = 0.1; @@ -189,7 +189,7 @@ void func_train_find(entity this) targ = find(world, targetname, this.target); this.target = targ.target; if (this.target == "") - objerror("func_train_find: no next target"); + objerror(this, "func_train_find: no next target"); SUB_SETORIGIN(this, targ.origin - this.view_ofs); if(!(this.spawnflags & 4)) @@ -215,7 +215,7 @@ spawnfunc(func_train) precache_sound(this.noise); if (this.target == "") - objerror("func_train without a target"); + objerror(this, "func_train without a target"); if (!this.speed) this.speed = 100; @@ -237,7 +237,7 @@ spawnfunc(func_train) // wait for targets to spawn InitializeEntity(this, func_train_find, INITPRIO_FINDTARGET); - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); if(this.dmg && (this.message == "")) this.message = " was squished"; if(this.dmg && (this.message2 == "")) diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc index dd8804106..a336c8b7e 100644 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ b/qcsrc/common/triggers/func/vectormamamam.qc @@ -86,7 +86,7 @@ void func_vectormamamam_findtarget(entity this) this.wp03 = find(world, targetname, this.target4); if(!this.wp00 && !this.wp01 && !this.wp02 && !this.wp03) - objerror("No reference entity found, so there is nothing to move. Aborting."); + objerror(this, "No reference entity found, so there is nothing to move. Aborting."); this.destvec = this.origin - func_vectormamamam_origin(this, 0); @@ -129,7 +129,7 @@ spawnfunc(func_vectormamamam) if(vlen(this.target4normal)) this.target4normal = normalize(this.target4normal); - this.blocked = generic_plat_blocked; + setblocked(this, generic_plat_blocked); if(this.dmg && (this.message == "")) this.message = " was squished"; if(this.dmg && (this.message == "")) diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc index dbe1cb7d8..8949f17a9 100644 --- a/qcsrc/common/triggers/misc/follow.qc +++ b/qcsrc/common/triggers/misc/follow.qc @@ -13,7 +13,7 @@ void follow_init(entity this) if(!src && !dst) { - objerror("follow: could not find target/killtarget"); + objerror(this, "follow: could not find target/killtarget"); return; } @@ -25,7 +25,7 @@ void follow_init(entity this) } else if(!src || !dst) { - objerror("follow: could not find target/killtarget"); + objerror(this, "follow: could not find target/killtarget"); return; } else if(this.spawnflags & 1) diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc index b3a242076..285f7357f 100644 --- a/qcsrc/common/triggers/misc/teleport_dest.qc +++ b/qcsrc/common/triggers/misc/teleport_dest.qc @@ -44,7 +44,7 @@ spawnfunc(info_teleport_destination) { } else - objerror ("^3Teleport destination without a targetname"); + objerror (this, "^3Teleport destination without a targetname"); teleport_dest_link(this); } diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc index 9d2b3f2bc..7b2f43f33 100644 --- a/qcsrc/common/triggers/platforms.qc +++ b/qcsrc/common/triggers/platforms.qc @@ -1,7 +1,6 @@ -void generic_plat_blocked() +void generic_plat_blocked(entity this) { #ifdef SVQC - SELFPARAM(); if(this.dmg && other.takedamage != DAMAGE_NO) { if(this.dmgtime2 < time) @@ -55,7 +54,7 @@ void plat_spawn_inside_trigger(entity this) // otherwise, something is fishy... remove(trigger); - objerror("plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); + objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); } void plat_hit_top(entity this) @@ -140,8 +139,8 @@ void plat_trigger_use(entity this, entity actor, entity trigger) } -void plat_crush() -{SELFPARAM(); +void plat_crush(entity this) +{ if((this.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! #ifdef SVQC @@ -174,7 +173,7 @@ void plat_use(entity this, entity actor, entity trigger) { this.use = func_null; if (this.state != 4) - objerror ("plat_use: not in up state"); + objerror (this, "plat_use: not in up state"); plat_go_down(this); } @@ -222,7 +221,7 @@ bool set_platmovetype(entity e, string s) if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end)) { - objerror("Invalid platform move type; platform would go in reverse, which is not allowed."); + objerror(e, "Invalid platform move type; platform would go in reverse, which is not allowed."); return false; } diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh index acb90a7ef..6bdfb23d6 100644 --- a/qcsrc/common/triggers/platforms.qh +++ b/qcsrc/common/triggers/platforms.qh @@ -8,7 +8,7 @@ void plat_outside_touch(entity this); void plat_trigger_use(entity this, entity actor, entity trigger); void plat_go_up(entity this); void plat_go_down(entity this); -void plat_crush(); +void plat_crush(entity this); const float PLAT_LOW_TRIGGER = 1; .float dmg; diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc index 1595b9a9c..a71267b0c 100644 --- a/qcsrc/common/triggers/subs.qc +++ b/qcsrc/common/triggers/subs.qc @@ -189,7 +189,7 @@ void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspe entity controller; if (!tspeed) - objerror ("No speed is defined!"); + objerror (this, "No speed is defined!"); this.think1 = func; this.finaldest = tdest; @@ -245,7 +245,7 @@ void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, vo float traveltime; if (!tspeed) - objerror ("No speed is defined!"); + objerror (this, "No speed is defined!"); this.think1 = func; this.finaldest = tdest; @@ -320,7 +320,7 @@ void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float t float traveltime; if (!tspeed) - objerror ("No speed is defined!"); + objerror (this, "No speed is defined!"); // take the shortest distance for the angles this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5); diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc index dc9b34b4a..7db93a3d1 100644 --- a/qcsrc/common/triggers/target/speaker.qc +++ b/qcsrc/common/triggers/target/speaker.qc @@ -119,7 +119,7 @@ spawnfunc(target_speaker) } else if(this.spawnflags & 2) // LOOPED_OFF { - objerror("This sound entity can never be activated"); + objerror(this, "This sound entity can never be activated"); } else { diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc index 492f7f353..475904d21 100644 --- a/qcsrc/common/triggers/teleporters.qc +++ b/qcsrc/common/triggers/teleporters.qc @@ -264,7 +264,7 @@ void teleport_findtarget(entity this) if(n == 0) { // no dest! - objerror ("Teleporter with nonexistant target"); + objerror (this, "Teleporter with nonexistant target"); return; } else if(n == 1) diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc index c174aa513..aee2f72cb 100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@ -16,7 +16,7 @@ void trigger_impulse_touch1(entity this) targ = find(world, targetname, this.target); if(!targ) { - objerror("trigger_force without a (valid) .target!\n"); + objerror(this, "trigger_force without a (valid) .target!\n"); remove(this); return; } diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc index 9691cdad8..464d05d08 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@ -304,7 +304,7 @@ void trigger_push_findtarget(entity this) { // no dest! #ifdef SVQC - objerror ("Jumppad with nonexistant target"); + objerror (this, "Jumppad with nonexistant target"); #endif return; } diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc index 1f08796dd..9f71eeff3 100644 --- a/qcsrc/common/triggers/trigger/multi.qc +++ b/qcsrc/common/triggers/trigger/multi.qc @@ -167,7 +167,7 @@ spawnfunc(trigger_multiple) if (this.health) { if (this.spawnflags & SPAWNFLAG_NOTOUCH) - objerror ("health and notouch don't make sense\n"); + objerror (this, "health and notouch don't make sense\n"); this.max_health = this.health; this.event_damage = multi_eventdamage; this.takedamage = DAMAGE_YES; diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc index 8caa2d5eb..8ee6b7a69 100644 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ b/qcsrc/common/triggers/trigger/teleport.qc @@ -94,7 +94,7 @@ spawnfunc(trigger_teleport) if (this.target == "") { - objerror ("Teleporter with no target"); + objerror (this, "Teleporter with no target"); return; } diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc index d7d988f51..0e13a6bde 100644 --- a/qcsrc/common/triggers/triggers.qc +++ b/qcsrc/common/triggers/triggers.qc @@ -41,8 +41,7 @@ void trigger_init(entity this) void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc) { - this.SendEntity = SendEntity_self; - this.SendEntity3 = sendfunc; + setSendEntity(this, sendfunc); this.SendFlags = 0xFFFFFF; } diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index 8a0254bfb..a512ad2ca 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -57,7 +57,7 @@ void W_Porto_Success(entity this) { if(this.realowner == world) { - objerror("Cannot succeed successfully: no owner\n"); + objerror(this, "Cannot succeed successfully: no owner\n"); return; } @@ -70,7 +70,7 @@ void W_Porto_Fail(entity this, float failhard) { if(this.realowner == world) { - objerror("Cannot fail successfully: no owner\n"); + objerror(this, "Cannot fail successfully: no owner\n"); return; } diff --git a/qcsrc/dpdefs/post.qh b/qcsrc/dpdefs/post.qh new file mode 100644 index 000000000..fe465e593 --- /dev/null +++ b/qcsrc/dpdefs/post.qh @@ -0,0 +1,3 @@ +#pragma once + +#undef objerror diff --git a/qcsrc/dpdefs/pre.qh b/qcsrc/dpdefs/pre.qh new file mode 100644 index 000000000..f63f0aa83 --- /dev/null +++ b/qcsrc/dpdefs/pre.qh @@ -0,0 +1,3 @@ +#pragma once + +#define objerror builtin_objerror diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index c5e020660..23b55cb69 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -12,6 +12,8 @@ #define bool float #endif +#include + #if defined(CSQC) #include #include @@ -25,6 +27,8 @@ #include #endif +#include + #define USING(name, T) typedef T name #include "bool.qh" diff --git a/qcsrc/lib/csqcmodel/sv_model.qc b/qcsrc/lib/csqcmodel/sv_model.qc index b02df19e0..2d0e5eb09 100644 --- a/qcsrc/lib/csqcmodel/sv_model.qc +++ b/qcsrc/lib/csqcmodel/sv_model.qc @@ -121,8 +121,7 @@ void CSQCModel_CheckUpdate(entity e) void CSQCModel_LinkEntity(entity e) { - e.SendEntity = SendEntity_self; - e.SendEntity3 = CSQCModel_Send; + setSendEntity(e, CSQCModel_Send); e.SendFlags = 0xFFFFFF; CSQCModel_CheckUpdate(e); } diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 771782ea7..507807919 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -101,8 +101,6 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } /** return false to remove from the client */ .bool(entity this, entity to, int sendflags) SendEntity3; - bool SendEntity_self(entity to, int sendflags) { SELFPARAM(); return this.SendEntity3(this, to, sendflags); } - void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc) { if (e.classname == "") e.classname = "net_linked"; @@ -115,8 +113,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } setsize(e, mi, ma); } - e.SendEntity = SendEntity_self; - e.SendEntity3 = sendfunc; + setSendEntity(e, sendfunc); e.SendFlags = 0xFFFFFF; if (!docull) e.effects |= EF_NODEPTHTEST; diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index fe26b08a4..4af4b8ab0 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -21,6 +21,7 @@ #define self (this, self) #undef SELFPARAM #define SELFPARAM() const entity this = __self + #define ENGINE_EVENT() const entity this = __self #endif // Step 4: kill unstructured setself @@ -35,7 +36,7 @@ #endif // Step 6: remove SELFPARAM, add parameters -#if 0 +#if 1 #undef SELFPARAM #endif @@ -45,11 +46,11 @@ #define WITHSELF(value, block) block #endif -#define SELFWRAP(T, R, args, forward) \ - .R() T; \ - .R() __##T = T; \ +#define SELFWRAP(T, R, oldargs, args, forward) \ + .R oldargs T; \ + .R oldargs __##T = T; \ .R args self##T; \ - R T##_self() { SELFPARAM(); return this.self##T forward; } + R T##_self oldargs { ENGINE_EVENT(); return this.self##T forward; } noref entity _selftemp; #define SELFWRAP_SET(T, e, f) \ @@ -61,21 +62,43 @@ noref entity _selftemp; #define _SELFWRAP_GET(T, e) \ (0, (e).__##T) -SELFWRAP(think, void, (entity this), (this)) +SELFWRAP(think, void, (), (entity this), (this)) #define setthink(e, f) SELFWRAP_SET(think, e, f) #define getthink(e) SELFWRAP_GET(think, e) -SELFWRAP(touch, void, (entity this), (this)) +SELFWRAP(touch, void, (), (entity this), (this)) #define settouch(e, f) SELFWRAP_SET(touch, e, f) #define gettouch(e) SELFWRAP_GET(touch, e) -SELFWRAP(predraw, void, (entity this), (this)) +SELFWRAP(blocked, void, (), (entity this), (this)) +#define setblocked(e, f) SELFWRAP_SET(blocked, e, f) +#define blocked stopusingthis + +SELFWRAP(predraw, void, (), (entity this), (this)) #define setpredraw(e, f) SELFWRAP_SET(predraw, e, f) -SELFWRAP(customizeentityforclient, bool, (entity this), (this)) +SELFWRAP(customizeentityforclient, bool, (), (entity this), (this)) #define setcefc(e, f) SELFWRAP_SET(customizeentityforclient, e, f) #define getcefc(e) SELFWRAP_GET(customizeentityforclient, e) +SELFWRAP(camera_transform, vector, (vector org, vector ang), (entity this, vector org, vector ang), (this, org, ang)) +#define setcamera_transform(e, f) SELFWRAP_SET(camera_transform, e, f) + +SELFWRAP(SendEntity, bool, (entity to, int sendflags), (entity this, entity to, int sendflags), (this, to, sendflags)) +#define setSendEntity(e, f) SELFWRAP_SET(SendEntity, e, f) + +#ifdef SVQC +void make_safe_for_remove(entity this); +#endif + +void objerror(entity this, string s) +{ +#ifdef SVQC + make_safe_for_remove(this); +#endif + WITHSELF(this, builtin_objerror(s)); +} + #ifndef MENUQC void adaptor_think2touch(entity this) { WITH(entity, other, NULL, gettouch(this)(this)); } void adaptor_think2use(entity this) { if (this.use) this.use(this, NULL, NULL); } diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index 5967c1ba1..704feeb7e 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -37,10 +37,9 @@ void WarpZone_Accumulator_AddInverse(entity acc, entity wz) WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift); } -.vector(vector, vector) camera_transform; float autocvar_cl_warpzone_usetrace = 1; -vector WarpZone_camera_transform(vector org, vector ang) -{SELFPARAM(); +vector WarpZone_camera_transform(entity this, vector org, vector ang) +{ vector vf, vr, vu; if(this.warpzone_fadestart) if(vdist(org - this.origin - 0.5 * (this.mins + this.maxs), >, this.warpzone_fadeend + 400)) @@ -74,11 +73,11 @@ void WarpZone_SetUp(entity e, vector my_org, vector my_ang, vector other_org, ve e.warpzone_targetangles = other_ang; fixedmakevectors(my_ang); e.warpzone_forward = v_forward; fixedmakevectors(other_ang); e.warpzone_targetforward = v_forward; - e.camera_transform = WarpZone_camera_transform; + setcamera_transform(e, WarpZone_camera_transform); } -vector WarpZone_Camera_camera_transform(vector org, vector ang) -{SELFPARAM(); +vector WarpZone_Camera_camera_transform(entity this, vector org, vector ang) +{ // a fixed camera view if(this.warpzone_fadestart) if(vdist(org - this.origin - 0.5 * (this.mins + this.maxs), >, this.warpzone_fadeend + 400)) @@ -94,7 +93,7 @@ void WarpZone_Camera_SetUp(entity e, vector my_org, vector my_ang) // we assume { e.warpzone_origin = my_org; e.warpzone_angles = my_ang; - e.camera_transform = WarpZone_Camera_camera_transform; + setcamera_transform(e, WarpZone_Camera_camera_transform); } .entity enemy; diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index c373e1eae..a84b7a739 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -169,8 +169,7 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) entity ts = new(warpzone_teleported); setmodel(ts, MDL_Null); - ts.SendEntity = SendEntity_self; - ts.SendEntity3 = WarpZone_Teleported_Send; + setSendEntity(ts, WarpZone_Teleported_Send); ts.SendFlags = 0xFFFFFF; ts.drawonlytoclient = player; setthink(ts, SUB_Remove); @@ -771,8 +770,7 @@ spawnfunc(trigger_warpzone) setsize(this, this.mins * this.scale, this.maxs * this.scale); else setsize(this, this.mins, this.maxs); - this.SendEntity = SendEntity_self; - this.SendEntity3 = WarpZone_Send; + setSendEntity(this, WarpZone_Send); this.SendFlags = 0xFFFFFF; BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); this.warpzone_next = warpzone_first; @@ -798,8 +796,7 @@ spawnfunc(func_camera) this.solid = SOLID_BSP; else if(this.solid < 0) this.solid = SOLID_NOT; - this.SendEntity = SendEntity_self; - this.SendEntity3 = WarpZone_Camera_Send; + setSendEntity(this, WarpZone_Camera_Send); this.SendFlags = 0xFFFFFF; this.warpzone_next = warpzone_camera_first; warpzone_camera_first = this; diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 7dda06011..e92a6082a 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -110,7 +110,7 @@ void info_autoscreenshot_findtarget(entity this) e = find(world, targetname, this.target); if(!e) { - objerror("Missing target. FAIL!"); + objerror(this, "Missing target. FAIL!"); return; } vector a = vectoangles(e.origin - this.origin); @@ -123,7 +123,7 @@ spawnfunc(info_autoscreenshot) { if(++num_autoscreenshot > autocvar_g_max_info_autoscreenshot) { - objerror("Too many info_autoscreenshot entitites. FAIL!"); + objerror(this, "Too many info_autoscreenshot entitites. FAIL!"); return; } if(this.target != "") diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index bd12c1e5c..8da1f1076 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -441,8 +441,7 @@ void FixPlayermodel(entity player) /** Called when a client spawns in the server */ void PutClientInServer() -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if (IS_BOT_CLIENT(this)) { TRANSMUTE(Player, this); } else if (IS_REAL_CLIENT(this)) { @@ -733,7 +732,7 @@ SetChangeParms ============= */ void SetChangeParms () -{SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); // save parms for level change parm1 = this.parm_idlesince - time; @@ -949,7 +948,7 @@ void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, } void ClientKill () -{SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if(gameover) return; if(this.player_blocked) return; if(STAT(FROZEN, this)) return; @@ -999,7 +998,7 @@ Called once (not at each match start) when a client begins a connection to the s ============= */ void ClientPreConnect () -{SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if(autocvar_sv_eventlog) { GameLogEcho(sprintf(":connect:%d:%d:%s", @@ -1019,8 +1018,7 @@ Called when a client connects to the server ============= */ void ClientConnect() -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if (Ban_MaybeEnforceBanOnce(this)) return; assert(!IS_CLIENT(this), return); this.flags |= FL_CLIENT; @@ -1176,8 +1174,7 @@ Called when a client disconnects from the server .entity chatbubbleentity; void ReadyCount(); void ClientDisconnect() -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); assert(IS_CLIENT(this), return); PlayerStats_GameReport_FinalizePlayer(this); @@ -2070,8 +2067,7 @@ Called every frame for each client before the physics are run .float last_vehiclecheck; .int items_added; void PlayerPreThink () -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); WarpZone_PlayerPhysics_FixVAngle(this); STAT(GAMESTARTTIME, this) = game_starttime; @@ -2420,8 +2416,7 @@ Called every frame for each client after the physics are run */ .float idlekick_lasttimeleft; void PlayerPostThink () -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); if (sv_maxidle > 0) if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). if (IS_REAL_CLIENT(this)) diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index cf0b536d5..1cbc699ae 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -705,8 +705,7 @@ void ClientCommand_macro_write_aliases(float fh) // If this function exists, server game code parses clientcommand before the engine code gets it. void SV_ParseClientCommand(string command) -{ - SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); // If invalid UTF-8, don't even parse it string command2 = ""; float len = strlen(command); diff --git a/qcsrc/server/g_lights.qc b/qcsrc/server/g_lights.qc index 9e2cd1156..98f2c4363 100644 --- a/qcsrc/server/g_lights.qc +++ b/qcsrc/server/g_lights.qc @@ -45,7 +45,7 @@ void dynlight_find_aiment(entity this) { entity targ; if (!this.target) - objerror ("dynlight: no target to follow"); + objerror (this, "dynlight: no target to follow"); targ = find(world, targetname, this.target); this.movetype = MOVETYPE_FOLLOW; @@ -61,7 +61,7 @@ void dynlight_find_path(entity this) { entity targ; if (!this.target) - objerror ("dynlight: no target to follow"); + objerror (this, "dynlight: no target to follow"); targ = find(world, targetname, this.target); this.target = targ.target; @@ -73,7 +73,7 @@ void dynlight_find_target(entity this) { entity targ; if (!this.target) - objerror ("dynlight: no target to follow"); + objerror (this, "dynlight: no target to follow"); targ = find(world, targetname, this.target); setattachment(this, targ, this.dtagname); diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index b3b4d2eb0..d4463f693 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -443,7 +443,7 @@ bool InitMovingBrushTrigger(entity this) this.movetype = MOVETYPE_PUSH; if(this.modelindex == 0) { - objerror("InitMovingBrushTrigger: no brushes found!"); + objerror(this, "InitMovingBrushTrigger: no brushes found!"); return false; } return true; diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index 656eede4c..317b897b0 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -159,7 +159,7 @@ spawnfunc(item_key) // reject this entity if more than one key was set! if (this.itemkeys>0 && (this.itemkeys & (this.itemkeys-1)) != 0) { - objerror("item_key.itemkeys must contain only 1 bit set specifying the key it represents!"); + objerror(this, "item_key.itemkeys must contain only 1 bit set specifying the key it represents!"); remove(this); return; } @@ -201,7 +201,7 @@ spawnfunc(item_key) _colormod = '1 1 1'; if (this.netname == "") { - objerror("item_key doesn't have a default name for this key and a custom one was not specified!"); + objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!"); remove(this); return; } @@ -216,7 +216,7 @@ spawnfunc(item_key) } else if (this.itemkeys >= ITEM_KEY_BIT(3) && this.itemkeys <= ITEM_KEY_BIT(5)) { _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model! } else if (this.model == "") { - objerror("item_key doesn't have a default model for this key and a custom one was not specified!"); + objerror(this, "item_key doesn't have a default model for this key and a custom one was not specified!"); remove(this); return; } diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 44c21610a..d4c4bb7e5 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -823,12 +823,6 @@ void make_safe_for_remove(entity e) } } -void objerror(string s) -{SELFPARAM(); // needed for engine functions - make_safe_for_remove(this); - builtin_objerror(s); -} - .float remove_except_protected_forbidden; void remove_except_protected(entity e) { diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index 4ce33b198..a8304b0e3 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -55,7 +55,6 @@ void InitializeEntitiesRun(); void stopsoundto(float _dest, entity e, float chan); void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten); -void objerror(string s); void droptofloor(entity this); void attach_sameorigin(entity e, entity to, string tag); diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index cf27a8f0d..8c9a01e27 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -148,13 +148,13 @@ void assault_setenemytoobjective(entity this) if(this.enemy == world) this.enemy = objective; else - objerror("more than one objective as target - fix the map!"); + objerror(this, "more than one objective as target - fix the map!"); break; } } if(this.enemy == world) - objerror("no objective as target - fix the map!"); + objerror(this, "no objective as target - fix the map!"); } bool assault_decreaser_sprite_visible(entity this, entity player, entity view) diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc index 5f17bf6a1..eed44a789 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc @@ -312,7 +312,7 @@ void dom_controlpoint_setup(entity this) while(head && head.netname != "") head = find(head, classname, "dom_team"); if (!head) - objerror("no spawnfunc_dom_team with netname \"\" found\n"); + objerror(this, "no spawnfunc_dom_team with netname \"\" found\n"); // copy important properties from spawnfunc_dom_team entity this.goalentity = head; diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index 6859b4849..c71ce1d31 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -81,7 +81,7 @@ void relocate_spawnpoint(entity this) this.mins = PL_MIN_CONST; this.maxs = PL_MAX_CONST; if (!move_out_of_solid(this)) - objerror("could not get out of solid at all!"); + objerror(this, "could not get out of solid at all!"); LOG_INFO("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1')); LOG_INFO(" needs to be moved out of solid, e.g. by '", ftos(this.origin.x - o.x)); LOG_INFO(" ", ftos(this.origin.y - o.y)); @@ -96,7 +96,7 @@ void relocate_spawnpoint(entity this) { setorigin(this, o); this.mins = this.maxs = '0 0 0'; - objerror("player spawn point in solid, mapper sucks!\n"); + objerror(this, "player spawn point in solid, mapper sucks!\n"); return; } } diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 0c1cc6676..e1bf5c0be 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -238,7 +238,7 @@ void StartFrame() .string cvarfilter; bool DoesQ3ARemoveThisEntity(entity this); void SV_OnEntityPreSpawnFunction() -{SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); __spawnfunc_expect = this; if (this) if (this.gametypefilter != "") diff --git a/qcsrc/server/sys-post.qh b/qcsrc/server/sys-post.qh index a74e521ab..e24a790a1 100644 --- a/qcsrc/server/sys-post.qh +++ b/qcsrc/server/sys-post.qh @@ -1,6 +1,5 @@ #pragma once -#undef objerror #undef droptofloor #undef sound #undef remove diff --git a/qcsrc/server/sys-pre.qh b/qcsrc/server/sys-pre.qh index ca6a2044d..7ab3f7b9f 100644 --- a/qcsrc/server/sys-pre.qh +++ b/qcsrc/server/sys-pre.qh @@ -1,6 +1,5 @@ #pragma once -#define objerror builtin_objerror #define droptofloor builtin_droptofloor #define remove builtin_remove #define cvar_set builtin_cvar_set diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 945e7333b..203396f31 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -560,7 +560,7 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam) //void() ctf_playerchanged; void SV_ChangeTeam(float _color) -{SELFPARAM(); // needed for engine functions +{ENGINE_EVENT(); float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount; // in normal deathmatch we can just apply the color and we're done -- 2.39.2