Skip to content

Commit

Permalink
Merge branch 'master' into itemprop
Browse files Browse the repository at this point in the history
  • Loading branch information
Jhobean committed Dec 14, 2023
2 parents f5dcb5d + a5d5714 commit 42ae1c9
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 51 deletions.
5 changes: 4 additions & 1 deletion Changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3439,5 +3439,8 @@ Additionally, the problem of zig-zag issue following in the South direction has
09-12-2023, DavideRei
- Added: DispID can now also be used on chars, to change their appearance.

11-12-2023, Jhobean
13-12-2023, Nolok
- Fixed: Rare crash occurring when a NPC is selecting an attackable target, but there's only one target (not attackable) in sight.

14-12-2023, Jhobean
- Added: Item properties functionnality:HITAREAPHYSICAL,HITAREAFIRE,HITAREACOLD,HITAREAPOISON,HITAREAENERGY,HITFIREBALL,HITHARM,HITLIGHTNING,HITMAGICARROW,REFLECTPHYSICALDAM
6 changes: 4 additions & 2 deletions src/game/CSector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,8 @@ void CSector::MoveItemToSector( CItem * pItem )
{
if (_CanSleep(true))
{
pItem->GoSleep();
if (!pItem->CanTick())
pItem->GoSleep();
}
else
{
Expand Down Expand Up @@ -1011,7 +1012,8 @@ bool CSector::MoveCharToSector( CChar * pChar )
}
else if (!pChar->IsSleeping()) // An NPC entered, but the sector is sleeping
{
pChar->GoSleep(); // then make the NPC sleep too.
if (!pChar->CanTick())
pChar->GoSleep(); // then make the NPC sleep too.
}
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/game/CServerConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4441,7 +4441,7 @@ bool CServerConfig::LoadIni( bool fTest )
if( !fTest )
{
g_Log.Event(LOGL_FATAL|LOGM_INIT|LOGF_CONSOLE_ONLY, SPHERE_FILE ".ini has not been found.\n");
g_Log.Event(LOGL_FATAL|LOGM_INIT|LOGF_CONSOLE_ONLY, "Download a sample sphere.ini from https://github.com/Sphereserver/Source-experimental/tree/master/src\n");
g_Log.Event(LOGL_FATAL|LOGM_INIT|LOGF_CONSOLE_ONLY, "Download a sample sphere.ini from https://github.com/Sphereserver/Source-X/tree/master/src\n");
}
return false;
}
Expand Down
5 changes: 0 additions & 5 deletions src/game/CTeleport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,3 @@ bool CTeleport::RealizeTeleport()
else
return false;
}

CTeleport::~CTeleport() noexcept
{
fprintf(stderr, "deleted %s.\n", _ptDst.WriteUsed());
}
2 changes: 1 addition & 1 deletion src/game/CTeleport.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CTeleport : public CPointSort // The static world teleporters.
_fNpc = false;
}
explicit CTeleport(tchar* pszArgs);
~CTeleport() noexcept;
~CTeleport() noexcept = default;

CTeleport(const CTeleport& copy) = delete;
CTeleport& operator=(const CTeleport& other) = delete;
Expand Down
4 changes: 2 additions & 2 deletions src/game/chars/CChar.h
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept;
SKILL_TYPE Fight_GetWeaponSkill() const;
DAMAGE_TYPE Fight_GetWeaponDamType(const CItem* pWeapon = nullptr) const;
int Fight_CalcDamage( const CItem * pWeapon, bool bNoRandom = false, bool bGetMax = true ) const;
bool Fight_IsAttackable();
bool Fight_IsAttackableState();

