Skip to content

Commit 4d3fbcf

Browse files
authored
Merge pull request #447 from openmultiplayer/fixes.inc-players
Fixes.inc players
2 parents b9d5e8e + b00b735 commit 4d3fbcf

File tree

15 files changed

+491
-27
lines changed

15 files changed

+491
-27
lines changed

SDK/include/Server/Components/Classes/classes.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct IPlayerClassData : public IExtension {
3535

3636
virtual const PlayerClass& getClass() = 0;
3737
virtual void setSpawnInfo(const PlayerClass& info) = 0;
38+
virtual void spawnPlayer() = 0;
3839
};
3940

4041
/// The player class event handler
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
3+
#include <sdk.hpp>
4+
#include <Server/Components/Timers/timers.hpp>
5+
6+
static const UID FixesData_UID = UID(0x672d5d6fbb094ef7);
7+
struct IPlayerFixesData : public IExtension {
8+
PROVIDE_EXT_UID(FixesData_UID);
9+
};
10+
11+
static const UID FixesComponent_UID = UID(0xb5c615eff0329ff7);
12+
struct IFixesComponent : public IComponent {
13+
PROVIDE_UID(FixesComponent_UID);
14+
};

SDK/include/Server/Components/Timers/timers.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#pragma once
2+
13
#include <chrono>
24
#include <component.hpp>
35
#include <events.hpp>
@@ -57,3 +59,35 @@ struct ITimersComponent : public IComponent {
5759
/// Returns running timers count.
5860
virtual const size_t count() const = 0;
5961
};
62+
63+
// Only a pointer to this is passed across binary boundaries, so using `std::function` should be ABI stable.
64+
class SimpleTimerHandler final : public TimerTimeOutHandler {
65+
private:
66+
std::function<void()> handler_;
67+
68+
// Ensure only `free` can delete this.
69+
~SimpleTimerHandler()
70+
{
71+
}
72+
73+
public:
74+
SimpleTimerHandler(std::function<void()> const& handler)
75+
: handler_(handler)
76+
{
77+
}
78+
79+
SimpleTimerHandler(std::function<void()>&& handler)
80+
: handler_(std::move(handler))
81+
{
82+
}
83+
84+
void timeout(ITimer& timer) override
85+
{
86+
handler_();
87+
}
88+
89+
void free(ITimer& timer) override
90+
{
91+
delete this;
92+
}
93+
};

