Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="httplib.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="httplib.cc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<Keyword>Win32Proj</Keyword>
<RootNamespace>libconfig</RootNamespace>
<ProjectGuid>{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir).vs\build\</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir).vs\build\</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<OutDir>$(SolutionDir).vs\build\</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir).vs\build\</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="httplib.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="httplib.cc">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
@@ -226,6 +226,10 @@ endif()
message( STATUS "Detecting networking library (socket/nsl/ws2_32) - done" )
endif()

#
# enable web server?
#
option( ENABLE_WEB_SERVER "Build web-server (default=ON)" ON )

#
# Test for big endian
@@ -4,18 +4,20 @@ HAVE_MYSQL=@HAVE_MYSQL@
OMAP=@OMAP@
ifeq ($(HAVE_MYSQL),yes)
ALL_DEPENDS=server tools
SERVER_DEPENDS=common login char map import
SERVER_DEPENDS=common login char map web import
COMMON_DEPENDS=libconfig rapidyaml yaml-cpp
LOGIN_DEPENDS=libconfig common
CHAR_DEPENDS=libconfig common rapidyaml
MAP_DEPENDS=libconfig common rapidyaml
WEB_DEPENDS=libconfig common yaml-cpp httplib
else
ALL_DEPENDS=needs_mysql
SERVER_DEPENDS=needs_mysql
COMMON_DEPENDS=needs_mysql
LOGIN_DEPENDS=needs_mysql
CHAR_DEPENDS=needs_mysql
MAP_DEPENDS=needs_mysql
WEB_DEPENDS=needs_mysql
endif


@@ -25,6 +27,7 @@ endif
login \
char \
map \
web \
tools \
import \
clean help \
@@ -49,6 +52,9 @@ char: $(CHAR_DEPENDS)
map: $(MAP_DEPENDS)
@$(MAKE) -C src/map server

web: $(WEB_DEPENDS)
@$(MAKE) -C src/web server

libconfig:
@$(MAKE) -C 3rdparty/libconfig

@@ -61,6 +67,9 @@ rapidyaml:
yaml-cpp:
@$(MAKE) -C 3rdparty/yaml-cpp

httplib:
@$(MAKE) -C 3rdparty/httplib

import:
# 1) create conf/import folder
# 2) add missing files
@@ -81,9 +90,11 @@ clean:
@$(MAKE) -C 3rdparty/libconfig $@
@$(MAKE) -C 3rdparty/rapidyaml $@
@$(MAKE) -C 3rdparty/yaml-cpp $@
@$(MAKE) -C 3rdparty/httplib $@
@$(MAKE) -C src/login $@
@$(MAKE) -C src/char $@
@$(MAKE) -C src/map $@
@$(MAKE) -C src/web $@
@$(MAKE) -C src/tool $@

help:
@@ -93,9 +104,11 @@ help:
@echo "'libconfig' - builds object files of libconfig"
@echo "'rapidyaml' - builds object files of rapidyaml"
@echo "'yaml-cpp' - builds object files of yaml-cpp"
@echo "'httplib' - builds object files of httplib"
@echo "'login' - builds login server"
@echo "'char' - builds char server"
@echo "'map' - builds map server"
@echo "'web' - builds web server"
@echo "'tools' - builds all the tools in src/tools"
@echo "'import' - builds conf/import, conf/msg_conf/import and db/import folders from their template folders (x-tmpl)"
@echo "'all' - builds all the above targets"
@@ -61,7 +61,7 @@ watch_serv(){
#now checking status and looping
count=0;
while true; do
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
LOGFILE="$LOG_DIR/$i.launch.log"
LOGRUN="$LOG_DIR/$i.log"
@@ -89,7 +89,7 @@ watch_serv(){
restart(){
$0 stop
if [ $1 ]; then sleep $1; fi
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
FIFO="$1_fifo"
while true; do
@@ -117,7 +117,7 @@ case $1 in
else
echo "Logging is disabled"
fi
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
start_serv $i $ENLOG
done
@@ -128,15 +128,15 @@ case $1 in
if [ -z $2 ]; then Restart_count=10; else Restart_count=$2; fi
if [ -z $3 ]; then Restart_sleep=3; else Restart_sleep=$3; fi
echo "Going to watch rAthena for restart_count = $Restart_count, restart_sleep = $Restart_sleep"
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
start_serv $i 1
done
watch_serv $Restart_count $Restart_sleep
echo "Watching rAthena now."
;;
'stop')
for i in ${M_SRV} ${C_SRV} ${L_SRV}
for i in ${W_SRV} ${M_SRV} ${C_SRV} ${L_SRV}
do
PIDFILE=.${i}.pid
if [ -e ./${PIDFILE} ]; then
@@ -158,20 +158,20 @@ case $1 in
restart
;;
'status')
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
get_status ${i}
if [ ${PSRUN} ]; then echo "'${i}' is running p${PSRUN}"; else echo "'${i}' seems to be down"; fi
done
;;
'val_runonce')
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
valgrind --leak-check=full --show-leak-kinds=all ./$i --run-once > "log/$i.runonce.leak"
done
;;
'valchk')
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
valgrind --leak-check=full --show-leak-kinds=all ./$i > "log/$i.runonce.leak"
done
@@ -118,12 +118,6 @@ client_reshuffle_dice: yes
// NOTE: Enabling this option degrades performance.
client_sort_storage: no

// Do we allow to change guilde emblem during woe_time?
emblem_woe_change: yes

// How many transparent pixel can be found in emblem before detected as invalid? (Note 2)
emblem_transparency_limit: 80

// Update enemy position while in invisible state? (Note 1)
// NOTE: Set to 'no' will make client won't update enemy position unless the players have "Intravision" effect.
// So that will help client handling WPE - Maya Purple Hack stuff.
Empty file.
@@ -15,6 +15,12 @@ party_share_level: 15
// Amount of status points a new character will start with
start_status_points: 48

// Do we allow to change guild emblem during woe_time?
emblem_woe_change: yes

// How many transparent pixel can be found in emblem before detected as invalid? (Note 2)
emblem_transparency_limit: 80

// You can specify the codepage to use in your MySQL tables here.
// (Note that this feature requires MySQL 4.1+)
//default_codepage:
@@ -58,6 +64,13 @@ map_server_id: ragnarok
map_server_pw: ragnarok
map_server_db: ragnarok

// MySQL Web Server
web_server_ip: 127.0.0.1
web_server_port: 3306
web_server_id: ragnarok
web_server_pw: ragnarok
web_server_db: ragnarok

// MySQL Log Database
log_db_ip: 127.0.0.1
log_db_port: 3306
@@ -152,6 +165,14 @@ market_table: market
roulette_table: db_roulette
guild_storage_log: guild_storage_log

// Web Database Tables
// NOTE: The web server reads the login (login) and char (guild) tables, so it needs
// the ability to connect to those databases.
guild_emblems: guild_emblems
Copy link
Member

@Lemongrass3110 Lemongrass3110 Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe put a small comment here that the web server also uses the character server's guild table?
And also the login server's login table (token)?

Otherwise redefine them here with a new name, but then again putting it into inter is useless in general.

user_configs: user_configs
char_configs: char_configs
merchant_configs: merchant_configs

// Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
use_sql_db: no

@@ -167,6 +167,11 @@ client_hash_check: off
// This is required for new clients that get data via an additional API over HTTP
use_web_auth_token: yes

// Delay (in milliseconds) disabling webtoken after character logs off
// There's a race condition when the client logs off. The char-server could revoke the token before
// the client can save the changed configs.
disable_webtoken_delay: 10000

// Client MD5 hashes
// The client with the specified hash can be used to log in by players with
// a group_id equal to or greater than the given value.
@@ -0,0 +1,3 @@
// rAthena msg_athena.conf
// Message Configuration of web-server
// -----------------------
@@ -0,0 +1,56 @@
// rAthena Web Server configuration file.

// Note: "Comments" are all text on the right side of a double slash "//"
// Whatever text is commented will not be parsed by the servers, and serves
// only as information/reference.

// The web server listens on the interface with this IP address.
// NOTE: This allows you to run multiple servers on multiple interfaces
// while using the same ports for each server.
//bind_ip: 127.0.0.1

// Web Server Port
web_port: 8888

//Time-stamp format which will be printed before all messages.
//Can at most be 20 characters long.
//Common formats:
// %I:%M:%S %p (hour:minute:second 12 hour, AM/PM format)
// %H:%M:%S (hour:minute:second, 24 hour format)
// %d/%b/%Y (day/Month/year)
//For full format information, consult the strftime() manual.
//timestamp_format: [%d/%b %H:%M]

//If redirected output contains escape sequences (color codes)
stdout_with_ansisequence: no

//Makes server log selected message types to a file in the /log/ folder
//1: Log Warning Messages
//2: Log Error and SQL Error messages.
//4: Log Debug Messages
//Example: "console_msg_log: 7" logs all 3 kinds
//Messages logged by this overrides console_silent setting
console_msg_log: 0

// File path to store the console messages above
console_log_filepath: ./log/web-msg_log.log

//Makes server output more silent by omitting certain types of messages:
//1: Hide Information messages
//2: Hide Status messages
//4: Hide Notice Messages
//8: Hide Warning Messages
//16: Hide Error and SQL Error messages.
//32: Hide Debug Messages
//Example: "console_silent: 7" Hides information, status and notice messages (1+2+4)
console_silent: 0