// Attacker System
enum ATTACKER_CLEAR_TYPE
Expand Down Expand Up @@ -1243,7 +1243,7 @@ public: void StatFlag_Mod(uint64 uiStatFlag, bool fMod) noexcept;
bool NPC_LookAtItem( CItem * pItem, int iDist );
bool NPC_LookAround( bool fForceCheckItems = false );
int NPC_WalkToPoint(bool fRun = false);
CChar * NPC_FightFindBestTarget();
CChar * NPC_FightFindBestTarget(const std::vector<CChar*> * pvExcludeList = nullptr);
bool NPC_FightMagery(CChar * pChar);
bool NPC_FightCast(CObjBase * &pChar ,CObjBase * pSrc, SPELL_TYPE &spell, int &skill, int iHealThreshold, bool bIgnoreAITargetChoice = false);
bool NPC_FightArchery( CChar * pChar );
Expand Down
2 changes: 1 addition & 1 deletion src/game/chars/CCharAct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2204,7 +2204,7 @@ bool CChar::ItemEquip( CItem * pItem, CChar * pCharMsg, bool fFromDClick )
ModPropNum(pCCPChar, PROPCH_DAMCOLD, + pItem->GetPropNum(pItemCCPItemEquippable, PROPIEQUIP_DAMCOLD, pItemBaseCCPItemEquippable));
ModPropNum(pCCPChar, PROPCH_DAMPOISON, + pItem->GetPropNum(pItemCCPItemEquippable, PROPIEQUIP_DAMPOISON, pItemBaseCCPItemEquippable));
ModPropNum(pCCPChar, PROPCH_DAMENERGY, + pItem->GetPropNum(pItemCCPItemEquippable, PROPIEQUIP_DAMENERGY, pItemBaseCCPItemEquippable));