SDK/include/anim.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1981,7 +1981,7 @@ static struct AnimationLibraryLookup {
19811981

19821982
inline Pair<StringView, StringView> splitAnimationNames(int ID)
19831983
{
1984-
if (ID < 0 || ID >= GLM_COUNTOF(AnimationNames)) {
1984+
if (ID <= 0 || ID >= GLM_COUNTOF(AnimationNames)) {
19851985
return { "", "" };
19861986
}
19871987

SDK/include/player.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,12 +464,18 @@ struct IPlayer : public IExtensible, public IEntity {
464464
/// Give a weapon to the player
465465
virtual void giveWeapon(WeaponSlotData weapon) = 0;
466466

467+
/// Removes player weapon
468+
virtual void removeWeapon(uint8_t weapon) = 0;
469+
467470
/// Set the player's ammo for a weapon
468471
virtual void setWeaponAmmo(WeaponSlotData data) = 0;
469472

470473
/// Get player's weapons
471474
virtual const WeaponSlots& getWeapons() const = 0;
472475

476+
/// Get single weapon
477+
virtual WeaponSlotData getWeaponSlot(int slot) = 0;
478+
473479
/// Reset the player's weapons
474480
virtual void resetWeapons() = 0;
475481

@@ -758,7 +764,7 @@ struct IPlayer : public IExtensible, public IEntity {
758764
virtual bool hasCameraTargeting() const = 0;
759765

760766
/// Remove the player from their vehicle
761-
virtual void removeFromVehicle() = 0;
767+
virtual void removeFromVehicle(bool force) = 0;
762768

763769
/// Get the player the player is looking at or nullptr if none
764770
virtual IPlayer* getCameraTargetPlayer() = 0;
@@ -804,6 +810,9 @@ struct IPlayer : public IExtensible, public IEntity {
804810

805811
/// Get if player is kicked or not (about to be disconnected)
806812
virtual bool getKickStatus() const = 0;
813+
814+
/// Clear player tasks
815+
virtual void clearTasks(PlayerAnimationSyncType syncType) = 0;
807816
};
808817

809818
/// A player event handler

Server/Components/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_subdirectory(Checkpoints)
44
add_subdirectory(Classes)
55
add_subdirectory(Console)
66
add_subdirectory(Dialogs)
7+
add_subdirectory(Fixes)
78
add_subdirectory(GangZones)
89
add_subdirectory(Menus)
910
add_subdirectory(Objects)

Server/Components/Classes/classes_main.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ class PlayerClassData final : public IPlayerClassData {
2424
private:
2525
IPlayer& player;
2626
PlayerClass cls;
27+
bool default_;
2728

2829
friend class ClassesComponent;
2930

3031
public:
3132
PlayerClassData(IPlayer& player)
3233
: player(player)
3334
, cls(defClass)
35+
, default_(true)
3436
{
3537
}
3638

@@ -39,6 +41,15 @@ class PlayerClassData final : public IPlayerClassData {
3941
return cls;
4042
}
4143

44+
void spawnPlayer() override
45+
{
46+
if (default_) {
47+
setSpawnInfo(defClass);
48+
}
49+
NetCode::RPC::ImmediatelySpawnPlayer RPC;
50+
PacketHelper::send(RPC, player);
51+
}
52+
4253
void setSpawnInfo(const PlayerClass& info) override
4354
{
4455
const WeaponSlots& weapons = info.weapons;
@@ -54,6 +65,7 @@ class PlayerClassData final : public IPlayerClassData {
5465

5566
cls = info;
5667
player.setTeam(info.team);
68+
default_ = false;
5769
PacketHelper::send(setSpawnInfoRPC, player);
5870
}
5971

@@ -65,6 +77,7 @@ class PlayerClassData final : public IPlayerClassData {
6577
void reset() override
6678
{
6779
cls = defClass;
80+
default_ = true;
6881
}
6982
};
7083

@@ -137,6 +150,7 @@ class ClassesComponent final : public IClassesComponent, public PlayerEventHandl
137150
PlayerClassData* player_data = queryExtension<PlayerClassData>(peer);
138151
if (player_data) {
139152
player_data->cls = *used_class;
153+
player_data->default_ = false;
140154
}
141155
}
142156

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME)
2+
add_server_component(${ProjectId})

Server/Components/Fixes/fixes.cpp

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#include <Impl/events_impl.hpp>
2+
#include <Server/Components/Fixes/fixes.hpp>
3+
#include <Server/Components/Classes/classes.hpp>
4+
#include <netcode.hpp>
5+
6+
using namespace Impl;
7+
8+
class PlayerFixesData final : public IPlayerFixesData {
9+
private:
10+
IPlayer& player_;
11+
ITimer* moneyTimer_ = nullptr;
12+
ITimersComponent& timers_;
13+
int money_ = 0;
14+
15+
void MoneyTimer()
16+
{
17+
player_.setMoney(money_);
18+
}
19+
20+
public:
21+
void freeExtension() override
22+
{
23+
delete this;
24+
}
25+
26+
PlayerFixesData(IPlayer& player, ITimersComponent& timers)
27+
: player_(player)
28+
, timers_(timers)
29+
{
30+
}
31+
32+
void startMoneyTimer()
33+
{
34+
if (moneyTimer_) {
35+
moneyTimer_->kill();
36+
}
37+
// TODO: This must be fixed on client side
38+
// 50 gives very good results in terms of not flickering. 100 gives OK results. 80 is
39+
// between them to try and balance effect and bandwidth.
40+
money_ = player_.getMoney();
41+
moneyTimer_ = timers_.create(new SimpleTimerHandler(std::bind(&PlayerFixesData::MoneyTimer, this)), Milliseconds(80), true);
42+
}
43+
44+
void stopMoneyTimer()
45+
{
46+
if (moneyTimer_) {
47+
moneyTimer_->kill();
48+
player_.setMoney(player_.getMoney());
49+
}
50+
moneyTimer_ = nullptr;
51+
}
52+
53+
void reset() override
54+
{
55+
if (moneyTimer_) {
56+
moneyTimer_->kill();
57+
}
58+
}
59+
};
60+
61+
class FixesComponent final : public IFixesComponent, public PlayerEventHandler, public ClassEventHandler {
62+
private:
63+
IClassesComponent* classes_ = nullptr;
64+
IPlayerPool* players_ = nullptr;
65+
ITimersComponent* timers_ = nullptr;
66+
Microseconds resetMoney_ = Microseconds(0);
67+
68+
public:
69+
StringView componentName() const override
70+
{
71+
return "Fixes";
72+
}
73+
74+
SemanticVersion componentVersion() const override
75+
{
76+
return SemanticVersion(0, 0, 0, BUILD_NUMBER);
77+
}
78+
79+
FixesComponent()
80+
{
81+
}
82+
83+
~FixesComponent()
84+
{
85+
if (players_) {
86+
players_->getEventDispatcher().removeEventHandler(this);
87+
}
88+
if (classes_) {
89+
classes_->getEventDispatcher().removeEventHandler(this);
90+
}
91+
}
92+
93+
void free() override
94+
{
95+
delete this;
96+
}
97+
98+
void reset() override
99+
{
100+
}
101+
102+
void onLoad(ICore* c) override
103+
{
104+
constexpr event_order_t EventPriority_Fixes = 100;
105+
players_ = &c->getPlayers();
106+
players_->getEventDispatcher().addEventHandler(this, EventPriority_Fixes);
107+
}
108+
109+
void onInit(IComponentList* components) override
110+
{
111+
constexpr event_order_t EventPriority_Fixes = -100;
112+
classes_ = components->queryComponent<IClassesComponent>();
113+
if (classes_) {
114+
classes_->getEventDispatcher().addEventHandler(this, EventPriority_Fixes);
115+
}
116+
timers_ = components->queryComponent<ITimersComponent>();
117+
}
118+
119+
void onPlayerSpawn(IPlayer& player) override
120+
{
121+
// TODO: This must be fixed on client side
122+
// *
123+
// * <problem>
124+
// * San Andreas deducts $100 from players.
125+
// * </problem>
126+
// * <solution>
127+
// * Reset the player's money to what it was before they died.
128+
// * </solution>
129+
// * <see>OnPlayerSpawn</see>
130+
// * <author href="https://github.com/Y-Less/" >Y_Less</author>
131+
// *
132+
// This is a minimal implementation on its own as the death money isn't synced properly.
133+
// However this code will cause the money to flicker slightly while it goes down and up a
134+
// little bit due to lag. So instead we pre-empt it with a timer constantly resetting the
135+
// cash until they spawn.
136+
PlayerFixesData* data = queryExtension<PlayerFixesData>(player);
137+
if (data) {
138+
data->stopMoneyTimer();
139+
}
140+
}
141+
142+
bool onPlayerRequestClass(IPlayer& player, unsigned int classId) override
143+
{
144+
// TODO: This must be fixed on client side
145+
// *
146+
// * <problem>
147+
// * Random blunts and bottles sometimes appear in class selection.
148+
// * </problem>
149+
// * <solution>
150+
// * Call "RemoveBuildingForPlayer".
151+
// * </solution>
152+
// * <see>OnPlayerRequestClass</see>
153+
// * <author href="https://github.com/Y-Less/" >Y_Less</author>
154+
// *
155+
auto pos = player.getPosition();
156+
player.removeDefaultObjects(1484, pos, 10.0f);
157+
player.removeDefaultObjects(1485, pos, 10.0f);
158+
player.removeDefaultObjects(1486, pos, 10.0f);
159+
return true;
160+
}
161+
162+
void onPlayerDeath(IPlayer& player, IPlayer* killer, int reason) override
163+
{
164+
PlayerFixesData* data = queryExtension<PlayerFixesData>(player);
165+
if (data) {
166+
data->startMoneyTimer();
167+
}
168+
169+
// TODO: This must be fixed on client side
170+
// *
171+
// * <problem>
172+
// * Clients get stuck when they die with an animation applied.
173+
// * </problem>
174+
// * <solution>
175+
// * Clear their animations.
176+
// * </solution>
177+
// * <see>OnPlayerDeath</see>
178+
// * <see>OnPlayerUpdate</see>
179+
// * <author >h02</author>
180+
// * <post href="https://sampforum.blast.hk/showthread.php?tid=312862&amp;pid=1641144#pid1641144" />
181+
// *
182+
auto anim = player.getAnimationData();
183+
if (anim.ID != 0 && anim.name().first.compare("PED")) {
184+
// Not in a "PED" library so may get stuck.
185+
NetCode::RPC::ClearPlayerTasks clearPlayerTasksRPC;
186+
clearPlayerTasksRPC.PlayerID = player.getID();
187+
PacketHelper::send(clearPlayerTasksRPC, player);
188+
}
189+
}
190+
191+
void onPlayerConnect(IPlayer& player) override
192+
{
193+
if (timers_) {
194+
player.addExtension(new PlayerFixesData(player, *timers_), true);
195+
}
196+
}
197+
};
198+
199+
COMPONENT_ENTRY_POINT()
200+
{
201+
return new FixesComponent();
202+
}

Server/Components/Pawn/Manager/Manager.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <Server/Components/Console/console.hpp>
2020
#include <Server/Components/Databases/databases.hpp>
2121
#include <Server/Components/Dialogs/dialogs.hpp>
22+
#include <Server/Components/Fixes/fixes.hpp>
2223
#include <Server/Components/GangZones/gangzones.hpp>
2324
#include <Server/Components/Menus/menus.hpp>
2425
#include <Server/Components/Objects/objects.hpp>
@@ -58,6 +59,7 @@ class PawnManager : public Singleton<PawnManager> {
5859
IDatabasesComponent* databases = nullptr;
5960
IDialogsComponent* dialogs = nullptr;
6061
IGangZonesComponent* gangzones = nullptr;
62+
IFixesComponent* fixes = nullptr;
6163
IMenusComponent* menus = nullptr;
6264
IObjectsComponent* objects = nullptr;
6365
IPickupsComponent* pickups = nullptr;

0 commit comments

Comments
 (0)