// Print requests and responses?
// This is useful for debugging purposes, it will print the entire
// request and response for each transaction.
print_req_res: off

// Allow GIF images to be uploaded as guild emblem?
allow_gifs: yes

import: conf/import/web_conf.txt
Lemongrass3110 marked this conversation as resolved.
Show resolved Hide resolved
129 configure
@@ -1,5 +1,5 @@
#! /bin/sh
# From configure.in Revision.
# From configure.ac Revision.
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69.
#
@@ -642,6 +642,7 @@ ac_ct_CC
CFLAGS
CC
SET_MAKE
OWEB
OMAP
OCHR
OLOG
@@ -674,6 +675,7 @@ infodir
docdir
oldincludedir
includedir
runstatedir
localstatedir
sharedstatedir
sysconfdir
@@ -712,6 +714,7 @@ with_maxconn
with_outputlogin
with_outputchar
with_outputmap
with_outputweb
with_mysql
with_MYSQL_CFLAGS
with_MYSQL_LIBS
@@ -768,6 +771,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
@@ -1020,6 +1024,15 @@ do
| -silent | --silent | --silen | --sile | --sil)
silent=yes ;;

-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;

-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1157,7 +1170,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir
libdir localedir mandir runstatedir
do
eval ac_val=\$$ac_var
# Remove trailing slashes.
@@ -1310,6 +1323,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -1367,16 +1381,19 @@ Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-maxconn[=ARG] optionally set the maximum connections the core can
handle. By default the system header value will be used.
This will only be the compile time limit, make sure
you set the correct limit with ulimit on your OS.
handle. By default the system header value will be
used. This will only be the compile time limit, make
sure you set the correct limit with ulimit on your
OS.
--with-outputlogin[=ARG]
Specify the login-serv output name (defaults to
login-server)
--with-outputchar[=ARG] Specify the char-serv output name (defaults to
char-server)
--with-outputmap[=ARG] Specify the map-serv output name (defaults to
map-server)
--with-outputweb[=ARG] Specify the web-serv output name (defaults to
web-server)
--with-mysql[=ARG] optionally specify the path to the mysql_config
executable
--with-MYSQL_CFLAGS=ARG specify MYSQL_CFLAGS manually (instead of using
@@ -1718,21 +1735,21 @@ $as_echo "$ac_res" >&6; }
} # ac_fn_cxx_check_header_compile
# ac_fn_c_try_compile LINENO
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If i see it correctly compiling and linking order has been switched.
Is this really necessary?

Copy link
Member Author

@vstumpf vstumpf Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is an autotools project, this is a generated file. i modified configure.ac and ran autoreconf --install

# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_compile ()
# ac_fn_cxx_try_link LINENO
# -------------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext
if { { ac_try="$ac_compile"
rm -f conftest.$ac_objext conftest$ac_exeext
if { { ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
@@ -1741,36 +1758,44 @@ $as_echo "$ac_try_echo"; } >&5
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then :
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
} # ac_fn_cxx_try_link
# ac_fn_cxx_try_link LINENO
# -------------------------
# Try to link conftest.$ac_ext, and return whether this succeeded.
ac_fn_cxx_try_link ()
# ac_fn_c_try_compile LINENO
# --------------------------
# Try to compile conftest.$ac_ext, and return whether this succeeded.
ac_fn_c_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
rm -f conftest.$ac_objext conftest$ac_exeext
if { { ac_try="$ac_link"
rm -f conftest.$ac_objext
if { { ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
$as_echo "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
grep -v '^ *+' conftest.err >conftest.er1
@@ -1779,28 +1804,20 @@ $as_echo "$ac_try_echo"; } >&5
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_cxx_werror_flag" ||
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
}; then :
} && test -s conftest.$ac_objext; then :
ac_retval=0
else
$as_echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_link
} # ac_fn_c_try_compile
# ac_fn_cxx_check_func LINENO FUNC VAR
# ------------------------------------
@@ -2740,12 +2757,14 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu

ac_config_files="$ac_config_files Makefile src/common/Makefile"

ac_config_files="$ac_config_files 3rdparty/libconfig/Makefile 3rdparty/yaml-cpp/Makefile 3rdparty/rapidyaml/Makefile"
ac_config_files="$ac_config_files 3rdparty/libconfig/Makefile 3rdparty/yaml-cpp/Makefile 3rdparty/rapidyaml/Makefile 3rdparty/httplib/Makefile"

ac_config_files="$ac_config_files src/char/Makefile src/login/Makefile"

ac_config_files="$ac_config_files src/map/Makefile src/tool/Makefile"

ac_config_files="$ac_config_files src/web/Makefile"


ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
@@ -3284,11 +3303,10 @@ rm -f core conftest.err conftest.$ac_objext \
$as_echo "$have_linux_epoll" >&6; }
fi
if test x$enable_epoll,$have_linux_epoll = xyes,no; then
as_fn_error $? "epoll support explicitly enabled but not available" "$LINENO" 5
as_fn_error $? "epoll support explicitly enabled but not available" "$LINENO" 5
fi


#
# debug
#
@@ -3464,18 +3482,24 @@ fi


#
# Optionally set the max number of network conenctions
# the core will be support
# Optionally set the maximum number of network connections
# the core will be able to handle
#

# Check whether --with-maxconn was given.
if test "${with_maxconn+set}" = set; then :
withval=$with_maxconn;
if ! test "$withval" -ge 0 -o "$withval" -lt 0 2>&- ; then
as_fn_error $? "Invalid argument --with-maxconn=$withval ... stopping" "$LINENO" 5
else
CPPFLAGS="$CPPFLAGS -DMAXCONN=$withval"
fi
if ! test "$withval" -ge 0 -o "$withval" -lt 0 2>&- ; then
as_fn_error $? "Invalid argument --with-maxconn=$withval ... stopping" "$LINENO" 5
else
CPPFLAGS="$CPPFLAGS -DMAXCONN=$withval"
fi

else

CPPFLAGS="$CPPFLAGS"


fi


@@ -3520,6 +3544,18 @@ OMAP=$output_map



# Check whether --with-outputweb was given.
if test "${with_outputweb+set}" = set; then :
withval=$with_outputweb; output_web="$withval"
else
output_web="web-server"

fi

OWEB=$output_web



#
# Optionally specify the path to mysql_config
#
@@ -7027,7 +7063,6 @@ fi



#
# Host specific stuff
#
@@ -7092,8 +7127,8 @@ $as_echo "$as_me: CFLAGS_AR= $CFLAGS_AR" >&6;}
$as_echo "$as_me: LDFLAGS= $LDFLAGS" >&6;}
#AC_MSG_NOTICE([PROD_WARN= $PROD_WARN])
#AC_MSG_NOTICE([EXTRA_WARN= $EXTRA_WARN])
{ $as_echo "$as_me:${as_lineno-$LINENO}: output name = $output_login, $output_char, $output_map" >&5
$as_echo "$as_me: output name = $output_login, $output_char, $output_map" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: output name = $output_login, $output_char, $output_map, $output_web" >&5
$as_echo "$as_me: output name = $output_login, $output_char, $output_map, $output_web" >&6;}

###############################################################################
cat >confcache <<\_ACEOF
@@ -7807,10 +7842,12 @@ do
"3rdparty/libconfig/Makefile") CONFIG_FILES="$CONFIG_FILES 3rdparty/libconfig/Makefile" ;;
"3rdparty/yaml-cpp/Makefile") CONFIG_FILES="$CONFIG_FILES 3rdparty/yaml-cpp/Makefile" ;;
"3rdparty/rapidyaml/Makefile") CONFIG_FILES="$CONFIG_FILES 3rdparty/rapidyaml/Makefile" ;;
"3rdparty/httplib/Makefile") CONFIG_FILES="$CONFIG_FILES 3rdparty/httplib/Makefile" ;;
"src/char/Makefile") CONFIG_FILES="$CONFIG_FILES src/char/Makefile" ;;
"src/login/Makefile") CONFIG_FILES="$CONFIG_FILES src/login/Makefile" ;;
"src/map/Makefile") CONFIG_FILES="$CONFIG_FILES src/map/Makefile" ;;
"src/tool/Makefile") CONFIG_FILES="$CONFIG_FILES src/tool/Makefile" ;;
"src/web/Makefile") CONFIG_FILES="$CONFIG_FILES src/web/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
@@ -8,9 +8,10 @@ AC_LANG([C++])
AC_LANG_COMPILER_REQUIRE
AC_CONFIG_SRCDIR([src/common/cbasetypes.hpp])
AC_CONFIG_FILES([Makefile src/common/Makefile])
AC_CONFIG_FILES([3rdparty/libconfig/Makefile 3rdparty/yaml-cpp/Makefile 3rdparty/rapidyaml/Makefile])
AC_CONFIG_FILES([3rdparty/libconfig/Makefile 3rdparty/yaml-cpp/Makefile 3rdparty/rapidyaml/Makefile 3rdparty/httplib/Makefile])
AC_CONFIG_FILES([src/char/Makefile src/login/Makefile])
AC_CONFIG_FILES([src/map/Makefile src/tool/Makefile])
AC_CONFIG_FILES([src/web/Makefile])

AC_GNU_SOURCE