if ( pItem->GetPropNum(pItemCCPItemEquippable, PROPIEQUIP_NIGHTSIGHT, pItemBaseCCPItemEquippable) )
{
StatFlag_Mod( STATF_NIGHTSIGHT, 1 );
Expand Down
5 changes: 3 additions & 2 deletions src/game/chars/CCharFight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1259,7 +1259,7 @@ int CChar::Fight_CalcDamage( const CItem * pWeapon, bool bNoRandom, bool bGetMax
return( Calc_GetRandVal2(iDmgMin, iDmgMax) );
}

bool CChar::Fight_IsAttackable()
bool CChar::Fight_IsAttackableState()
{
ADDTOCALLSTACK("CChar::IsAttackable");
return !IsDisconnected() && !IsStatFlag(STATF_DEAD|STATF_STONE|STATF_INVISIBLE|STATF_INSUBSTANTIAL|STATF_HIDDEN|STATF_INVUL);
Expand Down Expand Up @@ -1434,7 +1434,8 @@ void CChar::Fight_HitTry()
// I can't hit this target, try switch to another one
if (m_pNPC)
{
if ( !Fight_Attack(NPC_FightFindBestTarget()) )
std::vector<CChar*> vExcludeTargets { pCharTarg }; // Ignore the current target, i want other npcs
if (!Fight_Attack(NPC_FightFindBestTarget(&vExcludeTargets)))
{
Skill_Start(SKILL_NONE);
m_Fight_Targ_UID.InitUID();
Expand Down
22 changes: 17 additions & 5 deletions src/game/chars/CCharNPCAct_Fight.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ bool CChar::NPC_FightArchery(CChar * pChar)
return true;
}

CChar * CChar::NPC_FightFindBestTarget()
CChar * CChar::NPC_FightFindBestTarget(const std::vector<CChar*>* pvExcludeList)
{
ADDTOCALLSTACK("CChar::NPC_FightFindBestTarget");
ASSERT(m_pNPC);
Expand All @@ -74,17 +74,26 @@ CChar * CChar::NPC_FightFindBestTarget()
for (size_t i = 0; i < m_lastAttackers.size(); )
{
LastAttackers &refAttacker = m_lastAttackers[i];
pChar = CUID(refAttacker.charUID).CharFind();
pChar = CUID::CharFindFromUID(refAttacker.charUID);
if (!pChar)
{
++i;
continue;
}
if (!pChar->Fight_IsAttackable())
if (!pChar->Fight_IsAttackableState())
{
++i;
continue;
}
if (pvExcludeList)
{
if (pvExcludeList->cend() != std::find(pvExcludeList->cbegin(), pvExcludeList->cend(), pChar))
{
++i;
continue;
}
}

if (refAttacker.ignore)
{
bool bIgnore = true;
Expand Down Expand Up @@ -140,10 +149,13 @@ CChar * CChar::NPC_FightFindBestTarget()
return pClosest ? pClosest : pChar;
}

// New target not found, check if I can keep attacking my current target
// New target not found, return the current target, if any
CChar *pTarget = m_Fight_Targ_UID.CharFind();
if (pTarget)
return pTarget;
{
if (!pvExcludeList || (pvExcludeList->cend() == std::find(pvExcludeList->cbegin(), pvExcludeList->cend(), pTarget)))
return pTarget;
}

return nullptr;
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/chars/CCharNPCStatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,7 @@ int CChar::NPC_GetAttackContinueMotivation( CChar * pChar, int iMotivation ) con
if ( !pChar )
return 0;

if ( !pChar->Fight_IsAttackable() )
if ( !pChar->Fight_IsAttackableState() )
return -100;
if ( m_pNPC->m_Brain == NPCBRAIN_GUARD )
return 100;
Expand Down
83 changes: 66 additions & 17 deletions src/network/linuxev.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,23 @@
}
*/

// Call this function when the socket is readable again -> we are not sending data anymore
// The data is sent (if the checks are passing) at each tick on a CNetworkThread, which sets also isSendingAsync to true. If in that tick
// the CNetworkThread can't send the data (maybe because socketslave_cb wasn't called, so onAsyncSendComplete wasn't called), wait for the next tick.
static void socketslave_cb(struct ev_loop *loop, struct ev_io *w, int revents)
// Call this callback function when the socket is readable or writable again -> we are not sending data anymore
// The data is sent when there's some queued data to send.
static void socketslave_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
ev_io_stop(loop, w);
CNetState* state = reinterpret_cast<CNetState *>( w->data );
// libev could call this function aliasing ev_io as a ev_watcher.
// ev_watcher is a "parent" struct of ev_io, they share the first member variables.
// it's a evil trick, but does the job since C doesn't have struct inheritance

ev_io_stop(loop, watcher);
CNetState* state = reinterpret_cast<CNetState *>( watcher->data );

if ( !g_Serv.IsLoading() )
{
if ( revents & EV_READ )
{
// This happens when the client socket is readable (i can try to retrieve data), this does NOT mean
// that i have data to read. It might also mean that i have done writing to the socket?
// g_NetworkOut.onAsyncSendComplete(state);
}
else if ( revents & EV_WRITE )
Expand All @@ -48,35 +53,72 @@ static void socketslave_cb(struct ev_loop *loop, struct ev_io *w, int revents)
}
}

if ( state->isSendingAsync() )
if (state->isSendingAsync())
{
ev_io_start(loop, w);
ev_io_start(loop, watcher);
}
}

LinuxEv::LinuxEv(void) : AbstractSphereThread("T_NetLoop", IThread::High)
LinuxEv::LinuxEv(void) : AbstractSphereThread("T_NetLoopOut", IThread::High)
//, m_watchMainsock{}
{
m_eventLoop = ev_loop_new(EV_BACKEND_LIST);
ASSERT(m_eventLoop != nullptr); // libev probably couldn't find sys/poll.h, select.h and other includes (compiling on ubuntu with both x86_64 and i386 compilers? or more gcc versions?)
ev_set_io_collect_interval(m_eventLoop, 0.01);

memset(&m_watchMainsock, 0, sizeof(ev_io));
// Right now, we use libev to send asynchronously packets to clients.
m_eventLoop = ev_loop_new(ev_recommended_backends() | EVFLAG_NOENV);
ASSERT(m_eventLoop != nullptr); // if fails, libev config.h probably was not configured properly
ev_set_io_collect_interval(m_eventLoop, 5e-3); // interval: the second is the unit, use decimals for smaller intervals
}

LinuxEv::~LinuxEv(void)
{
ev_loop_destroy(m_eventLoop);
}

void LinuxEv::printInitInfo()
{
g_Log.Event(LOGM_CLIENTS_LOG, "Networking: Libev. Initialized with backend 0x%x.\n", ev_backend(m_eventLoop));
}

void LinuxEv::onStart()
{
// g_Log.Event(LOGM_CLIENTS_LOG, "Event start backend 0x%x\n", ev_backend(m_eventLoop));
AbstractSphereThread::onStart();
}


static void periodic_cb(struct ev_loop* /*loop*/, ev_periodic* /*w*/, int /*revents*/) noexcept
{
;
}

void LinuxEv::tick()
{
ev_run(m_eventLoop, EVRUN_NOWAIT);
/*
A flags value of EVRUN_NOWAIT will look for new events,
will handle those events and any already outstanding ones,
but will not wait and block your process in case there are no events and will return after one iteration of the loop.
*/
//ev_run(m_eventLoop, EVRUN_NOWAIT);

// Trying a different approach: enter the event loop, run again if exits.
// ev_run will keep handling events until either no event watchers are active anymore or "ev_break" was called

#ifdef _DEBUG
g_Log.EventDebug("Networking: Libev. Starting event loop.\n");
#endif

// This periodic timer keeps awake the event loop. We could have used ev_ref but it had its problems...
// Don't ask me why (maybe i don't get how this actually should work, and this is only a workaround),
// but if we rely on ev_ref to increase the event loop reference counter to keep it alive without this periodic timer/callback,
// the loop will ignore the io_collect_interval. Moreover, it will make the polling backend in use (like most frequently epoll) wait the maximum
// time (MAX_BLOCKTIME in ev.c, circa 60 seconds) to collect incoming data, only then the callback will be called. So each batch of packets
// would be processed every 60 seconds...
struct ev_periodic periodic_check;
ev_periodic_init(&periodic_check, periodic_cb, 0, 5e-3, nullptr);
ev_periodic_start(m_eventLoop, &periodic_check);

ev_run(m_eventLoop, 0);

#ifdef _DEBUG
g_Log.EventDebug("Networking: Libev. Event loop STOPPED.\n");
#endif
}

void LinuxEv::waitForClose()
Expand All @@ -96,6 +138,13 @@ void LinuxEv::registerClient(CNetState * state, EventsID eventCheck)

memset(state->iocb(), 0, sizeof(ev_io));

// Right now we support only async writing to the socket.
// Pure async read would mean to call functions and access data typically managed by the main thread,
// but the core isn't designed for such usage, nor we have all the thread synchronization methods for every possible stuff we might need.
// A fair compromise TODO would be to async read incoming data, parse that in packets that will be stored in a buffer periodically accessed and processed
// by the main thread.
ASSERT(0 == (eventCheck & ~EV_WRITE));

#ifdef _WIN32
int fd = EV_WIN32_HANDLE_TO_FD(state->m_socket.GetSocket());
ev_io_init(state->iocb(), socketslave_cb, fd, (int)eventCheck);
Expand Down
20 changes: 7 additions & 13 deletions src/network/linuxev.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@
#include "../../lib/libev/src/ev.h"
#include "../common/sphere_library/smutex.h"
#include "../sphere/threads.h"

#ifdef _BSD
#define EV_BACKEND_LIST (EVBACKEND_SELECT | EVBACKEND_POLL | EVBACKEND_KQUEUE)
#else
#define EV_BACKEND_LIST (EVBACKEND_SELECT | EVBACKEND_POLL | EVBACKEND_EPOLL)
#endif

class CClient;
class CNetState;
Expand All @@ -36,25 +30,25 @@

private:
struct ev_loop * m_eventLoop;
struct ev_io m_watchMainsock;
// struct ev_io m_watchMainsock; // Watcher for Sphere's socket, to accept incoming connections (async read).

public:
LinuxEv(void);
virtual ~LinuxEv(void);

private:
LinuxEv(const LinuxEv& copy);
LinuxEv& operator=(const LinuxEv& other);
LinuxEv(const LinuxEv& copy) = delete;
LinuxEv& operator=(const LinuxEv& other) = delete;

public:
virtual void onStart();
virtual void tick();
virtual void waitForClose();
virtual void onStart() override;
virtual void tick() override;
virtual void waitForClose() override;

private:
void forceClientevent(CNetState *, EventsID);

public:
void printInitInfo();
void forceClientread(CNetState *);
void forceClientwrite(CNetState *);
// --------------------------------------
Expand Down

0 comments on commit 42ae1c9

Please sign in to comment.