X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fclient.qc;h=c1ee3aceb39780f2b3ede953fd45ff0aac798c60;hb=a426118517e1f268880c45f2fdbb57e4e56d6478;hp=1f61b18aa2729221c2d4be3d7e5bd5b9fd74d8d0;hpb=99a2f0703c672032268c7589db0a392a30010e96;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 1f61b18aa..c1ee3aceb 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -981,7 +982,7 @@ bool findinlist_abbrev(string tofind, string list) return false; // empty list or search, just return // this function allows abbreviated strings! - FOREACH_WORD(list, it == substring(tofind, 0, strlen(it)), + FOREACH_WORD(list, it != "" && it == substring(tofind, 0, strlen(it)), { return true; }); @@ -1009,6 +1010,8 @@ bool PlayerInIDList(entity p, string idlist) bool PlayerInList(entity player, string list) { + if (list == "") + return false; return boolean(PlayerInIDList(player, list) || PlayerInIPList(player, list)); } @@ -1041,9 +1044,7 @@ void SendWelcomeMessage(entity this, int msg_type) WriteByte(msg_type, boolean(autocvar_g_campaign)); if (boolean(autocvar_g_campaign)) { - WriteString(msg_type, Campaign_GetTitle()); WriteByte(msg_type, Campaign_GetLevelNum()); - WriteString(msg_type, Campaign_GetMessage()); return; } WriteString(msg_type, autocvar_hostname); @@ -1191,7 +1192,7 @@ void ClientConnect(entity this) if (PlayerInList(this, autocvar_g_playban_list)) TRANSMUTE(Observer, this); - if (PlayerInList(this, autocvar_g_muteban_list)) // muteban + if (PlayerInList(this, autocvar_g_chatban_list)) // chatban CS(this).muted = true; MUTATOR_CALLHOOK(ClientConnect, this); @@ -1203,6 +1204,11 @@ void ClientConnect(entity this) localcmd("\nsv_hook_firstjoin\n"); } } + +.string shootfromfixedorigin; +.entity chatbubbleentity; +void player_powerups_remove_all(entity this); + /* ============= ClientDisconnect @@ -1210,9 +1216,6 @@ ClientDisconnect Called when a client disconnects from the server ============= */ -.entity chatbubbleentity; -void player_powerups_remove_all(entity this); - void ClientDisconnect(entity this) { assert(IS_CLIENT(this), return); @@ -1256,6 +1259,8 @@ void ClientDisconnect(entity this) RemoveGrapplingHooks(this); + strfree(this.shootfromfixedorigin); + // Here, everything has been done that requires this player to be a client. this.flags &= ~FL_CLIENT; @@ -2097,7 +2102,32 @@ bool joinAllowed(entity this) return true; } -.string shootfromfixedorigin; +void show_entnum(entity this) +{ + // waypoint editor implements a similar feature for waypoints + if (waypointeditor_enabled) + return; + + if (wasfreed(this.wp_aimed)) + this.wp_aimed = NULL; + + WarpZone_crosshair_trace_plusvisibletriggers(this); + entity ent = NULL; + if (trace_ent) + { + ent = trace_ent; + if (ent != this.wp_aimed) + { + string str = sprintf( + "^7ent #%d\n^8 netname: ^3%s\n^8 classname: ^5%s\n^8 origin: ^2'%s'", + etof(ent), ent.netname, ent.classname, vtos(ent.origin)); + debug_text_3d((ent.absmin + ent.absmax) * 0.5, str, 0, 7, '0 0 0'); + } + } + if (this.wp_aimed != ent) + this.wp_aimed = ent; +} + .bool dualwielding_prev; bool PlayerThink(entity this) { @@ -2118,6 +2148,8 @@ bool PlayerThink(entity this) if (frametime) player_powerups(this); + if (frametime && autocvar_sv_show_entnum) show_entnum(this); + if (IS_DEAD(this)) { if (this.personal && g_race_qualifying) { if (time > this.respawn_time) { @@ -2197,7 +2229,7 @@ bool PlayerThink(entity this) FixPlayermodel(this); if (this.shootfromfixedorigin != autocvar_g_shootfromfixedorigin) { - this.shootfromfixedorigin = autocvar_g_shootfromfixedorigin; + strcpy(this.shootfromfixedorigin, autocvar_g_shootfromfixedorigin); stuffcmd(this, sprintf("\ncl_shootfromfixedorigin \"%s\"\n", autocvar_g_shootfromfixedorigin)); } @@ -2262,11 +2294,18 @@ void ObserverOrSpectatorThink(entity this) } } + if (frametime && autocvar_sv_show_entnum) show_entnum(this); + if (IS_BOT_CLIENT(this) && !CS(this).autojoin_checked) { CS(this).autojoin_checked = true; TRANSMUTE(Player, this); PutClientInServer(this); + + .entity weaponentity = weaponentities[0]; + if(this.(weaponentity).m_weapon == WEP_Null) + W_NextWeapon(this, 0, weaponentity); + return; } @@ -2387,135 +2426,23 @@ void PlayerUseKey(entity this) ============= PlayerPreThink -Called every frame for each client before the physics are run +Called every frame for each real client by DP (and for each bot by StartFrame()), +and when executing every asynchronous move, so only include things that MUST be done then. +Use PlayerFrame() instead for code that only needs to run once per server frame. +frametime == 0 in the asynchronous code path. + +TODO: move more stuff from here and PlayerThink() and ObserverOrSpectatorThink() to PlayerFrame() (frametime is always set there) ============= */ .float last_vehiclecheck; void PlayerPreThink (entity this) { - STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO - STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump; - WarpZone_PlayerPhysics_FixVAngle(this); - if (frametime) { - // physics frames: update anticheat stuff - anticheat_prethink(this); - - // WORKAROUND: only use dropclient in server frames (frametime set). - // Never use it in cl_movement frames (frametime zero). - if (blockSpectators && IS_REAL_CLIENT(this) - && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this) - && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime)) - { - if (dropclient_schedule(this)) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); - } - } - zoomstate_set = false; - // Check for nameless players - if (this.netname == "" || this.netname != CS(this).netname_previous) - { - bool assume_unchanged = (CS(this).netname_previous == ""); - if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength) - { - int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol); - this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7")); - sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength)); - assume_unchanged = false; - // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? - } - if (isInvisibleString(this.netname)) - { - this.netname = strzone(sprintf("Player#%d", this.playerid)); - sprint(this, "Warning: invisible names are not allowed.\n"); - assume_unchanged = false; - // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? - } - if (!assume_unchanged && autocvar_sv_eventlog) - GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false))); - strcpy(CS(this).netname_previous, this.netname); - } - - // version nagging - if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) { - CS(this).version_nagtime = 0; - if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git client - } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); - } else { - int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion); - if (r < 0) { // old client - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); - } else if (r > 0) { // old server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); - } - } - } - - // GOD MODE info - if (!(this.flags & FL_GODMODE) && this.max_armorvalue) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); - this.max_armorvalue = 0; - } - - if (frametime && IS_PLAYER(this) && time >= game_starttime) - { - if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING) - { - STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1); - SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); - if (this.iceblock) - this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); - - if (STAT(REVIVE_PROGRESS, this) >= 1) - Unfreeze(this, false); - } - else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING) - { - STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1); - SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); - - if (GetResource(this, RES_HEALTH) < 1) - { - if (this.vehicle) - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(this.event_damage) - this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); - } - else if (STAT(REVIVE_PROGRESS, this) <= 0) - Unfreeze(this, false); - } - } - MUTATOR_CALLHOOK(PlayerPreThink, this); - if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle) - if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this)) - { - FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO, - { - if(!it.owner) - { - if(!it.team || SAME_TEAM(this, it)) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); - else if(autocvar_g_vehicles_steal) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); - } - else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this)) - { - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); - } - }); - - this.last_vehiclecheck = time + 1; - } - if(PHYS_INPUT_BUTTON_USE(this) && !CS(this).usekeypressed) PlayerUseKey(this); CS(this).usekeypressed = PHYS_INPUT_BUTTON_USE(this); @@ -2563,6 +2490,7 @@ void PlayerPreThink (entity this) SetZoomState(this, PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) || wep_zoomed); } + // Voice sound effects if (CS(this).teamkill_soundtime && time > CS(this).teamkill_soundtime) { CS(this).teamkill_soundtime = 0; @@ -2633,15 +2561,180 @@ void Player_Physics(entity this) ============= PlayerPostThink -Called every frame for each client after the physics are run +Called every frame for each real client by DP (and for each bot by StartFrame()), +and when executing every asynchronous move, so only include things that MUST be done then. +Use PlayerFrame() instead for code that only needs to run once per server frame. +frametime == 0 in the asynchronous code path. ============= */ void PlayerPostThink (entity this) { Player_Physics(this); + if (IS_PLAYER(this)) { + if(this.death_time == time && IS_DEAD(this)) + { + // player's bbox gets resized now, instead of in the damage event that killed the player, + // once all the damage events of this frame have been processed with normal size + this.maxs.z = 5; + setsize(this, this.mins, this.maxs); + } + DrownPlayer(this); + UpdateChatBubble(this); + if (CS(this).impulse) ImpulseCommands(this); + GetPressedKeys(this); + if (game_stopped) + { + CSQCMODEL_AUTOUPDATE(this); + return; + } + } + else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this)) + { + CS(this).pressedkeys = 0; + STAT(PRESSED_KEYS, this) = 0; + } + + CSQCMODEL_AUTOUPDATE(this); +} + +/* +============= +PlayerFrame + +Called every frame for each client by StartFrame(). +Use this for code that only needs to run once per server frame. +frametime is always set here. +============= +*/ +void PlayerFrame (entity this) +{ +// formerly PreThink code + STAT(GUNALIGN, this) = CS_CVAR(this).cvar_cl_gunalign; // TODO + STAT(MOVEVARS_CL_TRACK_CANJUMP, this) = CS_CVAR(this).cvar_cl_movement_track_canjump; + + // physics frames: update anticheat stuff + anticheat_prethink(this); + + // Check if spectating is allowed + if (blockSpectators && IS_REAL_CLIENT(this) + && (IS_SPEC(this) || IS_OBSERVER(this)) && !INGAME(this) + && time > (CS(this).spectatortime + autocvar_g_maxplayers_spectator_blocktime)) + { + if (dropclient_schedule(this)) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); + } + + // Check for nameless players + if (this.netname == "" || this.netname != CS(this).netname_previous) + { + bool assume_unchanged = (CS(this).netname_previous == ""); + if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength) + { + int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol); + this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7")); + sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength)); + assume_unchanged = false; + // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? + } + if (isInvisibleString(this.netname)) + { + this.netname = strzone(sprintf("Player#%d", this.playerid)); + sprint(this, "Warning: invisible names are not allowed.\n"); + assume_unchanged = false; + // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? + } + if (!assume_unchanged && autocvar_sv_eventlog) + GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this.netname, this.team, false))); + strcpy(CS(this).netname_previous, this.netname); + } + + // version nagging + if (CS(this).version_nagtime && CS_CVAR(this).cvar_g_xonoticversion && time > CS(this).version_nagtime) + { + CS(this).version_nagtime = 0; + if (strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(CS_CVAR(this).cvar_g_xonoticversion, "autobuild", 0) >= 0) + { + // git client + } + else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) + { + // git server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); + } + else + { + int r = vercmp(CS_CVAR(this).cvar_g_xonoticversion, autocvar_g_xonoticversion); + if (r < 0) // old client + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); + else if (r > 0) // old server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, CS_CVAR(this).cvar_g_xonoticversion); + } + } + + // GOD MODE info + if (!(this.flags & FL_GODMODE) && this.max_armorvalue) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); + this.max_armorvalue = 0; + } + + // FreezeTag + if (IS_PLAYER(this) && time >= game_starttime) + { + if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING) + { + STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1); + SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); + if (this.iceblock) + this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); + + if (STAT(REVIVE_PROGRESS, this) >= 1) + Unfreeze(this, false); + } + else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING) + { + STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1); + SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); + + if (GetResource(this, RES_HEALTH) < 1) + { + if (this.vehicle) + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(this.event_damage) + this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); + } + else if (STAT(REVIVE_PROGRESS, this) <= 0) + Unfreeze(this, false); + } + } + + // Vehicles + if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle) + if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this)) + { + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO, + { + if(!it.owner) + { + if(!it.team || SAME_TEAM(this, it)) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); + else if(autocvar_g_vehicles_steal) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); + } + else if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this)) + { + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); + } + }); + + this.last_vehiclecheck = time + 1; + } + + + +// formerly PostThink code if (autocvar_sv_maxidle > 0 || (IS_PLAYER(this) && autocvar_sv_maxidle_playertospectator > 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)) if (IS_PLAYER(this) || autocvar_sv_maxidle_alsokickspectators) if (!intermission_running) // NextLevel() kills all centerprints after setting this true @@ -2728,36 +2821,10 @@ void PlayerPostThink (entity this) CS(this).teamkill_soundsource = NULL; } - if (IS_PLAYER(this)) { - if(this.death_time == time && IS_DEAD(this)) - { - // player's bbox gets resized now, instead of in the damage event that killed the player, - // once all the damage events of this frame have been processed with normal size - this.maxs.z = 5; - setsize(this, this.mins, this.maxs); - } - DrownPlayer(this); - UpdateChatBubble(this); - if (CS(this).impulse) ImpulseCommands(this); - GetPressedKeys(this); - if (game_stopped) - { - CSQCMODEL_AUTOUPDATE(this); - return; - } - } - else if (IS_OBSERVER(this) && STAT(PRESSED_KEYS, this)) - { - CS(this).pressedkeys = 0; - STAT(PRESSED_KEYS, this) = 0; - } - if (this.waypointsprite_attachedforcarrier) { float hp = healtharmor_maxdamage(GetResource(this, RES_HEALTH), GetResource(this, RES_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id).x; WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, hp); } - - CSQCMODEL_AUTOUPDATE(this); } // hack to copy the button fields from the client entity to the Client State