@@ -46,7 +47,7 @@ AC_ARG_ENABLE(
[packetver],
AC_HELP_STRING(
[--enable-packetver=ARG],
[Sets the PACKETVER define. (see src/common/mmo.h)]
[Sets the PACKETVER define. (see src/common/mmo.hpp)]
),
[enable_packetver="$enableval"],
[enable_packetver=""]
@@ -302,7 +303,8 @@ AC_ARG_WITH(
[maxconn],
AC_HELP_STRING(
[--with-maxconn@<:@=ARG@:>@],
[optionally set the maximum connections the core can handle. By default the system header value is used.]
[optionally set the maximum connections the core can handle. By default the system header value will be used.
This will only be the compile time limit, make sure you set the correct limit with ulimit on your OS.]
),
[
if ! test "$withval" -ge 0 -o "$withval" -lt 0 2>&- ; then
@@ -353,6 +355,17 @@ AC_ARG_WITH(
)
AC_SUBST([OMAP],$output_map)

AC_ARG_WITH(
[outputweb],
AC_HELP_STRING(
[--with-outputweb@<:@=ARG@:>@],
[Specify the web-serv output name (defaults to web-server)]
),
[output_web="$withval"],
[output_web="web-server"]
)
AC_SUBST([OWEB],$output_web)


#
# Optionally specify the path to mysql_config
@@ -635,7 +648,7 @@ if test "$enable_lto" != "no" ; then
fi


AC_DEFUN(AC_CHECK_COMPILER_WFLAG,
AC_DEFUN([AC_CHECK_COMPILER_WFLAG],
[
AC_MSG_CHECKING([whether $CC supports -W$1])
OLD_CFLAGS="$CFLAGS"
@@ -670,7 +683,7 @@ AC_DEFUN(AC_CHECK_COMPILER_WFLAG,
]
)

AC_DEFUN(AC_CHECK_COMPILER_WNOFLAG,
AC_DEFUN([AC_CHECK_COMPILER_WNOFLAG],
[
AC_MSG_CHECKING([whether $CC supports -Wno-$1])
OLD_CFLAGS="$CFLAGS"
@@ -1301,7 +1314,6 @@ AC_SUBST([PCRE_VERSION])
AC_SUBST([PCRE_LIBS])
AC_SUBST([PCRE_CFLAGS])


#
# Host specific stuff
#
@@ -1356,7 +1368,7 @@ AC_MSG_NOTICE([CFLAGS_AR= $CFLAGS_AR])
AC_MSG_NOTICE([LDFLAGS= $LDFLAGS])
#AC_MSG_NOTICE([PROD_WARN= $PROD_WARN])
#AC_MSG_NOTICE([EXTRA_WARN= $EXTRA_WARN])
AC_MSG_NOTICE([output name = $output_login, $output_char, $output_map])
AC_MSG_NOTICE([output name = $output_login, $output_char, $output_map, $output_web])

###############################################################################
AC_OUTPUT
@@ -1,12 +1,13 @@
L_SRV=login-server
C_SRV=char-server
M_SRV=map-server
W_SRV=web-server
INST_PATH=/opt
PKG=rathena
PKG_PATH=$INST_PATH/$PKG

check_files() {
for i in ${L_SRV} ${C_SRV} ${M_SRV}
for i in ${L_SRV} ${C_SRV} ${M_SRV} ${W_SRV}
do
if [ ! -f ./$i ]; then
echo "$i does not exist... exiting..."
@@ -84,6 +84,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml2sql", "src\tool\yaml2s
{352B45B3-FE88-4431-9D89-48CF811446DB} = {352B45B3-FE88-4431-9D89-48CF811446DB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "web-server", "src\web\web-server.vcxproj", "{5809F27D-65B5-46A3-A340-C8685A8C09F4}"
ProjectSection(ProjectDependencies) = postProject
{F8FD7B1E-8E1C-4CC3-9CD1-2E28F77B6559} = {F8FD7B1E-8E1C-4CC3-9CD1-2E28F77B6559}
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6} = {7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "httplib", "3rdparty\httplib\httplib.vcxproj", "{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yamlupgrade", "src\tool\yamlupgrade.vcxproj", "{9115C6D1-520A-4540-B4FE-95F3C923FE2C}"
ProjectSection(ProjectDependencies) = postProject
{61D6A599-6BED-4154-A9FC-40553BD972E0} = {61D6A599-6BED-4154-A9FC-40553BD972E0}
@@ -181,6 +189,22 @@ Global
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|Win32.Build.0 = Release|Win32
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|x64.ActiveCfg = Release|x64
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|x64.Build.0 = Release|x64
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Debug|Win32.ActiveCfg = Debug|Win32
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Debug|Win32.Build.0 = Debug|Win32
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Debug|x64.ActiveCfg = Debug|x64
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Debug|x64.Build.0 = Debug|x64
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Release|Win32.ActiveCfg = Release|Win32
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Release|Win32.Build.0 = Release|Win32
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Release|x64.ActiveCfg = Release|x64
{5809F27D-65B5-46A3-A340-C8685A8C09F4}.Release|x64.Build.0 = Release|x64
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Debug|Win32.ActiveCfg = Debug|Win32
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Debug|Win32.Build.0 = Debug|Win32
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Debug|x64.ActiveCfg = Debug|x64
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Debug|x64.Build.0 = Debug|x64
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Release|Win32.ActiveCfg = Release|Win32
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Release|Win32.Build.0 = Release|Win32
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Release|x64.ActiveCfg = Release|x64
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6}.Release|x64.Build.0 = Release|x64
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|Win32.ActiveCfg = Debug|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|Win32.Build.0 = Debug|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|x64.ActiveCfg = Debug|x64
@@ -213,6 +237,8 @@ Global
{61D6A599-6BED-4154-A9FC-40553BD972E0} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
{5A9059F2-4933-49A2-BEE6-CC67F66FA070} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
{CDBBB260-B245-44EC-80FB-3F9421885E40} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
{5809F27D-65B5-46A3-A340-C8685A8C09F4} = {6D9F5D00-2988-4812-844D-D155C4F588DC}
{7A1A25BC-2CF7-44B2-8CAF-B4273B510FC6} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
{9115C6D1-520A-4540-B4FE-95F3C923FE2C} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
{492E2981-34F4-3A6A-BFD9-46096C641203} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
EndGlobalSection
@@ -10,6 +10,7 @@ For a new install, the following needs to be imported into the 'ragnarok' schema
Note: The schema name is defined in `conf/inter_athena.conf::map_server_db`.

* main.sql - Contains tables for normal server usage.
* web.sql - Contains tables for the web service
* roulette_default_data.sql - Contains data for the client's roulette game.

For a new install, the following needs to be imported into the 'log' schema:
@@ -72,3 +73,7 @@ To run these queries the user requires the [FILE](https://dev.mysql.com/doc/refm
* item_db_to_txt.sql - Dumps the __pre-renewal__ item data table to TXT format.
* item_db2_re_to_txt.sql - Dumps the __renewal__ item data table (import) to TXT format.
* item_db2_to_txt.sql - Dumps the __pre-renewal__ item data table (import) to TXT format.

### Notes
---
The web-server must be able to read the `login` and `guild` tables from the `login-server` and `char-server`, respectively.
@@ -0,0 +1,50 @@

Copy link
Contributor

@aleos89 aleos89 Apr 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a quick doc in the SQL Readme?

--
-- Table structure for table `guild_emblems`
--

CREATE TABLE IF NOT EXISTS `guild_emblems` (
`guild_id` int(11) unsigned NOT NULL,
`world_name` varchar(32) NOT NULL,
`file_type` varchar(255) NOT NULL,
`file_data` blob,
`version` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`guild_id`, `world_name`)
) ENGINE=MyISAM;

--
-- Table structure for table `user_configs`
--

CREATE TABLE IF NOT EXISTS `user_configs` (
`account_id` int(11) unsigned NOT NULL,
`world_name` varchar(32) NOT NULL,
`data` longtext NOT NULL,
PRIMARY KEY (`account_id`, `world_name`)
) ENGINE=MyISAM;


--
-- Table structure for table `char_configs`
--

CREATE TABLE IF NOT EXISTS `char_configs` (
`account_id` int(11) unsigned NOT NULL,
`char_id` int(11) unsigned NOT NULL,
`world_name` varchar(32) NOT NULL,
`data` longtext NOT NULL,
PRIMARY KEY (`account_id`, `char_id`, `world_name`)
) ENGINE=MyISAM;

--
-- Table structure for table `merchant_configs`
--

CREATE TABLE IF NOT EXISTS `merchant_configs` (
`account_id` int(11) unsigned NOT NULL,
`char_id` INT(11) UNSIGNED NOT NULL,
`world_name` varchar(32) NOT NULL,
`store_type` tinyint(3) UNSIGNED NOT NULL DEFAULT 0,
`data` longtext NOT NULL,
PRIMARY KEY (`account_id`, `char_id`, `world_name`)
) ENGINE=MyISAM;
@@ -16,5 +16,6 @@ endif()
add_subdirectory( login )
add_subdirectory( char )
add_subdirectory( map )
add_subdirectory( web )
add_subdirectory( tool )

@@ -228,7 +228,9 @@ typedef uintptr_t uintptr;
// some redefine of function redefines for some Compilers
//////////////////////////////////////////////////////////////////////////
#if defined(_MSC_VER) || defined(__BORLANDC__)
#ifndef strcasecmp
Copy link
Member

@Lemongrass3110 Lemongrass3110 Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you mention why this was needed?
Right now we only knew this was necessary for MSC and BORLAND.

Copy link
Member Author

@vstumpf vstumpf May 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was running into issues with it already being defined on windows build, I can remove it if needed.

#define strcasecmp stricmp
#endif
#define strncasecmp strnicmp
#define strncmpi strnicmp
#if defined(__BORLANDC__) || _MSC_VER < 1900
@@ -379,7 +379,11 @@ int main (int argc, char **argv)
// Main runtime cycle
while (runflag != CORE_ST_STOP) {
t_tick next = do_timer(gettick_nocache());
do_sockets(next);

if (SERVER_TYPE != ATHENA_SERVER_WEB)
do_sockets(next);
else
do_wait(next);
}

do_final();
@@ -30,6 +30,7 @@ enum {
ATHENA_SERVER_CHAR = 2, // char server
ATHENA_SERVER_INTER = 4, // inter server
ATHENA_SERVER_MAP = 8, // map server
ATHENA_SERVER_WEB = 16, // web server
};

extern char SERVER_TYPE;
@@ -40,6 +40,9 @@
#endif
#endif

#include <chrono>
#include <thread>

#include "cbasetypes.hpp"
#include "malloc.hpp"
#include "mmo.hpp"
@@ -887,6 +890,14 @@ int WFIFOSET(int fd, size_t len)
return 0;
}


// replacement for do_sockets, where it does nothing
int do_wait(t_tick next)
{
std::this_thread::sleep_for(std::chrono::milliseconds(next));
return 0;
}

int do_sockets(t_tick next)
{
#ifndef SOCKET_EPOLL
@@ -133,6 +133,7 @@ int WFIFOSET(int fd, size_t len);
int RFIFOSKIP(int fd, size_t len);

int do_sockets(t_tick next);
int do_wait(t_tick next);
void do_close(int fd);
void socket_init(void);
void socket_final(void);
@@ -433,6 +433,12 @@ void Sql_FreeResult(Sql* self)
}
}

/// Closes the handle
void Sql_Close(Sql* self) {
if (self) {
mysql_close(&self->handle);
}
}


/// Shows debug information (last query).
@@ -456,6 +462,7 @@ void Sql_Free(Sql* self)
Sql_FreeResult(self);
StringBuf_Destroy(&self->buf);
if( self->keepalive != INVALID_TIMER ) delete_timer(self->keepalive, Sql_P_KeepaliveTimer);
Sql_Close(self);
aFree(self);
}
}
@@ -88,4 +88,7 @@
/// Check if the specified packetvresion supports the cashshop sale system
#define PACKETVER_SUPPORTS_SALES PACKETVER >= 20131223

/// Use web service?
#define WEB_SERVER_ENABLE PACKETVER > 20200300

#endif /* CONFIG_PACKETS_HPP */
@@ -54,15 +54,15 @@ static bool account_db_sql_remove(AccountDB* self, const uint32 account_id);
static bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id );
static bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id );
static bool account_db_sql_remove_webtokens( AccountDB* self );
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc);
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token);
static bool account_db_sql_load_num(AccountDB* self, struct mmo_account* acc, const uint32 account_id);
static bool account_db_sql_load_str(AccountDB* self, struct mmo_account* acc, const char* userid);
static AccountDBIterator* account_db_sql_iterator(AccountDB* self);
static void account_db_sql_iter_destroy(AccountDBIterator* self);
static bool account_db_sql_iter_next(AccountDBIterator* self, struct mmo_account* acc);

static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32 account_id);
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new);
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token);

