1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-30 08:07:46 +00:00
This commit is contained in:
Mordred 2017-09-25 17:14:01 +02:00
commit 14c8d674cc
49 changed files with 652 additions and 238 deletions

2
.gitignore vendored
View file

@ -97,3 +97,5 @@ src/libraries/external/boost_*
*.iobj
*.filters
# sapphire version
src/servers/Server_Common/Version\.cpp

View file

@ -31,6 +31,14 @@ include( "cmake/boost.cmake" )
include( "cmake/mysql.cmake" )
include( "cmake/compiler.cmake" )
##############################
# Git #
##############################
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(VERSION --tags --dirty=-d)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/servers/Server_Common/Version.cpp" @ONLY)
##########################################################################
# Common include folders
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/libraries/external/ChaiScript-6.0.0/include/")

View file

@ -1,40 +1,42 @@
# Sapphire - FINAL FANTASY XIV Server Emulator
[![Discord Server](https://img.shields.io/badge/discord-Sapphire-7289DA.svg)](https://discord.gg/KfrZCkx)
[![Build Status](https://travis-ci.org/SapphireMordred/Sapphire.svg?branch=master)](https://travis-ci.org/SapphireMordred/Sapphire)
[![Linux Build Status](https://travis-ci.org/SapphireMordred/Sapphire.svg?branch=master)](https://travis-ci.org/SapphireMordred/Sapphire)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/lil7lxa3ty165emm?svg=true)](https://ci.appveyor.com/project/SapphireMordred/Sapphire)
![FFXIV Sapphire](http://i.imgur.com/I4bj1tR.png)
Sapphire is a FINAL FANTASY XIV 4.0+ Server Emulator currently in development.
## Features
* Fully working base world servers, including character creation, chat, player interaction, etc.
* Basic action/battle system
* Teleports, discovery
* NPCs, enemies
* Support for most Quest types(instanced content planned, to be added later)
* Content scripting via ChaiScript
* Retail GM commands working
Sapphire is a **research project** to learn how retail servers work and currently not production code; at this time it is **insecure** (use throwaway passwords for accounts) and you should expect a lot of things unimplemented or broken (of course contributions are always welcome).
## Dependencies + Compiling
Sapphire requires the following software:
| *Name* | *Windows* | *Linux* |
| ------ | --------- | ------- |
| CMake 2.6+ and C++14 capable compiler | [Visual Studio 2017](https://www.visualstudio.com/) | Your favorite C++14 capable compiler |
| CMake 2.6+ and C++14 capable compiler | [Visual Studio 2017](https://www.visualstudio.com/) | `gcc 4.9` and `g++ 4.9` or newer |
| Boost 1.63.0 | [Win32 precompiled binaries](https://sourceforge.net/projects/boost/files/boost-binaries/1.63.0/boost_1_63_0-msvc-14.0-32.exe/download) | Boost libraries from your distribution's package manager |
| MySQL Server 5.7 | [Official Site](https://dev.mysql.com/downloads/mysql/) | MySQL server from your distribution's package manager |
| C# Compiler(used for various tools) | [Visual Studio 2017](https://www.visualstudio.com/) \| [Mono](http://www.mono-project.com/) | [Mono](http://www.mono-project.com/) from your distribution's package manager |
**Windows**
Set the environment variables ``BOOST_ROOT_DIR`` and ``BOOST_LIB_DIR`` to ``[boost main folder]`` and ``[boost main folder]/lib32-msvc-14.0`` respectively **or** copy your boost installation into the project's ``src/lib`` folder.
Visual Studio 2017 users: open `Visual Studio Installer` and ensure `Individual Components > SDKs, libraries, and frameworks > Windows Universal C Runtime` is installed. You may also need to install `Individual Components > Compilers, build tools, and runtimes > VC++ 2015.3 v140 toolset for desktop (x86, 64)`.
To enable [Edit and Continue](https://msdn.microsoft.com/en-us/library/esaeyddf.aspx) ensure you define `-DCMAKE_BUILD_TYPE="Debug"` when generating with CMake.
If you installed CMake outside of Visual Studio and have it in your `PATH` environment variable, navigate to Sapphire folder and `cmake -DCMAKE_BUILD_TYPE="Debug" && cmake --build .`.
Otherwise:
* In *Visual Studio 2017*: Open the project via ``File`` > ``Open`` > ``Folder`` and wait, till CMake automatically finishes cache generation. Right click any CMakeLists.txt and select build to build a application.
To configure debug startup parameters, select ``Debug and Launch settings``, the application you want to set up parameters for, and add a ``args`` json array containing your launch parameters in ``configurations``.
If Visual Studio fails to generate a CMake Cache or does not show options to build, make sure that the newest version of it is installed and all environment variables are set correctly. Keep in mind that generating a cache can take a while on some machines.
* In *Visual Studio 2015*: Generate a Visual Studio project via CMake: ``cmake -G "Visual Studio 14 2015"`` and open it as a normal solution in Visual Studio.
The solution `Sapphire.sln` is also generated in the main Sapphire folder.
**Linux**
Generate a cache with CMake and use your favorite C++14 capable compiler to compile those wonderful lines of code into binaries with ``make``.
Sapphire is **not** currently configured to compile in 64bit. Make sure that all libraries have their ``:i386`` versions installed.
`cmake . -DSAPPHIRE_BOOST_VER="your.boost.ver" && make -j JOBS` where `JOBS` is number of cores on your system.
`-j JOBS` can be omitted if your system lacks resources (spoiler: your system probably lacks resources if running on 1GB RAM or lower, or if the build fails due to being out of memory)
## Links

View file

@ -0,0 +1,168 @@
# - Returns a version string from Git
#
# These functions force a re-configure on each git commit so that you can
# trust the values of the variables in your build system.
#
# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
#
# Returns the refspec and sha hash of the current head revision
#
# git_describe(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe on the source tree, and adjusting
# the output so that it tests false if an error occurs.
#
# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
#
# Returns the results of git describe --exact-match on the source tree,
# and adjusting the output so that it tests false if there was no exact
# matching tag.
#
# git_local_changes(<var>)
#
# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes.
# Uses the return code of "git diff-index --quiet HEAD --".
# Does not regard untracked files.
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
if(__get_git_revision_description)
return()
endif()
set(__get_git_revision_description YES)
# We must run the following at "include" time, not at function call time,
# to find the path to this module rather than the path to a calling list file
get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
function(get_git_head_revision _refspecvar _hashvar)
set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
# We have reached the root directory, we are not in git
set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
return()
endif()
set(GIT_DIR "${GIT_PARENT_DIR}/.git")
endwhile()
# check if this is a submodule
if(NOT IS_DIRECTORY ${GIT_DIR})
file(READ ${GIT_DIR} submodule)
string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
endif()
set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
if(NOT EXISTS "${GIT_DATA}")
file(MAKE_DIRECTORY "${GIT_DATA}")
endif()
if(NOT EXISTS "${GIT_DIR}/HEAD")
return()
endif()
set(HEAD_FILE "${GIT_DATA}/HEAD")
configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
"${GIT_DATA}/grabRef.cmake"
@ONLY)
include("${GIT_DATA}/grabRef.cmake")
set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
endfunction()
function(git_describe _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
# TODO sanitize
#if((${ARGN}" MATCHES "&&") OR
# (ARGN MATCHES "||") OR
# (ARGN MATCHES "\\;"))
# message("Please report the following error to the project!")
# message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
#endif()
#message(STATUS "Arguments to execute_process: ${ARGN}")
execute_process(COMMAND
"${GIT_EXECUTABLE}"
describe
${hash}
${ARGN}
WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT res EQUAL 0)
set(out "${out}-${res}-NOTFOUND")
endif()
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_get_exact_tag _var)
git_describe(out --exact-match ${ARGN})
set(${_var} "${out}" PARENT_SCOPE)
endfunction()
function(git_local_changes _var)
if(NOT GIT_FOUND)
find_package(Git QUIET)
endif()
get_git_head_revision(refspec hash)
if(NOT GIT_FOUND)
set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
return()
endif()
if(NOT hash)
set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
return()
endif()
execute_process(COMMAND
"${GIT_EXECUTABLE}"
diff-index --quiet HEAD --
WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE
res
OUTPUT_VARIABLE
out
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(res EQUAL 0)
set(${_var} "CLEAN" PARENT_SCOPE)
else()
set(${_var} "DIRTY" PARENT_SCOPE)
endif()
endfunction()

View file

@ -0,0 +1,41 @@
#
# Internal file for GetGitRevisionDescription.cmake
#
# Requires CMake 2.6 or newer (uses the 'function' command)
#
# Original Author:
# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net>
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2009-2010.
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
set(HEAD_HASH)
file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
if(HEAD_CONTENTS MATCHES "ref")
# named branch
string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
if(EXISTS "@GIT_DIR@/${HEAD_REF}")
configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
else()
configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
set(HEAD_HASH "${CMAKE_MATCH_1}")
endif()
endif()
else()
# detached HEAD
configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
endif()
if(NOT HEAD_HASH)
file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
string(STRIP "${HEAD_HASH}" HEAD_HASH)
endif()

View file

@ -11,7 +11,7 @@ class skillDef_121Def
def onFinish( player, target )
{
player.handleScriptSkill( STD_DAMAGE, 121, 50, 0, target );
target.addStatusEffectByIdIfNotExist(143, 20000, 0);
target.addStatusEffectByIdIfNotExist(143, 20000, player, 0);
}
};

View file

@ -0,0 +1,18 @@
// Skill Name: Medica
// Skill ID: 124
class skillDef_124Def
{
def skillDef_124Def()
{
}
def onFinish( player, target )
{
player.handleScriptSkill( STD_HEAL, 124, 300, 0, player );
}
};
GLOBAL skillDef_124 = skillDef_124Def();

View file

@ -10,7 +10,7 @@ class skillDef_125Def
def onFinish( player, target )
{
target.addStatusEffectByIdIfNotExist( 148, 60000, 0 );
target.addStatusEffectByIdIfNotExist( 148, 60000, player, 0 );
}
};

View file

@ -10,7 +10,7 @@ class skillDef_128Def
def onFinish( player, target )
{
target.addStatusEffectByIdIfNotExist(3, 30000, 0);
target.addStatusEffectByIdIfNotExist( 3, 30000, player, 0 );
}
};

View file

@ -11,7 +11,7 @@ class skillDef_132Def
def onFinish( player, target )
{
player.handleScriptSkill( STD_DAMAGE, 132, 50, 0, target );
target.addStatusEffectByIdIfNotExist( 143, 20000, 0 );
target.addStatusEffectByIdIfNotExist( 143, 20000, player, 0 );
}
};

View file

@ -0,0 +1,19 @@
// Skill Name: Medica II
// Skill ID: 133
class skillDef_133Def
{
def skillDef_133Def()
{
}
def onFinish( player, target )
{
player.handleScriptSkill( STD_HEAL, 133, 200, 0, player );
target.addStatusEffectByIdIfNotExist( 150, 30000, player, 50 );
}
};
GLOBAL skillDef_133 = skillDef_133Def();

View file

@ -10,7 +10,7 @@ class skillDef_3Def
def onFinish( player, target )
{
player.addStatusEffectById(50, 20000, 30);
player.addStatusEffectById(50, 20000, player, 30);
}
};

View file

@ -23,7 +23,7 @@ CREATE TABLE IF NOT EXISTS `zonepositions` (
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
-- Dumping data for table sapphire.zonepositions: 157 rows
-- Dumping data for table sapphire.zonepositions: 186 rows
DELETE FROM `zonepositions`;
/*!40000 ALTER TABLE `zonepositions` DISABLE KEYS */;
INSERT INTO `zonepositions` (`id`, `target_zone_id`, `pos_x`, `pos_y`, `pos_z`, `pos_o`, `radius`) VALUES
@ -192,7 +192,27 @@ INSERT INTO `zonepositions` (`id`, `target_zone_id`, `pos_x`, `pos_y`, `pos_z`,
(4203092, 129, -213.582, 16, 48.801, -0.006, 2),
(6390356, 128, -11.685, 91.499, -13.191, -0.377, 2),
(4170499, 134, -39.562, 36.039, 152.972, 2.041, 2),
(4170784, 135, -39.271, 71.504, 116.516, 1.587, 2);
(4170784, 135, -39.271, 71.504, 116.516, 1.587, 2),
(6100648, 478, -74.827, 209.524, -23.346, 2.854, 2),
(5865600, 478, 135.482, 207, 114.076, -2.166, 2),
(5865605, 478, 79.813, 203.98, 132.018, 2.558, 2),
(5865668, 399, -221.2, 104.271, -599.535, 0.268, 2),
(5865672, 399, -221.2, 155.809, -516.036, 0.295, 2),
(5865598, 399, -221.2, 104.271, -599.535, 0.268, 2),
(5865604, 399, -533.153, 153.074, -487.968, 0.18, 2),
(5916706, 418, -111.521, 15.14, -29.188, 0.0077, 2),
(5916705, 418, 47.713, 23.979, 1.144, 1.457, 2),
(5916704, 418, 47.701, -12.02, 67.738, 2.057, 2),
(5916727, 155, -161.481, 304.153, -321.403, 0.795, 2),
(5916708, 419, 0.000456, 16.015, -35.806, -0.0296, 2),
(5916724, 419, 80.156, 10.054, -123.9, -2.445, 2),
(5916722, 419, -80.517, 10.054, -123.315, 2.468, 2),
(5916717, 419, -136.889, -12.634, -16.757, 0.978, 2),
(5916716, 419, 136.079, -9.234, -66.426, -0.989, 2),
(6905273, 612, 475.811, 61.576, -555.551, -1.274, 2),
(6905297, 620, -653.588, 51.867, -790.168, 1.1366, 2),
(6906489, 635, -82.295, 0, 8.925, 1.77, 2),
(6906492, 635, 100.312, 2.731, -113.366, -0.481, 2);
/*!40000 ALTER TABLE `zonepositions` ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;

View file

@ -424,15 +424,6 @@ namespace Core {
Land = 0x200,
};
enum UserLevel : uint8_t
{
all = 0xff,
player = 0x01,
gm = 0x02,
dev = 0x04,
admin = 0x08
};
struct QuestActive
{
QuestActive()
@ -571,6 +562,35 @@ namespace Core {
LimitBreak = 8,
};
enum ActionEffectType : uint8_t
{
Nothing = 0,
Miss = 1,
FullResist = 2,
Damage = 3,
Heal = 4,
BlockedDamage = 5,
ParriedDamage = 6,
Invulnerable = 7,
NoEffectText = 8,
Unknown_0 = 9,
MpLoss = 10,
MpGain = 11,
TpLoss = 12,
TpGain = 13,
GpGain = 14
};
enum ActionHitSeverityType : uint8_t
{
NormalDamage = 0,
CritHeal = 0,
CritDamage = 1,
NormalHeal = 1,
DirectHitDamage = 2,
CritDirectHitDamage = 3
};
enum HandleActionType : uint8_t
{
Event,
@ -743,7 +763,7 @@ namespace Core {
Flee = 0x1B,
Unk3 = 0x20,
Unk3 = 0x20, // Animation related?
CombatIndicationShow = 0x22,
@ -769,7 +789,7 @@ namespace Core {
ItemRepairMsg = 0x5C,
LeveStartAnim = 0x66,
Unk4 = 0x67,
LeveStartError = 0x67,
PlayerNameGrayout = 0x6A,
ItemObtainMsg = 0x75,
@ -869,7 +889,7 @@ namespace Core {
AchievementPopup = 0x203,
Unk7 = 0x205,
Unk7 = 0x205, // LogMessage?
AchievementMsg = 0x206,
SetItemLevel = 0x209,

View file

@ -34,6 +34,25 @@ namespace Db {
return static_cast< T >( atol( m_pValue ) );
}
uint64_t getUInt64()
{
if( m_pValue )
{
#ifdef _WIN32
uint64_t value;
sscanf( m_pValue, "%I64d", &value );
return value;
#else
uint64_t value;
sscanf( m_pValue, "%Lu", &value );
return value;
#endif
}
else
return 0;
}
uint32_t getLength() const;
private:

View file

@ -315,8 +315,7 @@ bool Core::Data::ExdData::loadActionInfo()
for( auto row : rows )
{
auto& fields = row.second;
ActionInfo info{ 0 };
auto info = boost::make_shared< ActionInfo >();
uint32_t id = row.first;
if( id == 0 )
@ -360,36 +359,36 @@ bool Core::Data::ExdData::loadActionInfo()
info.id = id;
info.name = name;
info.category = category;
info->id = id;
info->name = name;
info->category = category;
info.class_job = class_job;
info.unlock_level = unlock_level;
info.range = range;
info.can_target_self = can_target_self;
info.can_target_party = can_target_party;
info.can_target_friendly = can_target_friendly;
info.can_target_enemy = can_target_enemy;
info->class_job = class_job;
info->unlock_level = unlock_level;
info->range = range;
info->can_target_self = can_target_self;
info->can_target_party = can_target_party;
info->can_target_friendly = can_target_friendly;
info->can_target_enemy = can_target_enemy;
info.can_target_ko = can_target_ko;
info->can_target_ko = can_target_ko;
info.is_aoe = is_aoe;
info->is_aoe = is_aoe;
info.aoe_type = aoe_type;
info.radius = radius;
info->aoe_type = aoe_type;
info->radius = radius;
info.points_type = points_type;
info.points_cost = points_cost;
info->points_type = points_type;
info->points_cost = points_cost;
info.is_instant = is_instant;
info.cast_time = cast_time * 100;
info.recast_time = recast_time * 100;
info->is_instant = is_instant;
info->cast_time = cast_time * 100;
info->recast_time = recast_time * 100;
info.model = model;
info.aspect = aspect;
info->model = model;
info->aspect = aspect;
m_actionInfoMap[id] = info;
m_actionInfoMap.emplace( std::make_pair( info->id, info ) );
}
@ -464,6 +463,22 @@ boost::shared_ptr< Core::Data::AetheryteInfo >
}
boost::shared_ptr< Core::Data::ActionInfo >
Core::Data::ExdData::getActionInfo( uint32_t actionId )
{
try
{
return m_actionInfoMap[actionId];
}
catch ( ... )
{
return nullptr;
}
return nullptr;
}
boost::shared_ptr< Core::Data::CustomTalkInfo >
Core::Data::ExdData::getCustomTalkInfo( uint32_t customTalkId )
{

View file

@ -299,7 +299,7 @@ namespace Core {
std::map<uint8_t, ClassJobInfo> m_classJobInfoMap;
std::map<uint32_t, ParamGrowthInfo> m_paramGrowthInfoMap;
std::map<uint16_t, EventActionInfo> m_EventActionInfoMap;
std::map<uint16_t, ActionInfo> m_actionInfoMap;
std::map<uint16_t, boost::shared_ptr< ActionInfo > > m_actionInfoMap;
std::map<uint16_t, StatusEffectInfo> m_statusEffectInfoMap;
std::map<uint32_t, boost::shared_ptr< AetheryteInfo > > m_aetheryteInfoMap;
std::map<uint32_t, TribeInfo > m_tribeInfoMap;
@ -317,6 +317,7 @@ namespace Core {
boost::shared_ptr< OpeningInfo > getOpeningInfo( uint32_t openingId );
boost::shared_ptr< CustomTalkInfo > getCustomTalkInfo( uint32_t customTalkId );
boost::shared_ptr< AetheryteInfo > getAetheryteInfo( uint32_t aetheryteId );
boost::shared_ptr< ActionInfo > getActionInfo( uint32_t actionId );
boost::shared_ptr< PlaceNameInfo > getPlaceNameInfo( uint32_t placeNameId );
boost::shared_ptr< ItemInfo > getItemInfo( uint32_t catalogId );
boost::shared_ptr< RaceInfo > getRaceInfo( uint32_t raceId );

View file

@ -116,6 +116,7 @@ namespace Packets {
WeatherChange = 0x01AF, // updated for sb
Discovery = 0x01B2, // updated for sb
EorzeaTimeOffset = 0x01B4,
CFAvailableContents = 0x01CF,

View file

@ -301,12 +301,12 @@ struct FFXIVIpcUpdateHpMpTp : FFXIVIpcBasePacket<UpdateHpMpTp>
*/
struct effectEntry
{
uint8_t unknown_1;
uint8_t unknown_2;
Common::ActionEffectType effectType;
Common::ActionHitSeverityType hitSeverity;
uint8_t unknown_3;
int8_t bonusPercent;
int16_t param1;
uint8_t unknown_5;
int16_t value;
uint8_t valueMultiplier; // This multiplies whatever value is in the 'value' param by 10. Possibly a workaround for big numbers
uint8_t unknown_6;
};
@ -1281,6 +1281,11 @@ struct FFXIVIpcCFMemberStatus : FFXIVIpcBasePacket<CFMemberStatus>
uint32_t unknown3;
};
struct FFXIVIpcEorzeaTimeOffset : FFXIVIpcBasePacket<EorzeaTimeOffset>
{
uint64_t timestamp;
};
} /* Server */

View file

@ -0,0 +1,10 @@
#include "Version.h"
namespace Core {
namespace Version {
const std::string GIT_HASH = "@GIT_SHA1@";
const std::string VERSION = "@VERSION@";
} /* Version */
} /* Core */

View file

@ -0,0 +1,15 @@
#ifndef _VERSION_H
#define _VERSION_H
#include <string>
namespace Core {
namespace Version {
extern const std::string GIT_HASH;
extern const std::string VERSION;
} /* Version */
} /* Core */
#endif

View file

@ -70,7 +70,7 @@ namespace Core {
setBirthDay( field[2].get< int8_t >(), field[3].get< int8_t >() );
m_guardianDeity = field[4].get< int8_t >();
m_class = field[5].get< int8_t >();
m_contentId = field[7].get< uint64_t >();
m_contentId = field[7].getUInt64();
m_zoneId = field[8].get< uint16_t >();
auto pQR2 = g_database.query( "SELECT * FROM characlass WHERE CharacterId = " + std::to_string( charId ) + ";" );

View file

@ -30,7 +30,7 @@ namespace Core {
std::string getClassString();
// return the id of the actor
uint32_t getId()
uint32_t getId() const
{
return m_iD;
}
@ -45,13 +45,13 @@ namespace Core {
m_contentId = id;
}
uint64_t getContentId()
uint64_t getContentId() const
{
return m_contentId;
}
uint32_t getAccountId()
uint32_t getAccountId() const
{
return m_accountId;
}
@ -84,12 +84,12 @@ namespace Core {
m_class = classId;
}
uint8_t getClass()
uint8_t getClass() const
{
return m_class;
}
uint8_t getGuardianDeity()
uint8_t getGuardianDeity() const
{
return m_guardianDeity;
}
@ -105,17 +105,17 @@ namespace Core {
m_birthMonth = month;
}
uint8_t getBirthDay()
uint8_t getBirthDay() const
{
return m_birthDay;
}
uint8_t getBirthMonth()
uint8_t getBirthMonth() const
{
return m_birthMonth;
}
uint8_t getVoice()
uint8_t getVoice() const
{
return m_birthMonth;
}
@ -125,12 +125,12 @@ namespace Core {
m_voice = voice;
}
uint32_t getZoneId()
uint32_t getZoneId() const
{
return m_zoneId;
}
uint32_t getTribe()
uint32_t getTribe() const
{
return m_tribe;
}
@ -140,9 +140,9 @@ namespace Core {
m_tribe = tribe;
}
uint8_t getGmRank()
uint8_t getGmRank() const
{
return m_birthMonth;
return m_gmRank;
}
void setGmRank( uint8_t rank )

View file

@ -277,7 +277,7 @@ uint64_t Core::Network::SapphireAPI::getNextContentId()
return 0x0040000001000001;
}
contentId = pQR->fetch()[0].get< uint64_t >() + 1;
contentId = pQR->fetch()[0].getUInt64() + 1;
if( contentId < 0x0040000001000001 )
{
return 0x0040000001000001;

View file

@ -8,6 +8,7 @@
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket142.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket143.h"
#include "src/servers/Server_Zone/Network/PacketWrappers/ActorControlPacket144.h"
#include "src/servers/Server_Zone/Actor/Player.h"
#include "src/servers/Server_Zone/Script/ScriptManager.h"
@ -30,7 +31,7 @@ Core::Action::ActionCast::ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr
m_startTime = 0;
m_id = actionId;
m_handleActionType = HandleActionType::Spell;
m_castTime = g_exdData.m_actionInfoMap[actionId].cast_time; // TODO: Add security checks.
m_castTime = g_exdData.getActionInfo( actionId )->cast_time; // TODO: Add security checks.
m_pSource = pActor;
m_pTarget = pTarget;
m_bInterrupt = false;
@ -53,10 +54,11 @@ void Core::Action::ActionCast::onStart()
castPacket.data().action_id = m_id;
castPacket.data().unknown = 1;
castPacket.data().cast_time = m_castTime / 1000; // This is used for the cast bar above the target bar of the caster.
castPacket.data().unknown_1 = m_id;
castPacket.data().cast_time = static_cast< float >( m_castTime / 1000 ); // This is used for the cast bar above the target bar of the caster.
castPacket.data().target_id = m_pTarget->getId();
m_pSource->sendToInRangeSet( castPacket, false );
m_pSource->sendToInRangeSet( castPacket, true );
m_pSource->getAsPlayer()->setStateFlag( PlayerStateFlag::Casting );
m_pSource->getAsPlayer()->sendStateFlags();
@ -73,6 +75,10 @@ void Core::Action::ActionCast::onFinish()
pPlayer->unsetStateFlag( PlayerStateFlag::Casting );
pPlayer->sendStateFlags();
/*auto control = ActorControlPacket143( m_pTarget->getId(), ActorControlType::Unk7,
0x219, m_id, m_id, m_id, m_id );
m_pSource->sendToInRangeSet( control, true );*/
g_scriptMgr.onCastFinish( pPlayer, m_pTarget, m_id );
}
@ -86,7 +92,7 @@ void Core::Action::ActionCast::onInterrupt()
m_pSource->getAsPlayer()->sendStateFlags();
auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt,
0x219, 0x04, m_id, 1 );
0x219, 1, m_id, 1 );
m_pSource->sendToInRangeSet( control, true );
}

View file

@ -26,7 +26,7 @@ Core::Action::ActionTeleport::ActionTeleport( Entity::ActorPtr pActor, uint16_t
m_startTime = 0;
m_id = 5;
m_handleActionType = HandleActionType::Teleport;
m_castTime = g_exdData.m_actionInfoMap[5].cast_time; // TODO: Add security checks.
m_castTime = g_exdData.getActionInfo(5)->cast_time; // TODO: Add security checks.
m_pSource = pActor;
m_bInterrupt = false;
m_targetAetheryte = targetZone;

View file

@ -595,7 +595,7 @@ void Core::Entity::Actor::autoAttack( ActorPtr pTarget )
srand( static_cast< uint32_t >( tick ) );
uint32_t damage = 10 + rand() % 12;
uint32_t variation = 0 + rand() % 3;
uint32_t variation = 0 + rand() % 4;
GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() );
effectPacket.data().targetId = pTarget->getId();
@ -606,9 +606,9 @@ void Core::Entity::Actor::autoAttack( ActorPtr pTarget )
effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
effectPacket.data().effectTarget = pTarget->getId();
effectPacket.data().effects[0].param1 = damage;
effectPacket.data().effects[0].unknown_1 = 3;
effectPacket.data().effects[0].unknown_2 = 1;
effectPacket.data().effects[0].value = damage;
effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = static_cast< ActionHitSeverityType >( variation );
effectPacket.data().effects[0].unknown_3 = 7;
sendToInRangeSet( effectPacket );
@ -627,26 +627,26 @@ void Core::Entity::Actor::addStatusEffect( StatusEffect::StatusEffectPtr pEffect
}
/*! \param StatusEffectPtr to be applied to the actor */
void Core::Entity::Actor::addStatusEffectById( int32_t id, int32_t duration, uint16_t param )
void Core::Entity::Actor::addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param )
{
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, shared_from_this(), shared_from_this(), duration, 3000 ) );
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(), shared_from_this(), duration, 3000 ) );
effect->setParam( param );
addStatusEffect( effect );
}
/*! \param StatusEffectPtr to be applied to the actor */
void Core::Entity::Actor::addStatusEffectByIdIfNotExist( int32_t id, int32_t duration, uint16_t param )
void Core::Entity::Actor::addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param )
{
if( !m_pStatusEffectContainer->hasStatusEffect( id ) )
{
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, shared_from_this(), shared_from_this(), duration, 3000 ) );
StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pSource.shared_from_this(), shared_from_this(), duration, 3000 ) );
effect->setParam( param );
addStatusEffect( effect );
}
}
/*! \param Status that should be removed, based on its ID. */
void Core::Entity::Actor::removeSingleStatusEffectFromId( int32_t id )
void Core::Entity::Actor::removeSingleStatusEffectFromId( uint32_t id )
{
m_pStatusEffectContainer->removeSingleStatusEffectFromId( id );
}