/// public constructor
AccountDB* account_db_sql(void) {
@@ -334,7 +334,7 @@ static bool account_db_sql_create(AccountDB* self, struct mmo_account* acc) {

// insert the data into the database
acc->account_id = account_id;
return mmo_auth_tosql(db, acc, true);
return mmo_auth_tosql(db, acc, true, false);
}

/**
@@ -367,9 +367,9 @@ static bool account_db_sql_remove(AccountDB* self, const uint32 account_id) {
* @param acc: pointer of mmo_account to save
* @return true if successful, false if something has failed
*/
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc) {
static bool account_db_sql_save(AccountDB* self, const struct mmo_account* acc, bool refresh_token) {
AccountDB_SQL* db = (AccountDB_SQL*)self;
return mmo_auth_tosql(db, acc, false);
return mmo_auth_tosql(db, acc, false, refresh_token);
}

/**
@@ -559,7 +559,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, uint32
* @param is_new: if it's a new entry or should we update
* @return true if successful, false if something has failed
*/
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new) {
static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, bool is_new, bool refresh_token) {
Sql* sql_handle = db->accounts;
SqlStmt* stmt = SqlStmt_Malloc(sql_handle);
bool result = false;
@@ -644,7 +644,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
}
}

if( acc->sex != 'S' && login_config.use_web_auth_token ){
if( acc->sex != 'S' && login_config.use_web_auth_token && refresh_token ){
static bool initialized = false;
static const char* query;

@@ -919,13 +919,37 @@ bool account_db_sql_enable_webtoken( AccountDB* self, const uint32 account_id ){
return true;
}

/**
* Timered function to disable webtoken for user
* If the user is online, then they must have logged since we started the timer.
* In that case, do nothing. The new authtoken must be valid.
* @param tid: timer id
* @param tick: tick of execution
* @param id: user account id
* @param data: AccountDB // because we don't use singleton???
* @return :0
*/
TIMER_FUNC(account_disable_webtoken_timer){
const struct online_login_data* p = login_get_online_user(id);
AccountDB_SQL* db = reinterpret_cast<AccountDB_SQL*>(data);

if (p == nullptr) {
ShowInfo("Web Auth Token for account %d was disabled\n", id);
if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, id ) ){
Sql_ShowDebug( db->accounts );
return 0;
}
}

return 0;
}



bool account_db_sql_disable_webtoken( AccountDB* self, const uint32 account_id ){
AccountDB_SQL* db = (AccountDB_SQL*)self;

if( SQL_ERROR == Sql_Query( db->accounts, "UPDATE `%s` SET `web_auth_token_enabled` = '0' WHERE `account_id` = '%u'", db->account_db, account_id ) ){
Sql_ShowDebug( db->accounts );
return false;
}
add_timer(gettick() + login_config.disable_webtoken_delay, account_disable_webtoken_timer, account_id, reinterpret_cast<intptr_t>(db));

return true;
}
@@ -12,6 +12,7 @@
#define WEB_AUTH_TOKEN_LENGTH 16+1
#endif


typedef struct AccountDB AccountDB;
typedef struct AccountDBIterator AccountDBIterator;

@@ -120,8 +121,9 @@ struct AccountDB {
///
/// @param self Database
/// @param acc Account data
/// @param refresh_token Whether or not to refresh the web auth token
/// @return true if successful
bool (*save)(AccountDB* self, const struct mmo_account* acc);
bool (*save)(AccountDB* self, const struct mmo_account* acc, bool refresh_token);

/// Finds an account with account_id and copies it to acc.
///
@@ -413,7 +413,7 @@ int login_mmo_auth(struct login_session_data* sd, bool isServer) {
safestrncpy(acc.last_ip, ip, sizeof(acc.last_ip));
acc.unban_time = 0;
acc.logincount++;
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, true);

if( login_config.use_web_auth_token ){
safestrncpy( sd->web_auth_token, acc.web_auth_token, WEB_AUTH_TOKEN_LENGTH );
@@ -646,6 +646,8 @@ bool login_config_read(const char* cfgName, bool normal) {
login_config.client_hash_check = config_switch(w2);
else if(!strcmpi(w1, "use_web_auth_token"))
login_config.use_web_auth_token = config_switch(w2);
else if (!strcmpi(w1, "disable_webtoken_delay"))
login_config.disable_webtoken_delay = cap_value(atoi(w2), 0, INT_MAX);
else if(!strcmpi(w1, "client_hash")) {
int group = 0;
char md5[33];
@@ -761,6 +763,7 @@ void login_set_defaults() {
login_config.vip_sys.group = 5;
#endif
login_config.use_web_auth_token = true;
login_config.disable_webtoken_delay = 10000;

//other default conf
safestrncpy(login_config.loginconf_name, "conf/login_athena.conf", sizeof(login_config.loginconf_name));
@@ -112,6 +112,7 @@ struct Login_Config {
} vip_sys;
#endif
bool use_web_auth_token; /// Enable web authentication token system
int disable_webtoken_delay; /// delay disabling web token after char logs off in milliseconds
};
extern struct Login_Config login_config;

@@ -289,7 +289,7 @@ int logchrif_parse_reqchangemail(int fd, int id, char* ip){
safestrncpy(acc.email, new_email, 40);
ShowNotice("Char-server '%s': Modify an e-mail on an account (@email GM command) (account: %d (%s), new e-mail: %s, ip: %s).\n", ch_server[id].name, account_id, acc.userid, new_email, ip);
// Save
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);
}
}
return 1;
@@ -324,7 +324,7 @@ int logchrif_parse_requpdaccstate(int fd, int id, char* ip){

acc.state = state;
// Save
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);

// notify other servers
if (state != 0){
@@ -381,7 +381,7 @@ int logchrif_parse_reqbanacc(int fd, int id, char* ip){
acc.unban_time = timestamp;

// Save
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);

WBUFW(buf,0) = 0x2731;
WBUFL(buf,2) = account_id;
@@ -423,7 +423,7 @@ int logchrif_parse_reqchgsex(int fd, int id, char* ip){

acc.sex = sex;
// Save
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);

// announce to other servers
WBUFW(buf,0) = 0x2723;
@@ -483,7 +483,7 @@ int logchrif_parse_requnbanacc(int fd, int id, char* ip){
else{
ShowNotice("Char-server '%s': UnBan request (account: %d, ip: %s).\n", ch_server[id].name, account_id, ip);
acc.unban_time = 0;
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);
}
}
return 1;
@@ -605,7 +605,7 @@ int logchrif_parse_updpincode(int fd){
if( accounts->load_num(accounts, &acc, RFIFOL(fd,4) ) ){
strncpy( acc.pincode, RFIFOCP(fd,8), PINCODE_LENGTH+1 );
acc.pincode_change = time( NULL );
accounts->save(accounts, &acc);
accounts->save(accounts, &acc, false);
}
RFIFOSKIP(fd,8 + PINCODE_LENGTH+1);
}
@@ -689,7 +689,7 @@ int logchrif_parse_reqvipdata(int fd) {
acc.char_slots = login_config.char_per_account;
}
acc.vip_time = vip_time;
accounts->save(accounts,&acc);
accounts->save(accounts,&acc, false);
if( flag&1 )
logchrif_sendvipdata(fd,&acc,((isvip)?0x1:0)|((flag&0x8)?0x4:0),mapfd);
}
@@ -9963,8 +9963,6 @@ static const struct _battle_data {
{ "vip_disp_rate", &battle_config.vip_disp_rate, 1, 0, 1, },
{ "mon_trans_disable_in_gvg", &battle_config.mon_trans_disable_in_gvg, 0, 0, 1, },
{ "homunculus_S_growth_level", &battle_config.hom_S_growth_level, 99, 0, MAX_LEVEL, },
{ "emblem_woe_change", &battle_config.emblem_woe_change, 0, 0, 1, },
{ "emblem_transparency_limit", &battle_config.emblem_transparency_limit, 80, 0, 100, },
{ "discount_item_point_shop", &battle_config.discount_item_point_shop, 0, 0, 3, },
{ "update_enemy_position", &battle_config.update_enemy_position, 0, 0, 1, },
{ "devotion_rdamage", &battle_config.devotion_rdamage, 0, 0, 100, },
@@ -557,8 +557,6 @@ struct Battle_Config
int vip_exp_penalty_job;
int vip_disp_rate;
int mon_trans_disable_in_gvg;
int emblem_woe_change;
int emblem_transparency_limit;
int discount_item_point_shop;
int update_enemy_position;
int devotion_rdamage;
@@ -14290,7 +14290,7 @@ void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
enum e_result_validate_emblem { // Used as Result for clif_validate_emblem
EMBVALIDATE_SUCCESS = 0,
EMBVALIDATE_ERR_RAW_FILEFORMAT, // Invalid File Format (Error in zlib/decompression or malformed BMP header)
EMBVALIDATE_ERR_TRANSPARENCY // uploaded emblem does not met the requirements of battle_config.emblem_transparency_limit
EMBVALIDATE_ERR_TRANSPARENCY // uploaded emblem does not met the requirements of inter_config.emblem_transparency_limit
};

static enum e_result_validate_emblem clif_validate_emblem(const uint8* emblem, unsigned long emblem_len)
@@ -14306,15 +14306,15 @@ static enum e_result_validate_emblem clif_validate_emblem(const uint8* emblem, u
))
return EMBVALIDATE_ERR_RAW_FILEFORMAT;

if(battle_config.emblem_transparency_limit != 100) {
if(inter_config.emblem_transparency_limit != 100) {
int i, transcount = 1, tmp[3];
for(i = offset; i < buf_len-1; i++) {
int j = i%3;
tmp[j] = RBUFL(buf,i);
if(j == 2 && (tmp[0] == 0xFFFF00FF) && (tmp[1] == 0xFFFF00) && (tmp[2] == 0xFF00FFFF)) //if pixel is transparent
transcount++;
}
if(((transcount*300)/(buf_len-offset)) > battle_config.emblem_transparency_limit) //convert in % to chk
if(((transcount*300)/(buf_len-offset)) > inter_config.emblem_transparency_limit) //convert in % to chk
return EMBVALIDATE_ERR_TRANSPARENCY;
}

@@ -14333,7 +14333,7 @@ void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd){
if( !emblem_len || !sd->state.gmaster_flag )
return;

if(!(battle_config.emblem_woe_change) && is_agit_start() ){
if(!(inter_config.emblem_woe_change) && is_agit_start() ){
clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,385),false,SELF); //"You not allowed to change emblem during woe"
return;
}
@@ -14344,7 +14344,7 @@ void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd){
return;
} else if(emb_val == EMBVALIDATE_ERR_TRANSPARENCY){
char output[128];
safesnprintf(output,sizeof(output),msg_txt(sd,387),battle_config.emblem_transparency_limit);
safesnprintf(output,sizeof(output),msg_txt(sd,387),inter_config.emblem_transparency_limit);
clif_messagecolor(&sd->bl,color_table[COLOR_RED],output,false,SELF); //"The chosen emblem was detected invalid as it contain too much transparency (limit=%d)\n"
return;
}
@@ -14367,7 +14367,7 @@ void clif_parse_GuildChangeEmblem2(int fd, struct map_session_data* sd) {
if (!sd->state.gmaster_flag)
return;

if (!battle_config.emblem_woe_change && is_agit_start()) {
if (!inter_config.emblem_woe_change && is_agit_start()) {
clif_messagecolor(&sd->bl, color_table[COLOR_RED], msg_txt(sd, 385), false, SELF); //"You not allowed to change emblem during woe"
return;
}
@@ -98,7 +98,8 @@ char log_db_pw[32] = "";
char log_db_db[32] = "log";
Sql* logmysql_handle;

uint32 start_status_points = 48;
// inter config
struct inter_conf inter_config {};

// DBMap declaration
static DBMap* id_db=NULL; /// int id -> struct block_list*
@@ -4208,8 +4209,16 @@ int inter_config_read(const char *cfgName)
safestrncpy(log_db_db, w2, sizeof(log_db_db));
else
if(strcmpi(w1,"start_status_points")==0)
start_status_points=atoi(w2);
inter_config.start_status_points=atoi(w2);
else
if(strcmpi(w1, "emblem_woe_change")==0)
inter_config.emblem_woe_change = config_switch(w2) == 1;
else
if (strcmpi(w1, "emblem_transparency_limit") == 0) {
auto val = atoi(w2);
val = cap_value(val, 0, 100);
inter_config.emblem_transparency_limit = val;
}
if( mapreg_config_read(w1,w2) )
continue;
//support the import command, just like any other config
@@ -5120,6 +5129,11 @@ int do_init(int argc, char *argv[])
map_default.x = 156;
map_default.y = 191;

// default inter_config
inter_config.start_status_points = 48;
inter_config.emblem_woe_change = true;
inter_config.emblem_transparency_limit = 80;

cli_get_options(argc,argv);

rnd_init();
@@ -824,6 +824,14 @@ struct map_data_other_server {
uint16 port;
};

struct inter_conf {
uint32 start_status_points;
bool emblem_woe_change;
uint32 emblem_transparency_limit;
};

extern struct inter_conf inter_config;

int map_getcell(int16 m,int16 x,int16 y,cell_chk cellchk);
int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk);
void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag);
@@ -838,8 +846,6 @@ extern int16 save_settings;
extern int night_flag; // 0=day, 1=night [Yor]
extern int enable_spy; //Determines if @spy commands are active.

extern uint32 start_status_points;