View file

@ -293,13 +293,13 @@ public:
void addStatusEffect( StatusEffect::StatusEffectPtr pEffect );
// add a status effect by id
void addStatusEffectById( int32_t id, int32_t duration, uint16_t param = 0 );
void addStatusEffectById( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// add a status effect by id if it doesn't exist
void addStatusEffectByIdIfNotExist( int32_t id, int32_t duration, uint16_t param = 0 );
void addStatusEffectByIdIfNotExist( uint32_t id, int32_t duration, Entity::Actor& pSource, uint16_t param = 0 );
// remove a status effect by id
void removeSingleStatusEffectFromId( int32_t id );
void removeSingleStatusEffectFromId( uint32_t id );
//get the status effect container
StatusEffect::StatusEffectContainerPtr getStatusEffectContainer() const;

View file

@ -425,7 +425,7 @@ void Core::Entity::BattleNpc::onDeath()
// todo: check for companion
if( pHateEntry->m_pActor->isPlayer() ) // && pHateEntry->m_hateAmount >= plsBeHatedThisMuchAtLeast )
{
auto level = pHateEntry->m_pActor->getLevel();
uint8_t level = pHateEntry->m_pActor->getLevel();
auto levelDiff = (int)this->m_level - (int)level;
auto cappedLevelDiff = Math::Util::clamp( levelDiff, 1, 6 );

View file

@ -29,7 +29,7 @@ extern Core::Data::ExdData g_exdData;
// 3 Versions. SB and HW are linear, ARR is polynomial.
// Originally from Player.cpp, calculateStats().
uint32_t CalcBattle::calculateBaseStat( PlayerPtr pPlayer )
float CalcBattle::calculateBaseStat( PlayerPtr pPlayer )
{
float base = 0.0f;
uint8_t level = pPlayer->getLevel();
@ -37,7 +37,7 @@ uint32_t CalcBattle::calculateBaseStat( PlayerPtr pPlayer )
// SB Base Stat Formula (Aligned)
if ( level > 60 )
{
base = ( ( ( level == 61) ? 224 : 220 ) + ( level - 61 ) * 8);
base = ( ( ( level == 61 ) ? 224 : 220 ) + ( level - 61 ) * 8);
}
// HW Base Stat Formula (Aligned)
else if ( level > 50 )
@ -85,7 +85,7 @@ uint32_t CalcBattle::calculateMaxHp( PlayerPtr pPlayer )
else
approxBaseHp = paramGrowthInfoIt->second.mp_const * 0.7596f;
uint16_t result = floor( jobModHp * ( approxBaseHp / 100.0f ) ) + floor( hpMod / 100.0f * ( vitStat - baseStat ) );
uint16_t result = static_cast< uint16_t >( floor( jobModHp * ( approxBaseHp / 100.0f ) ) + floor( hpMod / 100.0f * ( vitStat - baseStat ) ) );
return result;
}
@ -108,7 +108,7 @@ uint32_t CalcBattle::calculateMaxMp( PlayerPtr pPlayer )
uint16_t jobModMp = classInfoIt->second.mod_mpcpgp;
uint16_t baseMp = paramGrowthInfoIt->second.mp_const;
uint16_t result = floor( floor( piety - baseStat ) * ( pietyScalar / 100 ) + baseMp ) * jobModMp / 100;
uint16_t result = static_cast< uint16_t >( floor( floor( piety - baseStat ) * ( pietyScalar / 100 ) + baseMp ) * jobModMp / 100 );
return result;
}

View file

@ -12,7 +12,7 @@ namespace Entity {
{
public:
static uint32_t calculateBaseStat( PlayerPtr pPlayer );
static float calculateBaseStat( PlayerPtr pPlayer );
static uint32_t calculateMaxMp( PlayerPtr pPlayer );
static uint32_t calculateMaxHp( PlayerPtr pPlayer );
static uint32_t calculateHealValue( PlayerPtr pPlayer, uint32_t potency );

View file

@ -218,12 +218,12 @@ void Core::Entity::Player::calculateStats()
// TODO: put formula somewhere else...
float base = CalcBattle::calculateBaseStat( getAsPlayer() );
m_baseStats.str = base * ( static_cast< float >( classInfo.mod_str ) / 100 ) + tribeInfo.mod_str;
m_baseStats.dex = base * ( static_cast< float >( classInfo.mod_dex ) / 100 ) + tribeInfo.mod_dex;
m_baseStats.vit = base * ( static_cast< float >( classInfo.mod_vit ) / 100 ) + tribeInfo.mod_vit;
m_baseStats.inte = base * ( static_cast< float >( classInfo.mod_int ) / 100 ) + tribeInfo.mod_int;
m_baseStats.mnd = base * ( static_cast< float >( classInfo.mod_mnd ) / 100 ) + tribeInfo.mod_mnd;
m_baseStats.pie = base * ( static_cast< float >( classInfo.mod_pie ) / 100 ) + tribeInfo.mod_pie;
m_baseStats.str = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_str ) / 100 ) + tribeInfo.mod_str );
m_baseStats.dex = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_dex ) / 100 ) + tribeInfo.mod_dex );
m_baseStats.vit = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_vit ) / 100 ) + tribeInfo.mod_vit );
m_baseStats.inte = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_int ) / 100 ) + tribeInfo.mod_int );
m_baseStats.mnd = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_mnd ) / 100 ) + tribeInfo.mod_mnd );
m_baseStats.pie = static_cast< uint32_t >( base * ( static_cast< float >( classInfo.mod_pie ) / 100 ) + tribeInfo.mod_pie );
m_baseStats.skillSpeed = paramGrowthInfo.base_secondary;
m_baseStats.spellSpeed = paramGrowthInfo.base_secondary;
@ -243,7 +243,7 @@ void Core::Entity::Player::calculateStats()
m_hp = m_baseStats.max_hp;
m_baseStats.determination = base;
m_baseStats.determination = static_cast< uint32_t >( base );
}
@ -513,7 +513,7 @@ bool Core::Entity::Player::isAetheryteRegistered( uint8_t aetheryteId ) const
uint8_t value;
Util::valueToFlagByteIndexValue( aetheryteId, value, index );
return m_aetheryte[index] & value;
return ( m_aetheryte[index] & value ) != 0;
}
uint8_t * Core::Entity::Player::getDiscoveryBitmask()
@ -608,7 +608,7 @@ bool Core::Entity::Player::isActionLearned( uint8_t actionId ) const
uint8_t value;
Util::valueToFlagByteIndexValue( actionId, value, index );
return m_unlocks[index] & value;
return ( m_unlocks[index] & value ) != 0;
}
void Core::Entity::Player::gainExp( uint32_t amount )
@ -980,10 +980,11 @@ const uint8_t * Core::Entity::Player::getStateFlags() const
bool Core::Entity::Player::actionHasCastTime( uint32_t actionId ) //TODO: Add logic for special cases
{
if( g_exdData.m_actionInfoMap[actionId].is_instant )
auto actionInfoPtr = g_exdData.getActionInfo( actionId );
if( actionInfoPtr->is_instant )
return false;
if( g_exdData.m_actionInfoMap[actionId].cast_time == 0 )
if( actionInfoPtr->cast_time == 0 )
return false;
return true;
@ -997,7 +998,7 @@ bool Core::Entity::Player::hasStateFlag( Core::Common::PlayerStateFlag flag ) co
uint8_t value;
Util::valueToFlagByteIndexValue( iFlag, value, index );
return m_stateFlags[index] & value;
return ( m_stateFlags[index] & value ) != 0;
}
void Core::Entity::Player::setStateFlag( Core::Common::PlayerStateFlag flag )
@ -1463,7 +1464,7 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget )
//uint64_t tick = Util::getTimeMs();
//srand(static_cast< uint32_t >(tick));
uint32_t damage = mainWeap->getAutoAttackDmg();
uint32_t damage = static_cast< uint32_t >( mainWeap->getAutoAttackDmg() );
uint32_t variation = 0 + rand() % 3;
if ( getClass() == JOB_MACHINIST ||
@ -1481,9 +1482,9 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget )
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation());
effectPacket.data().effectTargetId = pTarget->getId();
effectPacket.data().effectTarget = pTarget->getId();
effectPacket.data().effects[0].param1 = damage;
effectPacket.data().effects[0].unknown_1 = 3;
effectPacket.data().effects[0].unknown_2 = 1;
effectPacket.data().effects[0].value = damage;
effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 7;
sendToInRangeSet(effectPacket, true);
@ -1501,9 +1502,9 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget )
effectPacket.data().actionTextId = 7;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation());
effectPacket.data().effectTarget = pTarget->getId();
effectPacket.data().effects[0].param1 = damage;
effectPacket.data().effects[0].unknown_1 = 3;
effectPacket.data().effects[0].unknown_2 = 2;
effectPacket.data().effects[0].value = damage;
effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 71;
sendToInRangeSet(effectPacket, true);
@ -1518,6 +1519,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId,
sendDebug( std::to_string( pTarget.getId() ) );
sendDebug( "Handle script skill type: " + std::to_string( type ) );
auto actionInfoPtr = g_exdData.getActionInfo( actionId );
switch( type )
{
@ -1534,9 +1538,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId,
effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
effectPacket.data().effectTarget = pTarget.getId();
effectPacket.data().effects[0].param1 = param1;
effectPacket.data().effects[0].unknown_1 = 3;
effectPacket.data().effects[0].unknown_2 = 1;
effectPacket.data().effects[0].value = static_cast< int16_t >( param1 );
effectPacket.data().effects[0].effectType = ActionEffectType::Damage;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage;
effectPacket.data().effects[0].unknown_3 = 7;
sendToInRangeSet( effectPacket, true );
@ -1544,14 +1548,14 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId,
if ( !pTarget.isAlive() )
break;
pTarget.takeDamage( param1 );
pTarget.takeDamage( static_cast< uint32_t >( param1 ) );
pTarget.onActionHostile( shared_from_this() );
break;
}
case Core::Common::HandleSkillType::StdHeal:
{
uint32_t calculatedHeal = CalcBattle::calculateHealValue( getAsPlayer(), param1 );
uint32_t calculatedHeal = CalcBattle::calculateHealValue( getAsPlayer(), static_cast< uint32_t >( param1 ) );
sendDebug( "STD_HEAL" );
@ -1564,9 +1568,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId,
effectPacket.data().numEffects = 1;
effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() );
effectPacket.data().effectTarget = pTarget.getId();
effectPacket.data().effects[0].param1 = calculatedHeal;
effectPacket.data().effects[0].unknown_1 = 4;
effectPacket.data().effects[0].unknown_2 = 1;
effectPacket.data().effects[0].value = calculatedHeal;
effectPacket.data().effects[0].effectType = ActionEffectType::Heal;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal;
effectPacket.data().effects[0].unknown_3 = 7;
sendToInRangeSet( effectPacket, true );
@ -1574,6 +1578,41 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId,
if ( !pTarget.isAlive() )
break;
// todo: get proper packets: the following was just kind of thrown together from what we know
// also toss AoE to another spot and make it generic
if ( actionInfoPtr->is_aoe )
{
for ( auto pCurAct : m_inRangePlayers )
{
assert( pCurAct );
if ( !pCurAct->isAlive() )
break;
if ( Math::Util::distance( pTarget.getPos().x, pTarget.getPos().y, pTarget.getPos().z, pCurAct->getPos().x, pCurAct->getPos().y, pCurAct->getPos().z ) <= actionInfoPtr->radius )
{
GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( pCurAct->getId() );
effectPacket.data().targetId = pCurAct->getId();
effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work
effectPacket.data().unknown_2 = 1;
effectPacket.data().unknown_8 = 1;
effectPacket.data().unknown_5 = 1;
effectPacket.data().actionAnimationId = actionId;
effectPacket.data().actionTextId = 0;
effectPacket.data().numEffects = 1;
effectPacket.data().effectTarget = pCurAct->getId();
effectPacket.data().effects[0].value = calculatedHeal;
effectPacket.data().effects[0].effectType = ActionEffectType::Heal;
effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal;
effectPacket.data().effects[0].unknown_3 = 7;
pCurAct->sendToInRangeSet( effectPacket, true );
pCurAct->heal( calculatedHeal );
sendDebug( "AoE hit actor " + pCurAct->getName() );
}
}
}
pTarget.heal( calculatedHeal );
break;
}
@ -1629,3 +1668,14 @@ void Core::Entity::Player::setOpeningSequence( uint8_t seq )
setSyncFlag( OpeningSeq );
m_openingSequence = seq;
}
/// Tells client to offset their eorzean time by given timestamp.
void Core::Entity::Player::setEorzeaTimeOffset( uint64_t timestamp )
{
// TODO: maybe change to persistent?
GamePacketNew< FFXIVIpcEorzeaTimeOffset, ServerZoneIpcType > packet ( getId() );
packet.data().timestamp = timestamp;
// Send to single player
queuePacket( packet );
}