// Agit Flags
extern bool agit_flag;
extern bool agit2_flag;
@@ -13580,17 +13580,17 @@ void PlayerStatPointDatabase::loadingFinished(){
level_one = std::make_shared<s_statpoint_entry>();

level_one->level = 1;
level_one->statpoints = start_status_points;
level_one->statpoints = inter_config.start_status_points;
level_one->traitpoints = 0;

this->put( 1, level_one );
}else if( battle_config.use_statpoint_table ){
if( level_one->statpoints != start_status_points ){
ShowError( "Status points for Level 1 (=%u) do not match inter_athena.conf value (=%u).\n", level_one->statpoints, start_status_points );
level_one->statpoints = start_status_points;
if( level_one->statpoints != inter_config.start_status_points ){
ShowError( "Status points for Level 1 (=%u) do not match inter_athena.conf value (=%u).\n", level_one->statpoints, inter_config.start_status_points );
level_one->statpoints = inter_config.start_status_points;
}
}else{
level_one->statpoints = start_status_points;
level_one->statpoints = inter_config.start_status_points;
level_one->traitpoints = 0;
}

@@ -0,0 +1,41 @@
#
# setup
#
set( WEB_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "" )


#
# web server
#
if ( NOT ENABLE_WEB_SERVER )
return()
endif ( NOT ENABLE_WEB_SERVER )

if( BUILD_SERVERS AND ENABLE_WEB_SERVER )
message( STATUS "Creating target web-server" )
file(GLOB WEB_HEADERS ${WEB_SOURCE_DIR}/*.hpp)
file(GLOB WEB_SOURCES ${WEB_SOURCE_DIR}/*.cpp)
set( DEPENDENCIES common yaml-cpp httplib)
set( LIBRARIES ${GLOBAL_LIBRARIES})
set( INCLUDE_DIRS ${GLOBAL_INCLUDE_DIRS} ${COMMON_BASE_INCLUDE_DIRS} ${HTTPLIB_INCLUDE_DIRS})
set( DEFINITIONS "${GLOBAL_DEFINITIONS} ${COMMON_BASE_DEFINITIONS}" )
set( SOURCE_FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} ${WEB_HEADERS} ${WEB_SOURCES} )
source_group( common FILES ${COMMON_BASE_HEADERS} ${COMMON_HEADERS} )
source_group( web FILES ${WEB_HEADERS} ${WEB_SOURCES} )
include_directories( ${INCLUDE_DIRS} )

#message( STATUS "web-server SOURCE_FILES=${SOURCE_FILES}")
add_executable( web-server ${SOURCE_FILES} )
#message( STATUS "web-server LIBRARIES=${LIBRARIES}, DEPENDENCIES=${DEPENDENCIES} DEFINITIONS=${DEFINITIONS}")
add_dependencies( web-server ${DEPENDENCIES} )
target_link_libraries( web-server ${LIBRARIES} ${DEPENDENCIES} )
set_target_properties( web-server PROPERTIES COMPILE_FLAGS "${DEFINITIONS}" )
if( INSTALL_COMPONENT_RUNTIME )
cpack_add_component( Runtime_webserver DESCRIPTION "web-server" DISPLAY_NAME "web-server" GROUP Runtime )
install( TARGETS web-server
DESTINATION "."
COMPONENT Runtime_webserver )
endif( INSTALL_COMPONENT_RUNTIME )
set( TARGET_LIST ${TARGET_LIST} web-server CACHE INTERNAL "" )
message( STATUS "Creating target web-server - done" )
endif( BUILD_SERVERS AND ENABLE_WEB_SERVER)
@@ -0,0 +1,93 @@

COMMON_H = $(shell ls ../common/*.hpp)
COMMON_AR = ../common/obj/common.a
COMMON_INCLUDE = -I../common

LIBCONFIG_H = $(shell ls ../../3rdparty/libconfig/*.h)
LIBCONFIG_AR = ../../3rdparty/libconfig/obj/libconfig.a
LIBCONFIG_INCLUDE = -I../../3rdparty/libconfig

RAPIDYAML_OBJ = $(shell find ../../3rdparty/rapidyaml/ -type f -name "*.cpp" | sed -e "s/\.cpp/\.o/g" )
RAPIDYAML_DIR_OBJ = $(RAPIDYAML_OBJ:%=obj/%)
RAPIDYAML_AR = ../../3rdparty/rapidyaml/obj/ryml.a
RAPIDYAML_H = $(shell find ../../3rdparty/rapidyaml/ -type f -name "*.h*")
RAPIDYAML_INCLUDE = -I../../3rdparty/rapidyaml/src -I../../3rdparty/rapidyaml/ext/c4core/src

HTTPLIB_OBJ = $(shell find ../../3rdparty/httplib/ -type f -name "*.cc" | sed -e "s/\.cc/\.o/g" )
HTTPLIB_DIR_OBJ = $(HTTPLIB_OBJ:%=obj/%)
HTTPLIB_AR = ../../3rdparty/httplib/obj/httplib.a
HTTPLIB_H = $(shell find ../../3rdparty/httplib/ -type f -name "*.h")
HTTPLIB_INCLUDE = -I../../3rdparty/httplib

WEB_OBJ = $(shell ls *.cpp | sed -e "s/\.cpp/\.o/g")
WEB_DIR_OBJ = $(WEB_OBJ:%=obj/%)
WEB_H = $(shell ls *.hpp)


SERVER_DEPENDS=web-server

HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
SERVER_DEPENDS=web-server
else
SERVER_DEPENDS=needs_mysql
endif

ALL_DEPENDS=server

@SET_MAKE@

#####################################################################
.PHONY :all server clean help

all: $(ALL_DEPENDS)

server: $(SERVER_DEPENDS)

clean:
@echo " CLEAN web"
@rm -rf *.o obj ../../@OWEB@@EXEEXT@

help:
@echo "possible targets are 'server' 'all' 'clean' 'help'"
@echo "'server' - web server"
@echo "'all' - builds all above targets"
@echo "'clean' - cleans builds and objects"
@echo "'help' - outputs this message"

#####################################################################

needs_mysql:
@echo "MySQL not found or disabled by the configure script"
@exit 1

# object directories

obj:
@echo " MKDIR obj"
@-mkdir obj

#executables

web-server: obj $(WEB_DIR_OBJ) $(COMMON_AR) $(LIBCONFIG_AR) $(RAPIDYAML_AR) $(HTTPLIB_AR)
@echo " LD @OWEB@@EXEEXT@"
@@CXX@ @LDFLAGS@ -o ../../@OWEB@@EXEEXT@ $(WEB_DIR_OBJ) $(COMMON_AR) $(LIBCONFIG_AR) $(RAPIDYAML_AR) $(HTTPLIB_AR) @LIBS@ @MYSQL_LIBS@

# web object files

obj/%.o: %.cpp $(WEB_H) $(COMMON_H) $(LIBCONFIG_H) $(HTTPLIB_H) $(RAPIDYAML_H)
@echo " CXX $<"
@@CXX@ @CXXFLAGS@ $(COMMON_INCLUDE) $(LIBCONFIG_INCLUDE) $(HTTPLIB_INCLUDE) $(RAPIDYAML_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $<

# missing object files
$(COMMON_AR):
@$(MAKE) -C ../common server

$(LIBCONFIG_AR):
@$(MAKE) -C ../../3rdparty/libconfig

$(RAPIDYAML_AR):
@$(MAKE) -C ../../3rdparty/rapidyaml

$(HTTPLIB_AR):
@$(MAKE) -C ../../3rdparty/httplib
Copy link
Contributor

@aleos89 aleos89 Apr 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@$(MAKE) -C ../../3rdparty/httplib
@$(MAKE) -C ../../3rdparty/httplib

Missing end of file space.

@@ -0,0 +1,91 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "auth.hpp"

#include <string.h>

#include "../common/showmsg.hpp"
#include "../common/sql.hpp"

#include "http.hpp"
#include "sqllock.hpp"
#include "web.hpp"


bool isAuthorized(const Request &request, bool checkGuildLeader) {
if (!request.has_file("AuthToken") || !request.has_file("AID"))
return false;

if (checkGuildLeader && !request.has_file("GDID"))
return false;

auto token_str = request.get_file_value("AuthToken").content;
auto token = token_str.c_str();
auto account_id = std::stoi(request.get_file_value("AID").content);

SQLLock loginlock(LOGIN_SQL_LOCK);

loginlock.lock();

auto handle = loginlock.getHandle();

SqlStmt * stmt = SqlStmt_Malloc(handle);

if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `account_id` FROM `%s` WHERE (`account_id` = ? AND `web_auth_token` = ? AND `web_auth_token_enabled` = '1')",
login_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)token, strlen(token))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
loginlock.unlock();
return false;
}

if (SqlStmt_NumRows(stmt) <= 0) {
ShowWarning("Request with AID %d and token %s unverified\n", account_id, token);
SqlStmt_Free(stmt);
loginlock.unlock();
return false;
}

SqlStmt_Free(stmt);
loginlock.unlock();
if (!checkGuildLeader) {
// we're done, auth ok
return true;
}

auto guild_id = std::stoi(request.get_file_value("GDID").content);

SQLLock charlock(CHAR_SQL_LOCK);
charlock.lock();
handle = charlock.getHandle();
stmt = SqlStmt_Malloc(handle);

if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `account_id` FROM `%s` LEFT JOIN `%s` using (`char_id`) WHERE (`%s`.`account_id` = ? AND `%s`.`guild_id` = ?) LIMIT 1",
guild_db_table, char_db_table, char_db_table, guild_db_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &guild_id, sizeof(guild_id))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
charlock.unlock();
return false;
}

if (SqlStmt_NumRows(stmt) <= 0) {
ShowDebug("Request with AID %d GDID %d and token %s unverified\n", account_id, guild_id, token);
SqlStmt_Free(stmt);
charlock.unlock();
return false;
}
SqlStmt_Free(stmt);
charlock.unlock();
return true;
}
@@ -0,0 +1,11 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef AUTH_HPP
#define AUTH_HPP

#include "http.hpp"

bool isAuthorized(const Request &request, bool checkGuildLeader=false);

#endif
@@ -0,0 +1,162 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "charconfig_controller.hpp"

#include <string>

#include "../common/showmsg.hpp"
#include "../common/sql.hpp"

#include "auth.hpp"
#include "http.hpp"
#include "sqllock.hpp"
#include "webutils.hpp"
#include "web.hpp"

HANDLER_FUNC(charconfig_save) {
if (!isAuthorized(req, false)) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto account_id = std::stoi(req.get_file_value("AID").content);
auto char_id = std::stoi(req.get_file_value("GID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
std::string data;

if (req.has_file("data")) {
data = req.get_file_value("data").content;
addToJsonObject(data, "\"Type\": 1");
} else {
data = "{\"Type\": 1}";
}
SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `account_id` FROM `%s` WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ?) LIMIT 1",
char_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"INSERT INTO `%s` (`account_id`, `char_id`, `world_name`, `data`) VALUES (?, ?, ?, ?)",
char_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
} else {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"UPDATE `%s` SET `data` = ? WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ?)",
char_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
}
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a stupid thought: REPLACE INTO?

Copy link
Member Author

@vstumpf vstumpf Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think INSERT INTO ... ON DUPLICATE KEY UPDATE ... is the preferred method now, I'll use that.


SqlStmt_Free(stmt);
sl.unlock();
res.set_content(data, "application/json");
}

HANDLER_FUNC(charconfig_load) {
if (!req.has_file("AID") || !req.has_file("WorldName")) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

// TODO: Figure out when client sends AuthToken for this path, then add packetver check
// if (!isAuthorized(req)) {
// ShowError("Not authorized!\n");
// message.reply(web::http::status_codes::Forbidden);
// return;
// }

auto account_id = std::stoi(req.get_file_value("AID").content);
auto char_id = std::stoi(req.get_file_value("GID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();

SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `data` FROM `%s` WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ?) LIMIT 1",
char_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
SqlStmt_Free(stmt);
ShowDebug("[AccountID: %d, CharID: %d, World: \"%s\"] Not found in table, sending new info.\n", account_id, char_id, world_name);
sl.unlock();
res.set_content("{\"Type\": 1}", "application/json");
return;
}

char databuf[SQL_BUFFER_SIZE];

if (SQL_SUCCESS != SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &databuf, sizeof(databuf), NULL, NULL)
|| SQL_SUCCESS != SqlStmt_NextRow(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

SqlStmt_Free(stmt);
sl.unlock();

databuf[sizeof(databuf) - 1] = 0;
res.set_content(databuf, "application/json");
}
@@ -0,0 +1,12 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef CHARCONFIG_CONTROLLER_HPP
#define CHARCONFIG_CONTROLLER_HPP

#include "http.hpp"

HANDLER_FUNC(charconfig_save);
HANDLER_FUNC(charconfig_load);

#endif
@@ -0,0 +1,287 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "emblem_controller.hpp"

#include <fstream>
#include <iostream>
#include <ostream>

#include "../common/showmsg.hpp"
#include "../common/socket.hpp"

#include "auth.hpp"
#include "http.hpp"
#include "sqllock.hpp"
#include "web.hpp"

// Max size is 50kb for gif
#define MAX_EMBLEM_SIZE 50000
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be moved somewhere else i guess.
Old BMP limit is 2048 it seems.

#define START_VERSION 1

HANDLER_FUNC(emblem_download) {
if (!isAuthorized(req, false)) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

bool fail = false;
if (!req.has_file("GDID")) {
ShowDebug("Missing GuildID field for emblem download.\n");
fail = true;
}
if (!req.has_file("WorldName")) {
ShowDebug("Missing WorldName field for emblem download.\n");
fail = true;
}
if (fail) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
auto guild_id = std::stoi(req.get_file_value("GDID").content);

SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `version`, `file_type`, `file_data` FROM `%s` WHERE (`guild_id` = ? AND `world_name` = ?)",
guild_emblems_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &guild_id, sizeof(guild_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

uint32 version = 0;
char filetype[256];
char blob[MAX_EMBLEM_SIZE]; // yikes
uint32 emblem_size;

if (SqlStmt_NumRows(stmt) <= 0) {
SqlStmt_Free(stmt);
ShowError("[GuildID: %d / World: \"%s\"] Not found in table\n", guild_id, world_name);
sl.unlock();
res.status = 404;
res.set_content("Error", "text/plain");
return;
}


if (SQL_SUCCESS != SqlStmt_BindColumn(stmt, 0, SQLDT_UINT32, &version, sizeof(version), nullptr, nullptr)
|| SQL_SUCCESS != SqlStmt_BindColumn(stmt, 1, SQLDT_STRING, &filetype, sizeof(filetype), nullptr, nullptr)
|| SQL_SUCCESS != SqlStmt_BindColumn(stmt, 2, SQLDT_BLOB, &blob, MAX_EMBLEM_SIZE, &emblem_size, nullptr)
|| SQL_SUCCESS != SqlStmt_NextRow(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

SqlStmt_Free(stmt);
sl.unlock();

if (emblem_size > MAX_EMBLEM_SIZE) {
ShowDebug("Emblem is too big, current size is %d and the max length is %d.\n", emblem_size, MAX_EMBLEM_SIZE);
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
const char * content_type;
if (!strcmp(filetype, "BMP"))
content_type = "image/bmp";
else if (!strcmp(filetype, "GIF"))
content_type = "image/gif";
Copy link
Contributor

@aleos89 aleos89 Apr 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
content_type = "image/gif";
content_type = "image/gif";
else {
ShowError("Invalid image type %s, rejecting!\n", filetype);
res.status = 404;
res.set_content("Error", "text/plain");
return;
}

else {
ShowError("Invalid image type %s, rejecting!\n", filetype);
res.status = 404;
res.set_content("Error", "text/plain");
return;
}

res.body.assign(blob, emblem_size);
res.set_header("Content-Type", content_type);
}


HANDLER_FUNC(emblem_upload) {
if (!isAuthorized(req, true)) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

bool fail = false;
if (!req.has_file("GDID")) {
ShowDebug("Missing GuildID field for emblem upload.\n");
fail = true;
}
if (!req.has_file("WorldName")) {
ShowDebug("Missing WorldName field for emblem upload.\n");
fail = true;
}
if (!req.has_file("Img")) {
ShowDebug("Missing Img field for emblem upload.\n");
fail = true;
}
if (!req.has_file("ImgType")) {
ShowDebug("Missing ImgType for emblem upload.\n");
fail = true;
}
if (fail) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
auto guild_id = std::stoi(req.get_file_value("GDID").content);
auto imgtype_str = req.get_file_value("ImgType").content;
auto imgtype = imgtype_str.c_str();
auto img = req.get_file_value("Img").content;
auto img_cstr = img.c_str();

if (imgtype_str != "BMP" && imgtype_str != "GIF") {
ShowError("Invalid image type %s, rejecting!\n", imgtype);
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto length = img.length();
if (length > MAX_EMBLEM_SIZE) {
ShowDebug("Emblem is too big, current size is %lu and the max length is %d.\n", length, MAX_EMBLEM_SIZE);
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (imgtype_str == "GIF") {
if (!web_config.allow_gifs) {
ShowDebug("Client sent GIF image but GIF image support is disabled.\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
if (img.substr(0, 3) != "GIF") {
ShowDebug("Server received ImgType GIF but received file does not start with \"GIF\" magic header.\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a TODO mark here for the transparency check.

// TODO: transparency check for GIF emblems
}
else if (imgtype_str == "BMP") {
if (length < 14) {
ShowDebug("File size is too short\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
if (img.substr(0, 2) != "BM") {
ShowDebug("Server received ImgType BMP but received file does not start with \"BM\" magic header.\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
if (RBUFL(img_cstr, 2) != length) {
ShowDebug("Bitmap size doesn't match size in file header.\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (inter_config.emblem_transparency_limit < 100) {
uint32 offset = RBUFL(img_cstr, 0x0A);
int i, transcount = 1, tmp[3];
for (i = offset; i < length - 1; i++) {
int j = i % 3;
tmp[j] = RBUFL(img_cstr, i);
if (j == 2 && (tmp[0] == 0xFFFF00FF) && (tmp[1] == 0xFFFF00) && (tmp[2] == 0xFF00FFFF)) //if pixel is transparent
transcount++;
}
if (((transcount * 300) / (length - offset)) > inter_config.emblem_transparency_limit) {
ShowDebug("Bitmap transparency check failed.\n");
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
}
}

SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `version` FROM `%s` WHERE (`guild_id` = ? AND `world_name` = ?)",
guild_emblems_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &guild_id, sizeof(guild_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

uint32 version = START_VERSION;

if (SqlStmt_NumRows(stmt) > 0) {
if (SQL_SUCCESS != SqlStmt_BindColumn(stmt, 0, SQLDT_UINT32, &version, sizeof(version), NULL, NULL)
|| SQL_SUCCESS != SqlStmt_NextRow(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
version += 1;
}

// insert new
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to "reuse" the stmt here without freeing the old one?

Copy link
Member Author

@vstumpf vstumpf Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, SqlStmt_PrepareV frees the statement at the beginning.

"REPLACE INTO `%s` (`version`, `file_type`, `guild_id`, `world_name`, `file_data`) VALUES (?, ?, ?, ?, ?)",
guild_emblems_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_UINT32, &version, sizeof(version))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)imgtype, strlen(imgtype))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_INT, &guild_id, sizeof(guild_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_BLOB, (void *)img.c_str(), length)
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

SqlStmt_Free(stmt);
sl.unlock();

std::ostringstream stream;
stream << "{\"Type\":1,\"version\":" << version << "}";
res.set_content(stream.str(), "application/json");
}
@@ -0,0 +1,12 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef EMBLEM_CONTROLLER_HPP
#define EMBLEM_CONTROLLER_HPP

#include "http.hpp"

HANDLER_FUNC(emblem_download);
HANDLER_FUNC(emblem_upload);

#endif
@@ -0,0 +1,21 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef HTTP_HPP
#define HTTP_HPP

#ifdef WIN32
#include "../common/winapi.hpp"
#endif

#include <httplib.h>


typedef httplib::Request Request;
typedef httplib::Response Response;

#define HANDLER_FUNC(x) void x (const Request &req, Response &res)
typedef HANDLER_FUNC((*handler_func));
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have loved to see this making use of C++, but we can change that later on.

Copy link
Member Author

@vstumpf vstumpf Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe with classes and static methods, probably a better implementation!



#endif
@@ -0,0 +1,169 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "merchantstore_controller.hpp"

#include <string>

#include "../common/showmsg.hpp"
#include "../common/sql.hpp"

#include "auth.hpp"
#include "http.hpp"
#include "sqllock.hpp"
#include "webutils.hpp"
#include "web.hpp"

HANDLER_FUNC(merchantstore_save) {
if (!isAuthorized(req, false)) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto account_id = std::stoi(req.get_file_value("AID").content);
auto char_id = std::stoi(req.get_file_value("GID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
auto store_type = std::stoi(req.get_file_value("Type").content);
std::string data;

if (req.has_file("data")) {
data = req.get_file_value("data").content;
addToJsonObject(data, "\"Type\": 1");
} else {
data = "{\"Type\": 1}";
}
SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt* stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `account_id` FROM `%s` WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ? AND `store_type` = ?) LIMIT 1",
merchant_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_INT, &store_type, sizeof(store_type))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"INSERT INTO `%s` (`account_id`, `char_id`, `world_name`, `store_type`, `data`) VALUES (?, ?, ?, ?, ?)",
merchant_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_INT, &store_type, sizeof(store_type))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
}
else {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"UPDATE `%s` SET `data` = ? WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ? AND `store_type` = ?)",
merchant_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 4, SQLDT_INT, &store_type, sizeof(store_type))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
}
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again: REPLACE INTO?


SqlStmt_Free(stmt);
sl.unlock();
res.set_content(data, "application/json");
}

HANDLER_FUNC(merchantstore_load) {
if (!req.has_file("AID") || !req.has_file("WorldName")) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

// TODO: Figure out when client sends AuthToken for this path, then add packetver check
// if (!isAuthorized(req)) {
// ShowError("Not authorized!\n");
// message.reply(web::http::status_codes::Forbidden);
// return;
// }

auto account_id = std::stoi(req.get_file_value("AID").content);
auto char_id = std::stoi(req.get_file_value("GID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
auto store_type = std::stoi(req.get_file_value("Type").content);

SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt* stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `data` FROM `%s` WHERE (`account_id` = ? AND `char_id` = ? AND `world_name` = ? AND `store_type` = ?) LIMIT 1",
merchant_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &char_id, sizeof(char_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 3, SQLDT_INT, &store_type, sizeof(store_type))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
SqlStmt_Free(stmt);
ShowDebug("[AccountID: %d, World: \"%s\"] Not found in table, sending new info.\n", account_id, world_name);
sl.unlock();
res.set_content("{\"Type\": 1}", "application/json");
return;
}

char databuf[SQL_BUFFER_SIZE] = { 0 };

if (SQL_SUCCESS != SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &databuf, sizeof(databuf), NULL, NULL)
|| SQL_SUCCESS != SqlStmt_NextRow(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

SqlStmt_Free(stmt);
sl.unlock();

databuf[sizeof(databuf) - 1] = 0;
res.set_content(databuf, "application/json");
}
@@ -0,0 +1,12 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef MERCHANTSTORE_CONTROLLER_HPP
#define MERCHANTSTORE_CONTROLLER_HPP

#include "http.hpp"

HANDLER_FUNC(merchantstore_save);
HANDLER_FUNC(merchantstore_load);

#endif
@@ -0,0 +1,71 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "sqllock.hpp"

#include <mutex>

#include "../common/showmsg.hpp"

std::mutex dbmutex;

extern Sql * login_handle;
extern Sql * char_handle;
extern Sql * web_handle;


SQLLock::SQLLock(locktype lt) : ulock(dbmutex, std::defer_lock), lt(lt) {
switch(lt) {
case LOGIN_SQL_LOCK:
handle = login_handle;
break;
case CHAR_SQL_LOCK:
handle = char_handle;
break;
case WEB_SQL_LOCK:
handle = web_handle;
break;
}
}

void SQLLock::lock() {
// switch(lt) {
// case LOGIN_SQL_LOCK:
// ShowDebug("Locking login sql\n");
// break;
// case CHAR_SQL_LOCK:
// ShowDebug("Locking char sql\n");
// break;
// case WEB_SQL_LOCK:
// ShowDebug("Locking web sql\n");
// break;
// }
ulock.lock();
}

void SQLLock::unlock() {
ulock.unlock();
// switch(lt) {
// case LOGIN_SQL_LOCK:
// ShowDebug("Unlocked login sql\n");
// break;
// case CHAR_SQL_LOCK:
// ShowDebug("Unlocked char sql\n");
// break;
// case WEB_SQL_LOCK:
// ShowDebug("Unlocked web sql\n");
// break;
// }

}


// can only get handle if locked
Sql * SQLLock::getHandle() {
if (!ulock.owns_lock())
return nullptr;
return handle;
}

SQLLock::~SQLLock() {
}
@@ -0,0 +1,32 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef SQLLOCK_HPP
#define SQLLOCK_HPP

#include <mutex>

#include "../common/sql.hpp"

enum locktype {
LOGIN_SQL_LOCK,
CHAR_SQL_LOCK,
WEB_SQL_LOCK
};

class SQLLock {
private:
std::unique_lock<std::mutex> ulock;
Sql * handle;
locktype lt;

public:
SQLLock(locktype);
~SQLLock();
void lock();
void unlock();
Sql * getHandle();
};


#endif
@@ -0,0 +1,156 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "userconfig_controller.hpp"

#include <string>

#include "../common/showmsg.hpp"
#include "../common/sql.hpp"

#include "auth.hpp"
#include "http.hpp"
#include "sqllock.hpp"
#include "webutils.hpp"
#include "web.hpp"

HANDLER_FUNC(userconfig_save) {
if (!isAuthorized(req, false)) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

auto account_id = std::stoi(req.get_file_value("AID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();
std::string data;

if (req.has_file("data")) {
data = req.get_file_value("data").content;
addToJsonObject(data, "\"Type\": 1");
} else {
data = "{\"Type\": 1}";
}
SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `account_id` FROM `%s` WHERE (`account_id` = ? AND `world_name` = ?) LIMIT 1",
user_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"INSERT INTO `%s` (`account_id`, `world_name`, `data`) VALUES (?, ?, ?)",
user_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
} else {
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"UPDATE `%s` SET `data` = ? WHERE (`account_id` = ? AND `world_name` = ?)",
user_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void *)data.c_str(), strlen(data.c_str()))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}
}
Copy link
Member

@Lemongrass3110 Lemongrass3110 Mar 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REPLACE INTO?


SqlStmt_Free(stmt);
sl.unlock();
res.set_content(data, "application/json");
}

HANDLER_FUNC(userconfig_load) {
if (!req.has_file("AID") || !req.has_file("WorldName")) {
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

// TODO: Figure out when client sends AuthToken for this path, then add packetver check
// if (!isAuthorized(req)) {
// ShowError("Not authorized!\n");
// message.reply(web::http::status_codes::Forbidden);
// return;
// }

auto account_id = std::stoi(req.get_file_value("AID").content);
auto world_name_str = req.get_file_value("WorldName").content;
auto world_name = world_name_str.c_str();

SQLLock sl(WEB_SQL_LOCK);
sl.lock();
auto handle = sl.getHandle();
SqlStmt * stmt = SqlStmt_Malloc(handle);
if (SQL_SUCCESS != SqlStmt_Prepare(stmt,
"SELECT `data` FROM `%s` WHERE (`account_id` = ? AND `world_name` = ?) LIMIT 1",
user_configs_table)
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, &account_id, sizeof(account_id))
|| SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void *)world_name, strlen(world_name))
|| SQL_SUCCESS != SqlStmt_Execute(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

if (SqlStmt_NumRows(stmt) <= 0) {
SqlStmt_Free(stmt);
ShowDebug("[AccountID: %d, World: \"%s\"] Not found in table, sending new info.\n", account_id, world_name);
sl.unlock();
res.set_content("{\"Type\": 1}", "application/json");
return;
}

char databuf[SQL_BUFFER_SIZE];

if (SQL_SUCCESS != SqlStmt_BindColumn(stmt, 0, SQLDT_STRING, &databuf, sizeof(databuf), NULL, NULL)
|| SQL_SUCCESS != SqlStmt_NextRow(stmt)
) {
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
sl.unlock();
res.status = 400;
res.set_content("Error", "text/plain");
return;
}

SqlStmt_Free(stmt);
sl.unlock();

databuf[sizeof(databuf) - 1] = 0;
res.set_content(databuf, "application/json");
}
@@ -0,0 +1,12 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#ifndef USERCONFIG_CONTROLLER_HPP
#define USERCONFIG_CONTROLLER_HPP

#include "http.hpp"

HANDLER_FUNC(userconfig_save);
HANDLER_FUNC(userconfig_load);

#endif