View file

@ -499,6 +499,10 @@ public:
uint32_t getCFPenaltyMinutes() const;
void setCFPenaltyMinutes( uint32_t minutes );
void setEorzeaTimeOffset( uint64_t timestamp );
private:
uint32_t m_lastWrite;
uint32_t m_lastPing;

View file

@ -264,14 +264,14 @@ void Core::Entity::Player::onTick()
if( !isAlive() || !isLoadingComplete() )
return;
int32_t addHp = getMaxHp() * 0.1f + 1;
int32_t addMp = getMaxMp() * 0.06f + 1;
int32_t addTp = 100;
uint32_t addHp = static_cast< uint32_t >( getMaxHp() * 0.1f + 1 );
uint32_t addMp = static_cast< uint32_t >( getMaxMp() * 0.06f + 1 );
uint32_t addTp = 100;
if( !m_actorIdTohateSlotMap.empty() )
{
addHp = getMaxHp() * 0.01f + 1;
addMp = getMaxHp() * 0.02f + 1;
addHp = static_cast< uint32_t >( getMaxHp() * 0.01f + 1 );
addMp = static_cast< uint32_t >( getMaxMp() * 0.02f + 1 );
addTp = 60;
}

View file

@ -74,7 +74,7 @@ bool Core::Entity::Player::loadActiveQuests()
void Core::Entity::Player::finishQuest( uint16_t questId )
{
int8_t idx = getQuestIndex( questId );
int8_t idx = getQuestIndex( static_cast< uint16_t >( questId ) );
if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) )
{
@ -123,7 +123,7 @@ void Core::Entity::Player::unfinishQuest( uint16_t questId )
void Core::Entity::Player::removeQuest( uint16_t questId )
{
int8_t idx = getQuestIndex( questId );
int8_t idx = getQuestIndex( static_cast< uint16_t >( questId ) );
if( ( idx != -1 ) && ( m_activeQuests[idx] != nullptr ) )
{

View file

@ -131,7 +131,7 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
field[11].getBinary( reinterpret_cast< char* >( m_customize ), sizeof( m_customize ) );
m_modelMainWeapon = field[12].get< uint64_t >();
m_modelMainWeapon = field[12].getUInt64();
field[14].getBinary( reinterpret_cast< char* >( m_modelEquip ), sizeof( m_modelEquip ) );
@ -144,7 +144,7 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession )
field[21].getBinary( reinterpret_cast< char* >( m_howTo ), sizeof( m_howTo ) );
m_contentId = field[22].get< uint64_t >();
m_contentId = field[22].getUInt64();
m_voice = field[23].get< uint32_t >();

View file

@ -29,14 +29,14 @@ namespace Core {
std::string m_helpText;
// userlevel needed to execute the command
Common::UserLevel m_userLevel;
uint8_t m_gmLevel;
DebugCommand( const std::string& n, pFunc functionPtr, const std::string& hText, Common::UserLevel uLevel )
DebugCommand( const std::string& n, pFunc functionPtr, const std::string& hText, uint8_t uLevel )
{
m_commandName = n;
m_pFunc = functionPtr;
m_helpText = hText;
m_userLevel = uLevel;
m_gmLevel = uLevel;
}
~DebugCommand()
@ -54,9 +54,9 @@ namespace Core {
return m_helpText;
}
Common::UserLevel getUserLevel() const
uint8_t getRequiredGmLevel() const
{
return m_userLevel;
return m_gmLevel;
}
};

View file

@ -1,6 +1,7 @@
#include <boost/lexical_cast.hpp>
#include <src/servers/Server_Common/Common.h>
#include <src/servers/Server_Common/Version.h>
#include <src/servers/Server_Common/Database/Database.h>
#include <src/servers/Server_Common/Network/GamePacketNew.h>
#include <src/servers/Server_Common/Network/CommonNetwork.h>
@ -39,16 +40,15 @@ extern Core::ServerZone g_serverZone;
// instanciate and initialize commands
Core::DebugCommandHandler::DebugCommandHandler()
{
// Push all commands onto the register map
registerCommand( "set", &DebugCommandHandler::set, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "get", &DebugCommandHandler::get, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "add", &DebugCommandHandler::add, "Loads and injects a premade Packet.", Common::UserLevel::all );
//registerCommand( "debug", &DebugCommandHandler::debug, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "inject", &DebugCommandHandler::injectPacket, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "injectc", &DebugCommandHandler::injectChatPacket, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "script_reload", &DebugCommandHandler::scriptReload, "Loads and injects a premade Packet.", Common::UserLevel::all );
registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down", Common::UserLevel::all );
// Push all commands onto the register map ( command name - function - description - required GM level )
registerCommand( "set", &DebugCommandHandler::set, "Loads and injects a premade Packet.", 1 );
registerCommand( "get", &DebugCommandHandler::get, "Loads and injects a premade Packet.", 1 );
registerCommand( "add", &DebugCommandHandler::add, "Loads and injects a premade Packet.", 1 );
registerCommand( "inject", &DebugCommandHandler::injectPacket, "Loads and injects a premade Packet.", 1 );
registerCommand( "injectc", &DebugCommandHandler::injectChatPacket, "Loads and injects a premade Packet.", 1 );
registerCommand( "script_reload", &DebugCommandHandler::scriptReload, "Loads and injects a premade Packet.", 1 );
registerCommand( "nudge", &DebugCommandHandler::nudge, "Nudges you forward/up/down", 1 );
registerCommand( "info", &DebugCommandHandler::serverInfo, "Send server info", 0 );
}
@ -61,7 +61,7 @@ Core::DebugCommandHandler::~DebugCommandHandler()
// add a command set to the register map
void Core::DebugCommandHandler::registerCommand( const std::string& n, Core::DebugCommand::pFunc functionPtr,
const std::string& hText, Core::Common::UserLevel uLevel )
const std::string& hText, uint8_t uLevel )
{
m_commandMap[std::string( n )] = boost::make_shared<DebugCommand>( n, functionPtr, hText, uLevel );
}
@ -70,12 +70,6 @@ void Core::DebugCommandHandler::registerCommand( const std::string& n, Core::Deb
void Core::DebugCommandHandler::execCommand( char * data, Core::Entity::PlayerPtr pPlayer )
{
if( pPlayer->getGmRank() <= 0 )
{
pPlayer->sendUrgent( "You are not allowed to use debug commands." );
return;
}
// define callback pointer
void ( DebugCommandHandler::*pf )( char *, Entity::PlayerPtr, boost::shared_ptr< DebugCommand > );
@ -98,9 +92,14 @@ void Core::DebugCommandHandler::execCommand( char * data, Core::Entity::PlayerPt
if( it == m_commandMap.end() )
// no command found, do something... or not
pPlayer->sendUrgent( "Command not found." );
// TODO Notify the client of the failed command
else
{
if( pPlayer->getGmRank() < it->second->getRequiredGmLevel() )
{
pPlayer->sendUrgent( "You are not allowed to use this command." );
return;
}
// command found, call the callback function and pass parameters if present.
pf = ( *it ).second->m_pFunc;
( this->*pf )( data, pPlayer, ( *it ).second );
@ -176,33 +175,6 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
pPlayer->queuePacket( setActorPosPacket );
}
else if( ( subCommand == "zone" ) && ( params != "" ) )
{
int32_t zoneId;
sscanf( params.c_str(), "%i", &zoneId );
if( zoneId < 1 )
pPlayer->sendUrgent( "Zone id out of range." );
else
{
pPlayer->setPosition( pPlayer->getPos() );
pPlayer->performZoning( zoneId, pPlayer->getPos(), 0);
}
}
else if( ( subCommand == "hp" ) && ( params != "" ) )
{
int32_t hp;
sscanf( params.c_str(), "%i", &hp );
pPlayer->setHp( hp );
auto control = Network::Packets::Server::ActorControlPacket142( pPlayer->getId(), Common::ActorControlType::HpSetStat, 1, pPlayer->getHp() );
pPlayer->sendToInRangeSet( control, true );
}
else if( ( subCommand == "tele" ) && ( params != "" ) )
{
int32_t aetheryteId;
@ -282,18 +254,25 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye
else if( subCommand == "aaah" )
{
int32_t id;
sscanf( params.c_str(), "%d", &id );
pPlayer->sendDebug( std::to_string( pPlayer->actionHasCastTime( id ) ) );
}
else if ( subCommand == "cfpenalty" )
{
int32_t minutes;
sscanf( params.c_str(), "%d", &minutes );
pPlayer->setCFPenaltyMinutes( minutes );
}
else if ( subCommand == "eorzeatime" )
{
uint64_t timestamp;
sscanf( params.c_str(), "%llu", &timestamp );
pPlayer->setEorzeaTimeOffset( timestamp );
pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( timestamp ) );
}
}
@ -324,37 +303,7 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye
"subCommand " + subCommand + " params: " + params );
if( ( subCommand == "item" ) && ( params != "" ) )
{
int32_t catalogId;
int32_t amount;
sscanf( params.c_str(), "%d %d", &catalogId, &amount );
if( amount < 1 || amount > 99 )
{
amount = 1;
}
if( ( catalogId == 0xcccccccc ) )
{
pPlayer->sendUrgent( "Syntaxerror." );
return;
}
if( !pPlayer->addItem( -1, catalogId, amount ) )
pPlayer->sendUrgent( "Item " + std::to_string( catalogId ) + " not found..." );
}
else if( subCommand == "exp" )
{
int32_t amount;
sscanf( params.c_str(), "%d", &amount );
pPlayer->gainExp( amount );
}
else if( subCommand == "status" )
if( subCommand == "status" )
{
int32_t id;
int32_t duration;
@ -534,3 +483,9 @@ void Core::DebugCommandHandler::nudge( char * data, Entity::PlayerPtr pPlayer, b
pPlayer->queuePacket( setActorPosPacket );
}
}
void Core::DebugCommandHandler::serverInfo( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr< Core::DebugCommand > command )
{
pPlayer->sendDebug( "SapphireServer " + Version::VERSION + " - " + Version::GIT_HASH );
pPlayer->sendDebug( "Sessions: " + std::to_string( g_serverZone.getSessionCount() ) );
}

View file

@ -22,7 +22,7 @@ public:
~DebugCommandHandler();
// register command to command map
void registerCommand( const std::string& n, DebugCommand::pFunc, const std::string& hText, Common::UserLevel uLevel );
void registerCommand( const std::string& n, DebugCommand::pFunc, const std::string& hText, uint8_t uLevel );
// execute command if registered
void execCommand( char * data, Entity::PlayerPtr pPlayer );
@ -37,6 +37,7 @@ public:
void injectPacket( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void injectChatPacket( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void nudge( char* data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
void serverInfo( char * data, Entity::PlayerPtr pPlayer, boost::shared_ptr<DebugCommand> command );
};

View file

@ -673,7 +673,7 @@ bool Core::Inventory::load()
for( int32_t i = 1; i <= 14; i++ )
{
uint64_t uItemId = field[i].get< uint64_t >();
uint64_t uItemId = field[i].getUInt64();
if( uItemId == 0 )
continue;
@ -710,7 +710,7 @@ bool Core::Inventory::load()
uint16_t storageId = bagField[0].get< uint16_t >();
for( int32_t i = 1; i <= 25; i++ )
{
uint64_t uItemId = bagField[i].get< uint64_t >();
uint64_t uItemId = bagField[i].getUInt64();
if( uItemId == 0 )
continue;
@ -743,7 +743,7 @@ bool Core::Inventory::load()
uint16_t storageId = curField[0].get< uint16_t >();
for( int32_t i = 1; i <= 12; i++ )
{
uint64_t uItemId = curField[i].get< uint64_t >();
uint64_t uItemId = curField[i].getUInt64();
if( uItemId == 0 )
continue;
@ -777,7 +777,7 @@ bool Core::Inventory::load()
uint16_t storageId = crystalField[0].get< uint16_t >();
for( int32_t i = 1; i <= 17; i++ )
{
uint64_t uItemId = crystalField[i].get< uint64_t >();
uint64_t uItemId = crystalField[i].getUInt64();
if( uItemId == 0 )
continue;

View file

@ -109,7 +109,7 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in
case 0x68: // Remove status (clicking it off)
{
// todo: check if status can be removed by client from exd
pPlayer->removeSingleStatusEffectFromId( param1 );
pPlayer->removeSingleStatusEffectFromId( static_cast< uint32_t >( param1 ) );
break;
}
case 0x69: // Cancel cast

View file

@ -69,6 +69,7 @@ enum GmCommand
Mp = 0x0065,
Tp = 0x0066,
Gp = 0x0067,
Exp = 0x0068,
Item = 0x00C8,
Gil = 0x00C9,
@ -82,6 +83,7 @@ enum GmCommand
QuestInspect = 0x0131,
GC = 0x0154,
GCRank = 0x0155,
Teri = 0x0258,
TeriInfo = 0x025D,
Jump = 0x025E,
JumpNpc = 0x025F,
@ -189,6 +191,12 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
pPlayer->sendNotice( "Gp for " + targetPlayer->getName() + " was set to " + std::to_string( param1 ) );
break;
}
case GmCommand::Exp:
{
targetPlayer->gainExp( param1 );
pPlayer->sendNotice( std::to_string( param1 ) + " Exp was added to " + targetPlayer->getName() );
break;
}
case GmCommand::Sex:
{
targetPlayer->setLookAt( CharaLook::Gender, param1 );
@ -245,6 +253,12 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
pPlayer->sendUrgent( "Item " + std::to_string( param1 ) + " not found..." );
break;
}
case GmCommand::Time:
{
pPlayer->setEorzeaTimeOffset( param2 );
pPlayer->sendNotice( "Eorzea time offset: " + std::to_string( param2 ) );
break;
}
case GmCommand::Weather:
{
targetPlayer->getCurrentZone()->setWeatherOverride( param1 );
@ -262,6 +276,18 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac
"\nNextWeather:" + std::to_string( pPlayer->getCurrentZone()->getNextWeather() ) );
break;
}
case GmCommand::Teri:
{
if( param1 < 128 )
pPlayer->sendUrgent( "Zone ID out of range." );
else
{
targetPlayer->setPosition( targetPlayer->getPos() );
targetPlayer->performZoning( param1, targetPlayer->getPos(), 0 );
pPlayer->sendNotice( targetPlayer->getName() + " was warped to Zone " + std::to_string( param1 ) );
}
break;
}
case GmCommand::Jump:
{

View file

@ -493,7 +493,7 @@ void Core::Network::GameConnection::chatHandler( const Packets::GamePacket& inPa
uint32_t sourceId = inPacket.getValAt< uint32_t >( 0x24 );
if( chatString.at( 0 ) == '@' )
if( chatString.at( 0 ) == '!' )
{
// execute game console command
g_gameCommandMgr.execCommand( const_cast< char * >( chatString.c_str() ) + 1, pPlayer );

View file

@ -52,7 +52,7 @@ void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inP
std::string actionIdStr = boost::str( boost::format( "%|04X|" ) % action );
pPlayer->sendDebug( "---------------------------------------" );
pPlayer->sendDebug( "ActionHandler ( " + actionIdStr + " | " +
g_exdData.m_actionInfoMap[action].name +
g_exdData.getActionInfo( action )->name +
" | " + std::to_string( targetId ) + " )" );
pPlayer->queuePacket( ActorControlPacket142( pPlayer->getId(), ActorControlType::ActionStart, 0x01, action ) );

View file

@ -346,7 +346,7 @@ bool Core::Scripting::ScriptManager::onMobKill( Entity::PlayerPtr pPlayer, uint1
// loop through all active quests and try to call available onMobKill callbacks
for( size_t i = 0; i < 30; i++ )
{
auto activeQuests = pPlayer->getQuestActive( i );
auto activeQuests = pPlayer->getQuestActive( static_cast< uint16_t >( i ) );
if( !activeQuests )
continue;

View file

@ -62,6 +62,11 @@ Core::XMLConfigPtr Core::ServerZone::getConfig() const
return m_pConfig;
}
size_t Core::ServerZone::getSessionCount() const
{
return m_sessionMap.size();
}
bool Core::ServerZone::registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId,
uint32_t bnpcNameId, uint32_t modelId, std::string aiName )
{
@ -101,7 +106,7 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] )
}
std::vector<std::string> args( argv + 1, argv + argc );
for( auto i = 0; i + 1 < args.size(); i += 2 )
for( uint32_t i = 0; i + 1 < args.size(); i += 2 )
{
std::string arg( "" );
std::string val( "" );

View file

@ -33,6 +33,8 @@ namespace Core {
XMLConfigPtr getConfig() const;
size_t getSessionCount() const;
bool registerBnpcTemplate( std::string templateName, uint32_t bnpcBaseId,
uint32_t bnpcNameId, uint32_t modelId, std::string aiName );

View file

@ -57,8 +57,8 @@ void Core::StatusEffect::StatusEffectContainer::addStatusEffect( StatusEffectPtr
m_effectMap[nextSlot] = pEffect;
GamePacketNew< Server::FFXIVIpcAddStatusEffect, ServerZoneIpcType > statusEffectAdd( m_pOwner->getId() );
statusEffectAdd.data().actor_id = m_pOwner->getId();
statusEffectAdd.data().actor_id1 = m_pOwner->getId();
statusEffectAdd.data().actor_id = pEffect->getTargetActorId();
statusEffectAdd.data().actor_id1 = pEffect->getSrcActorId();
statusEffectAdd.data().current_hp = m_pOwner->getHp();
statusEffectAdd.data().current_mp = m_pOwner->getMp();
statusEffectAdd.data().current_tp = m_pOwner->getTp();
@ -71,6 +71,7 @@ void Core::StatusEffect::StatusEffectContainer::addStatusEffect( StatusEffectPtr
//statusEffectAdd.data().unknown2 = 28;
statusEffectAdd.data().param = pEffect->getParam();
bool sendToSelf = m_pOwner->isPlayer() ? true : false;
m_pOwner->sendToInRangeSet( statusEffectAdd, sendToSelf );
@ -142,8 +143,8 @@ void Core::StatusEffect::StatusEffectContainer::update()
{
uint64_t currentTimeMs = Util::getTimeMs();
uint64_t thisTickDmg = 0;
uint64_t thisTickHeal = 0;
uint32_t thisTickDmg = 0;
uint32_t thisTickHeal = 0;
for( auto effectIt : m_effectMap )
{

View file

@ -189,7 +189,7 @@ void Zone::loadCellCache()
Entity::BattleNpcPtr pBNpc( new Entity::BattleNpc( modelId, nameId,
pos,
sizeId, type, level, behaviour, mobType ) );
pBNpc->setRotation( rotation );
pBNpc->setRotation( static_cast< float >( rotation ) );
cache.push_back( pBNpc );
//pushActor( pBNpc );