forked from metin2/server
Add project files.
This commit is contained in:
parent
453a74459f
commit
f4f90b2533
|
@ -0,0 +1,43 @@
|
|||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
# Build mode debug/release?
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
# Set C++ standard to C++11
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
project("Metin2 Server")
|
||||
|
||||
set(BINARY_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
# Set build directory
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
# Set the CMake module directory
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/Modules/")
|
||||
|
||||
# Set environment variables
|
||||
set(METIN2_OS_NAME ${CMAKE_SYSTEM})
|
||||
set(METIN2_COMPILER_NAME ${CMAKE_CXX_COMPILER_ID})
|
||||
set(METIN2_CPU_TARGET ${CMAKE_SYSTEM_PROCESSOR})
|
||||
|
||||
# Git revision
|
||||
include(FindGit)
|
||||
find_package(Git)
|
||||
|
||||
set(METIN2_REVISION "unknown")
|
||||
|
||||
message(STATUS "Current revision is ${METIN2_REVISION}")
|
||||
|
||||
# Set the global include directories
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_subdirectory(libgame)
|
||||
add_subdirectory(liblua)
|
||||
add_subdirectory(libpoly)
|
||||
add_subdirectory(libsql)
|
||||
add_subdirectory(libthecore)
|
||||
add_subdirectory(game)
|
||||
add_subdirectory(db)
|
||||
add_subdirectory(quest)
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "x64-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"inheritEnvironments": [ "msvc_x64_x64" ],
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": ""
|
||||
},
|
||||
{
|
||||
"name": "WSL-GCC-Debug",
|
||||
"generator": "Ninja",
|
||||
"configurationType": "Debug",
|
||||
"buildRoot": "${projectDir}\\out\\build\\${name}",
|
||||
"installRoot": "${projectDir}\\out\\install\\${name}",
|
||||
"cmakeExecutable": "cmake",
|
||||
"cmakeCommandArgs": "",
|
||||
"buildCommandArgs": "",
|
||||
"ctestCommandArgs": "",
|
||||
"inheritEnvironments": [ "linux_x64" ],
|
||||
"wslPath": "${defaultWSLPath}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
# Module for locating the Crypto++ encryption library.
|
||||
#
|
||||
# Customizable variables:
|
||||
# CRYPTOPP_ROOT_DIR
|
||||
# This variable points to the CryptoPP root directory. On Windows the
|
||||
# library location typically will have to be provided explicitly using the
|
||||
# -D command-line option. The directory should include the include/cryptopp,
|
||||
# lib and/or bin sub-directories.
|
||||
#
|
||||
# Read-only variables:
|
||||
# CRYPTOPP_FOUND
|
||||
# Indicates whether the library has been found.
|
||||
#
|
||||
# CRYPTOPP_INCLUDE_DIRS
|
||||
# Points to the CryptoPP include directory.
|
||||
#
|
||||
# CRYPTOPP_LIBRARIES
|
||||
# Points to the CryptoPP libraries that should be passed to
|
||||
# target_link_libararies.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2012 Sergiu Dotenco
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
INCLUDE (FindPackageHandleStandardArgs)
|
||||
|
||||
FIND_PATH (CRYPTOPP_ROOT_DIR
|
||||
NAMES cryptopp/cryptlib.h include/cryptopp/cryptlib.h
|
||||
PATHS ENV CRYPTOPPROOT
|
||||
DOC "CryptoPP root directory")
|
||||
|
||||
# Re-use the previous path:
|
||||
FIND_PATH (CRYPTOPP_INCLUDE_DIR
|
||||
NAMES cryptopp/cryptlib.h
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES include
|
||||
DOC "CryptoPP include directory")
|
||||
|
||||
FIND_LIBRARY (CRYPTOPP_LIBRARY_DEBUG
|
||||
NAMES cryptlibd cryptoppd
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES lib
|
||||
DOC "CryptoPP debug library")
|
||||
|
||||
FIND_LIBRARY (CRYPTOPP_LIBRARY_RELEASE
|
||||
NAMES cryptlib cryptopp
|
||||
HINTS ${CRYPTOPP_ROOT_DIR}
|
||||
PATH_SUFFIXES lib
|
||||
DOC "CryptoPP release library")
|
||||
|
||||
IF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE)
|
||||
SET (CRYPTOPP_LIBRARY
|
||||
optimized ${CRYPTOPP_LIBRARY_RELEASE}
|
||||
debug ${CRYPTOPP_LIBRARY_DEBUG} CACHE DOC "CryptoPP library")
|
||||
ELSEIF (CRYPTOPP_LIBRARY_RELEASE)
|
||||
SET (CRYPTOPP_LIBRARY ${CRYPTOPP_LIBRARY_RELEASE} CACHE DOC
|
||||
"CryptoPP library")
|
||||
ENDIF (CRYPTOPP_LIBRARY_DEBUG AND CRYPTOPP_LIBRARY_RELEASE)
|
||||
|
||||
IF (CRYPTOPP_INCLUDE_DIR)
|
||||
SET (_CRYPTOPP_VERSION_HEADER ${CRYPTOPP_INCLUDE_DIR}/cryptopp/config.h)
|
||||
|
||||
IF (EXISTS ${_CRYPTOPP_VERSION_HEADER})
|
||||
FILE (STRINGS ${_CRYPTOPP_VERSION_HEADER} _CRYPTOPP_VERSION_TMP REGEX
|
||||
"^#define CRYPTOPP_VERSION[ \t]+[0-9]+$")
|
||||
|
||||
STRING (REGEX REPLACE
|
||||
"^#define CRYPTOPP_VERSION[ \t]+([0-9]+)" "\\1" _CRYPTOPP_VERSION_TMP
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
|
||||
STRING (REGEX REPLACE "([0-9]+)[0-9][0-9]" "\\1" CRYPTOPP_VERSION_MAJOR
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
STRING (REGEX REPLACE "[0-9]([0-9])[0-9]" "\\1" CRYPTOPP_VERSION_MINOR
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
STRING (REGEX REPLACE "[0-9][0-9]([0-9])" "\\1" CRYPTOPP_VERSION_PATCH
|
||||
${_CRYPTOPP_VERSION_TMP})
|
||||
|
||||
SET (CRYPTOPP_VERSION_COUNT 3)
|
||||
SET (CRYPTOPP_VERSION
|
||||
${CRYPTOPP_VERSION_MAJOR}.${CRYPTOPP_VERSION_MINOR}.${CRYPTOPP_VERSION_PATCH})
|
||||
ENDIF (EXISTS ${_CRYPTOPP_VERSION_HEADER})
|
||||
ENDIF (CRYPTOPP_INCLUDE_DIR)
|
||||
|
||||
SET (CRYPTOPP_INCLUDE_DIRS ${CRYPTOPP_INCLUDE_DIR})
|
||||
SET (CRYPTOPP_LIBRARIES ${CRYPTOPP_LIBRARY})
|
||||
|
||||
MARK_AS_ADVANCED (CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY CRYPTOPP_LIBRARY_DEBUG
|
||||
CRYPTOPP_LIBRARY_RELEASE)
|
||||
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (CryptoPP REQUIRED_VARS CRYPTOPP_ROOT_DIR
|
||||
CRYPTOPP_INCLUDE_DIR CRYPTOPP_LIBRARY VERSION_VAR CRYPTOPP_VERSION)
|
|
@ -0,0 +1,176 @@
|
|||
################################################################################
|
||||
#
|
||||
# Program: 3D Slicer
|
||||
#
|
||||
# Copyright (c) Kitware Inc.
|
||||
#
|
||||
# See COPYRIGHT.txt
|
||||
# or http://www.slicer.org/copyright/copyright.txt for details.
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc.
|
||||
# and was partially funded by NIH grant 3P41RR013218-12S1
|
||||
#
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# The module defines the following variables:
|
||||
# GIT_EXECUTABLE - path to git command line client
|
||||
# GIT_FOUND - true if the command line client was found
|
||||
# GIT_VERSION_STRING - the version of git found (since CMake 2.8.8)
|
||||
#
|
||||
# If the command line client executable is found the macro
|
||||
# GIT_WC_INFO(<dir> <var-prefix>)
|
||||
# is defined to extract information of a git working copy at
|
||||
# a given location.
|
||||
#
|
||||
# The macro defines the following variables:
|
||||
# <var-prefix>_WC_REVISION_HASH - Current SHA1 hash
|
||||
# <var-prefix>_WC_REVISION - Current SHA1 hash
|
||||
# <var-prefix>_WC_REVISION_NAME - Name associated with <var-prefix>_WC_REVISION_HASH
|
||||
# <var-prefix>_WC_URL - output of command `git config --get remote.origin.url'
|
||||
# <var-prefix>_WC_ROOT - Same value as working copy URL
|
||||
# <var-prefix>_WC_LAST_CHANGED_DATE - date of last commit
|
||||
# <var-prefix>_WC_GITSVN - Set to false
|
||||
#
|
||||
# ... and also the following ones if it's a git-svn repository:
|
||||
# <var-prefix>_WC_GITSVN - Set to True if it is a
|
||||
# <var-prefix>_WC_INFO - output of command `git svn info'
|
||||
# <var-prefix>_WC_URL - url of the associated SVN repository
|
||||
# <var-prefix>_WC_ROOT - root url of the associated SVN repository
|
||||
# <var-prefix>_WC_REVISION - current SVN revision number
|
||||
# <var-prefix>_WC_LAST_CHANGED_AUTHOR - author of last commit
|
||||
# <var-prefix>_WC_LAST_CHANGED_DATE - date of last commit
|
||||
# <var-prefix>_WC_LAST_CHANGED_REV - revision of last commit
|
||||
# <var-prefix>_WC_LAST_CHANGED_LOG - last log of base revision
|
||||
#
|
||||
# Example usage:
|
||||
# find_package(Git)
|
||||
# if(GIT_FOUND)
|
||||
# GIT_WC_INFO(${PROJECT_SOURCE_DIR} Project)
|
||||
# message("Current revision is ${Project_WC_REVISION_HASH}")
|
||||
# message("git found: ${GIT_EXECUTABLE}")
|
||||
# endif()
|
||||
#
|
||||
|
||||
# Look for 'git' or 'eg' (easy git)
|
||||
#
|
||||
set(git_names git eg)
|
||||
|
||||
# Prefer .cmd variants on Windows unless running in a Makefile
|
||||
# in the MSYS shell.
|
||||
#
|
||||
if(WIN32)
|
||||
if(NOT CMAKE_GENERATOR MATCHES "MSYS")
|
||||
# Note: Due to a bug in 'git.cmd' preventing it from returning the exit code of 'git',
|
||||
# we excluded it from the list of executables to search.
|
||||
# See http://code.google.com/p/msysgit/issues/detail?id=428
|
||||
# TODO Check if 'git' exists, get the associated version, if the corresponding version
|
||||
# is known to have a working version of 'git.cmd', use it.
|
||||
set(git_names git eg.cmd eg)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_program(GIT_EXECUTABLE ${git_names}
|
||||
PATHS
|
||||
"C:/Program Files/Git/bin"
|
||||
"C:/Program Files (x86)/Git/bin"
|
||||
DOC "git command line client")
|
||||
# XXX Comment to workaround https://gitlab.kitware.com/cmake/cmake/issues/15448
|
||||
# mark_as_advanced(GIT_EXECUTABLE)
|
||||
|
||||
if(GIT_EXECUTABLE)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} --version
|
||||
OUTPUT_VARIABLE git_version
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (git_version MATCHES "^git version [0-9]")
|
||||
string(REPLACE "git version " "" GIT_VERSION_STRING "${git_version}")
|
||||
endif()
|
||||
unset(git_version)
|
||||
|
||||
macro(GIT_WC_INFO dir prefix)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=7 HEAD
|
||||
WORKING_DIRECTORY ${dir}
|
||||
ERROR_VARIABLE GIT_error
|
||||
OUTPUT_VARIABLE ${prefix}_WC_REVISION_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(${prefix}_WC_REVISION ${${prefix}_WC_REVISION_HASH})
|
||||
if(NOT ${GIT_error} EQUAL 0)
|
||||
message(SEND_ERROR "Command \"${GIT_EXECUTBALE} rev-parse --verify -q --short=7 HEAD\" in directory ${dir} failed with output:\n${GIT_error}")
|
||||
else(NOT ${GIT_error} EQUAL 0)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} name-rev ${${prefix}_WC_REVISION_HASH}
|
||||
WORKING_DIRECTORY ${dir}
|
||||
OUTPUT_VARIABLE ${prefix}_WC_REVISION_NAME
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif(NOT ${GIT_error} EQUAL 0)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url
|
||||
WORKING_DIRECTORY ${dir}
|
||||
OUTPUT_VARIABLE ${prefix}_WC_URL
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} show -s --format="%ci" ${${prefix}_WC_REVISION_HASH}
|
||||
WORKING_DIRECTORY ${dir}
|
||||
OUTPUT_VARIABLE ${prefix}_show_output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
string(REGEX REPLACE "^([0-9][0-9][0-9][0-9]\\-[0-9][0-9]\\-[0-9][0-9]).*"
|
||||
"\\1" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_show_output}")
|
||||
|
||||
set(${prefix}_WC_GITSVN False)
|
||||
|
||||
# Check if this git is likely to be a git-svn repository
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp "^svn-remote"
|
||||
WORKING_DIRECTORY ${dir}
|
||||
OUTPUT_VARIABLE git_config_output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(NOT "${git_config_output}" STREQUAL "")
|
||||
# In case git-svn is used, attempt to extract svn info
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} svn info
|
||||
WORKING_DIRECTORY ${dir}
|
||||
TIMEOUT 3
|
||||
ERROR_VARIABLE git_svn_info_error
|
||||
OUTPUT_VARIABLE ${prefix}_WC_INFO
|
||||
RESULT_VARIABLE git_svn_info_result
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
if(${git_svn_info_result} EQUAL 0)
|
||||
set(${prefix}_WC_GITSVN True)
|
||||
string(REGEX REPLACE "^(.*\n)?URL: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_URL "${${prefix}_WC_INFO}")
|
||||
string(REGEX REPLACE "^(.*\n)?Revision: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_REVISION "${${prefix}_WC_INFO}")
|
||||
string(REGEX REPLACE "^(.*\n)?Repository Root: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_ROOT "${${prefix}_WC_INFO}")
|
||||
string(REGEX REPLACE "^(.*\n)?Last Changed Author: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_LAST_CHANGED_AUTHOR "${${prefix}_WC_INFO}")
|
||||
string(REGEX REPLACE "^(.*\n)?Last Changed Rev: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_LAST_CHANGED_REV "${${prefix}_WC_INFO}")
|
||||
string(REGEX REPLACE "^(.*\n)?Last Changed Date: ([^\n]+).*"
|
||||
"\\2" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_WC_INFO}")
|
||||
endif(${git_svn_info_result} EQUAL 0)
|
||||
endif(NOT "${git_config_output}" STREQUAL "")
|
||||
|
||||
# If there is no 'remote.origin', default to "NA" value and print a warning message.
|
||||
if(NOT ${prefix}_WC_URL)
|
||||
message(WARNING "No remote origin set for git repository: ${dir}" )
|
||||
set( ${prefix}_WC_URL "NA" )
|
||||
else()
|
||||
set(${prefix}_WC_ROOT ${${prefix}_WC_URL})
|
||||
endif()
|
||||
|
||||
endmacro(GIT_WC_INFO)
|
||||
endif(GIT_EXECUTABLE)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE)
|
|
@ -0,0 +1,45 @@
|
|||
# Find liblzo2
|
||||
# LZO_FOUND - system has the LZO library
|
||||
# LZO_INCLUDE_DIR - the LZO include directory
|
||||
# LZO_LIBRARIES - The libraries needed to use LZO
|
||||
|
||||
if (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
||||
# in cache already
|
||||
SET(LZO_FOUND TRUE)
|
||||
else (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
||||
FIND_PATH(LZO_INCLUDE_DIR lzo/lzo1x.h
|
||||
${LZO_ROOT}/include/
|
||||
/usr/include/
|
||||
/usr/local/include/
|
||||
/sw/lib/
|
||||
/sw/local/lib/
|
||||
)
|
||||
|
||||
if(WIN32 AND MSVC)
|
||||
else(WIN32 AND MSVC)
|
||||
FIND_LIBRARY(LZO_LIBRARIES NAMES lzo2
|
||||
PATHS
|
||||
${LZO_ROOT}/lib
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/sw/lib
|
||||
/sw/local/lib
|
||||
)
|
||||
endif(WIN32 AND MSVC)
|
||||
|
||||
if (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
||||
set(LZO_FOUND TRUE)
|
||||
endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
||||
|
||||
if (LZO_FOUND)
|
||||
if (NOT LZO_FIND_QUIETLY)
|
||||
message(STATUS "Found LZO: ${LZO_LIBRARIES}")
|
||||
endif (NOT LZO_FIND_QUIETLY)
|
||||
else (LZO_FOUND)
|
||||
if (LZO_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could NOT find LZO")
|
||||
endif (LZO_FIND_REQUIRED)
|
||||
endif (LZO_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(LZO_INCLUDE_DIR LZO_LIBRARIES)
|
||||
endif (LZO_INCLUDE_DIR AND LZO_LIBRARIES)
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef __HEADER_VNUM_HELPER__
|
||||
#define __HEADER_VNUM_HELPER__
|
||||
|
||||
/**
|
||||
이미 존재하거나 앞으로 추가될 아이템, 몹 등을 소스에서 식별할 때 현재는 모두
|
||||
식별자(숫자=VNum)를 하드코딩하는 방식으로 되어있어서 가독성이 매우 떨어지는데
|
||||
|
||||
앞으로는 소스만 봐도 어떤 아이템(혹은 몹)인지 알 수 있게 하자는 승철님의 제안으로 추가.
|
||||
|
||||
* 이 파일은 변경이 잦을것으로 예상되는데 PCH에 넣으면 바뀔 때마다 전체 컴파일 해야하니
|
||||
일단은 필요한 cpp파일에서 include 해서 쓰도록 했음.
|
||||
|
||||
* cpp에서 구현하면 컴파일 ~ 링크해야하니 그냥 common에 헤더만 넣었음. (game, db프로젝트 둘 다 사용 예정)
|
||||
|
||||
@date 2011. 8. 29.
|
||||
*/
|
||||
|
||||
|
||||
class CItemVnumHelper
|
||||
{
|
||||
public:
|
||||
/// 독일 DVD용 불사조 소환권
|
||||
static const bool IsPhoenix(DWORD vnum) { return 53001 == vnum; } // NOTE: 불사조 소환 아이템은 53001 이지만 mob-vnum은 34001 입니다.
|
||||
|
||||
/// 라마단 이벤트 초승달의 반지 (원래는 라마단 이벤트용 특수 아이템이었으나 앞으로 여러 방향으로 재활용해서 계속 쓴다고 함)
|
||||
static const bool IsRamadanMoonRing(DWORD vnum) { return 71135 == vnum; }
|
||||
|
||||
/// 할로윈 사탕 (스펙은 초승달의 반지와 동일)
|
||||
static const bool IsHalloweenCandy(DWORD vnum) { return 71136 == vnum; }
|
||||
|
||||
/// 크리스마스 행복의 반지
|
||||
static const bool IsHappinessRing(DWORD vnum) { return 71143 == vnum; }
|
||||
|
||||
/// 발렌타인 사랑의 팬던트
|
||||
static const bool IsLovePendant(DWORD vnum) { return 71145 == vnum; }
|
||||
};
|
||||
|
||||
class CMobVnumHelper
|
||||
{
|
||||
public:
|
||||
/// 독일 DVD용 불사조 몹 번호
|
||||
static bool IsPhoenix(DWORD vnum) { return 34001 == vnum; }
|
||||
static bool IsIcePhoenix(DWORD vnum) { return 34003 == vnum; }
|
||||
/// PetSystem이 관리하는 펫인가?
|
||||
static bool IsPetUsingPetSystem(DWORD vnum) { return (IsPhoenix(vnum) || IsReindeerYoung(vnum)) || IsIcePhoenix(vnum); }
|
||||
|
||||
/// 2011년 크리스마스 이벤트용 펫 (아기 순록)
|
||||
static bool IsReindeerYoung(DWORD vnum) { return 34002 == vnum; }
|
||||
|
||||
/// 라마단 이벤트 보상용 흑마(20119) .. 할로윈 이벤트용 라마단 흑마 클론(스펙은 같음, 20219)
|
||||
static bool IsRamadanBlackHorse(DWORD vnum) { return 20119 == vnum || 20219 == vnum || 22022 == vnum; }
|
||||
};
|
||||
|
||||
class CVnumHelper
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
#endif //__HEADER_VNUM_HELPER__
|
|
@ -0,0 +1,335 @@
|
|||
#ifndef __INC_AUCTION_TABLES_H__
|
||||
#define __INC_AUCTION_TABLES_H__
|
||||
|
||||
#include "tables.h"
|
||||
|
||||
typedef struct _base_auction
|
||||
{
|
||||
public:
|
||||
DWORD item_num;
|
||||
TItemTable* item_proto;
|
||||
int offer_price;
|
||||
int price;
|
||||
DWORD offer_id;
|
||||
char shown_name[CHARACTER_NAME_MAX_LEN + 1];
|
||||
BYTE empire;
|
||||
time_t expired_time;
|
||||
|
||||
DWORD get_item_num () { return item_num; }
|
||||
DWORD get_offer_id () { return offer_id; }
|
||||
BYTE get_empire () { return empire; }
|
||||
time_t get_expired_time () { return expired_time; }
|
||||
bool is_expired ()
|
||||
{
|
||||
return (time(NULL) > expired_time);
|
||||
}
|
||||
int get_price () { return offer_price; }
|
||||
} TAuctionSimpleItemInfo;
|
||||
|
||||
// 각 auction 정보들.
|
||||
// primary key (item_id)
|
||||
typedef struct _auction : public _base_auction
|
||||
{
|
||||
public:
|
||||
DWORD item_id;
|
||||
DWORD bidder_id;
|
||||
|
||||
_auction (){}
|
||||
_auction (DWORD _item_num, int _offer_price, int _price, DWORD _offer_id,
|
||||
char* _shown_name, time_t _expired_time, DWORD _item_id, DWORD _bidder_id, BYTE _empire)
|
||||
{
|
||||
item_num = _item_num;
|
||||
offer_price= _offer_price;
|
||||
price = _price;
|
||||
offer_id = _offer_id;
|
||||
thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1);
|
||||
expired_time = _expired_time;
|
||||
item_id = _item_id;
|
||||
bidder_id = _bidder_id;
|
||||
empire = _empire;
|
||||
}
|
||||
|
||||
// 이 메소드들은 어떤 변수가 auction에서 어떤 역할을 하는지 까먹을 까봐
|
||||
// 만들어놓았다.
|
||||
// by rtsummit
|
||||
DWORD get_item_id () { return item_id; }
|
||||
DWORD get_bidder_id () { return bidder_id; }
|
||||
int get_bid_price () { return offer_price; }
|
||||
void set_bid_price (int new_price)
|
||||
{
|
||||
offer_price = new_price;
|
||||
}
|
||||
int get_impur_price () { return price; }
|
||||
|
||||
const char* get_bidder_name () { return shown_name; }
|
||||
void set_bidder_name (const char* new_bidder_name)
|
||||
{
|
||||
thecore_memcpy(shown_name, new_bidder_name, strlen(new_bidder_name) + 1);
|
||||
}
|
||||
} TAuctionItemInfo;
|
||||
|
||||
// primary key (item_id)
|
||||
typedef struct _sale : public _base_auction
|
||||
{
|
||||
_sale (){}
|
||||
|
||||
_sale (DWORD _item_num, int _offer_price, DWORD _offer_id,
|
||||
char* _shown_name, DWORD _item_id, DWORD _wisher_id)
|
||||
{
|
||||
item_num = _item_num;
|
||||
offer_price= _offer_price;
|
||||
offer_id = _offer_id;
|
||||
thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1);
|
||||
item_id = _item_id;
|
||||
wisher_id = _wisher_id;
|
||||
}
|
||||
|
||||
DWORD item_id;
|
||||
DWORD wisher_id;
|
||||
|
||||
} TSaleItemInfo;
|
||||
|
||||
// wish는 실제하는 아이템은 없다.
|
||||
// primary key (item_num, wisher_id)
|
||||
typedef struct _wish : public _base_auction
|
||||
{
|
||||
_wish (){}
|
||||
|
||||
_wish (DWORD _item_num, int _offer_price, DWORD _offer_id,
|
||||
char* _shown_name, time_t _expired_time, BYTE _empire)
|
||||
{
|
||||
item_num = _item_num;
|
||||
offer_price= _offer_price;
|
||||
offer_id = _offer_id;
|
||||
thecore_memcpy(shown_name, _shown_name, strlen(_shown_name) + 1);
|
||||
expired_time = _expired_time;
|
||||
empire = _empire;
|
||||
}
|
||||
} TWishItemInfo;
|
||||
|
||||
enum AuctionType {_AUCTION, _WISH_AUCTION, _MY_AUCTION, _MY_WISH_AUCTION, _AUCTION_MAX};
|
||||
|
||||
enum AuctionCmd {OPEN_AUCTION, OPEN_WISH_AUCTION, OPEN_MY_AUCTION, OPEN_MY_WISH_AUCTION,
|
||||
AUCTION_BID, AUCTION_IMME_PUR, AUCTION_ENR_AUC, AUCTION_ENR_WISH, AUCTION_ENR_SALE,
|
||||
AUCTION_GET_AUC, AUCTION_BUY_SOLD,
|
||||
AUCTION_CANCEL_AUC, AUCTION_CANCEL_WISH, AUCTION_CANCEL_SALE,
|
||||
AUCTION_DELETE_AUCTION_ITEM, AUCTION_DELETE_SALE_ITEM,
|
||||
AUCTION_CHANGING_MONEY,
|
||||
AUCTION_REBID, AUCTION_BID_CANCEL,
|
||||
};
|
||||
|
||||
// 반드시 FAIL 앞에, 실패 류 들이 와야한다.
|
||||
// 왜냐, <= AUCTION_FAIL 이런 CHECK을 할 거거든
|
||||
// 반대로 SUCCESS 뒤에, 성공 류 들이 와야한다. 근데 성공류가 있긴 하려나...
|
||||
|
||||
enum AuctionResult { AUCTION_EXPIRED, AUCTION_NOT_EXPIRED, AUCTION_NOT_ENOUGH_MONEY,
|
||||
AUCTION_SOLD, AUCTION_CANCEL, AUCTION_ALREADY_IN, AUCTION_NOT_IN, AUCTION_FAIL, AUCTION_SUCCESS };
|
||||
|
||||
enum AuctionSort { AUCTION_NO_ORDER,
|
||||
AUCTION_ITEM_NAME_AC, AUCTION_ITEM_NAME_DC,
|
||||
AUCTION_CATEGORY_AC, AUCTION_CATEGORY_DC,
|
||||
AUCTION_TIME_AC, AUCTION_TIME_DC,
|
||||
AUCTION_CHAR_NAME_AC, AUCTION_CHAR_NAME_DC,
|
||||
AUCTION_PRICE_AC, AUCTION_PRICE_DC,
|
||||
};
|
||||
|
||||
typedef struct command_get_auction_list
|
||||
{
|
||||
AuctionCmd cmd;
|
||||
DWORD start_idx;
|
||||
BYTE size;
|
||||
} TPacketGDGetAuctionList;
|
||||
|
||||
typedef struct command_auction
|
||||
{
|
||||
void enroll_product (DWORD _item_id, BYTE _empire, int _bidPrice, int _impurPrice)
|
||||
{
|
||||
cmd = AUCTION_ENR_AUC;
|
||||
item = _item_id;
|
||||
empire = _empire;
|
||||
price1 = _bidPrice;
|
||||
price2 = _impurPrice;
|
||||
}
|
||||
void enroll_sale (DWORD _item_id, DWORD _wisher_id, int _salePrice)
|
||||
{
|
||||
cmd = AUCTION_ENR_SALE;
|
||||
item = _item_id;
|
||||
price1 = _salePrice;
|
||||
player_id = _wisher_id;
|
||||
}
|
||||
void enroll_wish (DWORD _item_num, BYTE _empire, int _wishPrice)
|
||||
{
|
||||
cmd = AUCTION_ENR_WISH;
|
||||
item = _item_num;
|
||||
empire = _empire;
|
||||
price1 = _wishPrice;
|
||||
}
|
||||
void bid (DWORD _item_id, int _bidPrice)
|
||||
{
|
||||
cmd = AUCTION_BID;
|
||||
item = _item_id;
|
||||
price1 = _bidPrice;
|
||||
}
|
||||
void impur (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_IMME_PUR;
|
||||
item = _item_id;
|
||||
}
|
||||
void get_auctioned_item (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_GET_AUC;
|
||||
item = _item_id;
|
||||
}
|
||||
void buy_sold_item (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_BUY_SOLD;
|
||||
item = _item_id;
|
||||
}
|
||||
void cancel_auction (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_CANCEL_AUC;
|
||||
item = _item_id;
|
||||
}
|
||||
void cancel_wish (DWORD _item_num)
|
||||
{
|
||||
cmd = AUCTION_CANCEL_WISH;
|
||||
item = _item_num;
|
||||
}
|
||||
void cancel_sale (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_CANCEL_SALE;
|
||||
item = _item_id;
|
||||
}
|
||||
|
||||
void delete_auction_item (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_DELETE_AUCTION_ITEM;
|
||||
item = _item_id;
|
||||
}
|
||||
|
||||
void delete_sale_item (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_DELETE_SALE_ITEM;
|
||||
item = _item_id;
|
||||
}
|
||||
|
||||
void changing_money (int _money)
|
||||
{
|
||||
cmd = AUCTION_CHANGING_MONEY;
|
||||
price1 = _money;
|
||||
}
|
||||
// bid랑 cmd만 다르다.
|
||||
void rebid (DWORD _item_id, int _bidPrice)
|
||||
{
|
||||
cmd = AUCTION_REBID;
|
||||
item = _item_id;
|
||||
price1 = _bidPrice;
|
||||
}
|
||||
void bid_cancel (DWORD _item_id)
|
||||
{
|
||||
cmd = AUCTION_BID_CANCEL;
|
||||
item = _item_id;
|
||||
}
|
||||
DWORD get_item () { return item; }
|
||||
|
||||
protected:
|
||||
AuctionCmd cmd;
|
||||
DWORD player_id;
|
||||
DWORD item;
|
||||
BYTE empire;
|
||||
int price1;
|
||||
int price2;
|
||||
|
||||
public:
|
||||
AuctionCmd get_cmd() { return cmd; }
|
||||
BYTE get_empire () { return empire; }
|
||||
} TPacketGDCommnadAuction;
|
||||
|
||||
typedef struct result_auction
|
||||
{
|
||||
AuctionCmd cmd;
|
||||
BYTE result;
|
||||
DWORD target;
|
||||
} TPacketDGResultAuction;
|
||||
|
||||
// wrapper struct
|
||||
typedef struct auction_enroll_product : public command_auction
|
||||
{
|
||||
DWORD get_item_id() { return item; }
|
||||
int get_bid_price() { return price1; }
|
||||
int get_impur_price() { return price2; }
|
||||
} AuctionEnrollProductInfo;
|
||||
|
||||
typedef struct auction_enroll_sale : public command_auction
|
||||
{
|
||||
DWORD get_item_id() { return item; }
|
||||
DWORD get_wisher_id() { return player_id; }
|
||||
int get_sale_price() { return price1; }
|
||||
} AuctionEnrollSaleInfo;
|
||||
|
||||
typedef struct auction_enroll_wish : public command_auction
|
||||
{
|
||||
DWORD get_item_num() { return item; }
|
||||
int get_wish_price() { return price1; }
|
||||
} AuctionEnrollWishInfo;
|
||||
|
||||
typedef struct auction_bid : public command_auction
|
||||
{
|
||||
DWORD get_item_id() { return item; }
|
||||
int get_bid_price() { return price1; }
|
||||
} AuctionBidInfo;
|
||||
|
||||
typedef struct auction_impur : public command_auction
|
||||
{
|
||||
DWORD get_item_id() { return item; }
|
||||
} AuctionImpurInfo;
|
||||
|
||||
|
||||
//typedef struct get_auction_list
|
||||
//
|
||||
//bid
|
||||
//{
|
||||
// item_id;
|
||||
// bidder_id;
|
||||
// price;
|
||||
//}
|
||||
//impur
|
||||
//{
|
||||
// item_id;
|
||||
// purchaser_id;
|
||||
//}
|
||||
//enroll_wish
|
||||
//{
|
||||
// item_num;
|
||||
// wisher_id;
|
||||
// wish_price;
|
||||
//}
|
||||
//enroll_sale
|
||||
//{
|
||||
// item_id;
|
||||
// seller_id;
|
||||
// sale_price;
|
||||
//}
|
||||
//
|
||||
//return_packet
|
||||
//{
|
||||
// isSuccess;
|
||||
//}
|
||||
//
|
||||
//
|
||||
//get_auction_simple_item_info_list
|
||||
//{
|
||||
// auction_type;
|
||||
// start_idx;
|
||||
// size;
|
||||
// conditions; 정렬은 승철님께 조언을 구해보자.ㅇㅇ
|
||||
//}
|
||||
//
|
||||
//get_auction_detail_item_info
|
||||
//{
|
||||
// item_id;
|
||||
//}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __INC_METIN_II_COMMON_BILLING_H__
|
||||
#define __INC_METIN_II_COMMON_BILLING_H__
|
||||
|
||||
enum EBillingTypes
|
||||
{
|
||||
BILLING_NONE,
|
||||
BILLING_IP_FREE,
|
||||
BILLING_FREE,
|
||||
BILLING_IP_TIME,
|
||||
BILLING_IP_DAY,
|
||||
BILLING_TIME,
|
||||
BILLING_DAY,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef __METIN_II_COMMON_BUILDING_H__
|
||||
#define __METIN_II_COMMON_BUILDING_H__
|
||||
|
||||
namespace building
|
||||
{
|
||||
enum
|
||||
{
|
||||
OBJECT_MATERIAL_MAX_NUM = 5,
|
||||
};
|
||||
|
||||
typedef struct SLand
|
||||
{
|
||||
DWORD dwID;
|
||||
long lMapIndex;
|
||||
long x, y;
|
||||
long width, height;
|
||||
DWORD dwGuildID;
|
||||
BYTE bGuildLevelLimit;
|
||||
DWORD dwPrice;
|
||||
} TLand;
|
||||
|
||||
typedef struct SObjectMaterial
|
||||
{
|
||||
DWORD dwItemVnum;
|
||||
DWORD dwCount;
|
||||
} TObjectMaterial;
|
||||
|
||||
typedef struct SObjectProto
|
||||
{
|
||||
DWORD dwVnum;
|
||||
DWORD dwPrice;
|
||||
|
||||
TObjectMaterial kMaterials[OBJECT_MATERIAL_MAX_NUM];
|
||||
|
||||
DWORD dwUpgradeVnum;
|
||||
DWORD dwUpgradeLimitTime;
|
||||
long lLife;
|
||||
long lRegion[4];
|
||||
|
||||
DWORD dwNPCVnum;
|
||||
long lNPCX;
|
||||
long lNPCY;
|
||||
|
||||
DWORD dwGroupVnum; // 같은 그룹은 하나만 건설가능
|
||||
DWORD dwDependOnGroupVnum; // 지어져 있어야하는 그룹
|
||||
} TObjectProto;
|
||||
|
||||
typedef struct SObject
|
||||
{
|
||||
DWORD dwID;
|
||||
DWORD dwLandID;
|
||||
DWORD dwVnum;
|
||||
long lMapIndex;
|
||||
long x, y;
|
||||
|
||||
float xRot;
|
||||
float yRot;
|
||||
float zRot;
|
||||
long lLife;
|
||||
} TObject;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef __INC_COMMON_CACHE_H__
|
||||
#define __INC_COMMON_CACHE_H__
|
||||
|
||||
template <typename T> class cache
|
||||
{
|
||||
public:
|
||||
cache()
|
||||
: m_bNeedQuery(false), m_expireTime(600), m_lastUpdateTime(0)
|
||||
{
|
||||
m_lastFlushTime = time(0);
|
||||
|
||||
memset( &m_data, 0, sizeof(m_data) );
|
||||
}
|
||||
|
||||
T * Get(bool bUpdateTime = true)
|
||||
{
|
||||
if (bUpdateTime)
|
||||
m_lastUpdateTime = time(0);
|
||||
|
||||
return &m_data;
|
||||
}
|
||||
|
||||
void Put(T * pNew, bool bSkipQuery = false)
|
||||
{
|
||||
thecore_memcpy(&m_data, pNew, sizeof(T));
|
||||
m_lastUpdateTime = time(0);
|
||||
|
||||
if (!bSkipQuery)
|
||||
m_bNeedQuery = true;
|
||||
}
|
||||
|
||||
bool CheckFlushTimeout()
|
||||
{
|
||||
if (m_bNeedQuery && time(0) - m_lastFlushTime > m_expireTime)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckTimeout()
|
||||
{
|
||||
if (time(0) - m_lastUpdateTime > m_expireTime)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Flush()
|
||||
{
|
||||
if (!m_bNeedQuery)
|
||||
return;
|
||||
|
||||
OnFlush();
|
||||
m_bNeedQuery = false;
|
||||
m_lastFlushTime = time(0);
|
||||
}
|
||||
|
||||
virtual void OnFlush() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
T m_data;
|
||||
bool m_bNeedQuery;
|
||||
time_t m_expireTime;
|
||||
time_t m_lastUpdateTime;
|
||||
time_t m_lastFlushTime;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef __INC_METIN_II_D3DTYPE_H__
|
||||
#define __INC_METIN_II_D3DTYPE_H__
|
||||
|
||||
typedef struct D3DXVECTOR2
|
||||
{
|
||||
float x, y;
|
||||
} D3DXVECTOR2, *LPD3DXVECTOR2;
|
||||
|
||||
typedef struct D3DXVECTOR3
|
||||
{
|
||||
float x, y, z;
|
||||
} D3DXVECTOR3, *LPD3DXVECTOR3;
|
||||
|
||||
typedef struct D3DXVECTOR4
|
||||
{
|
||||
float x, y, z, w;
|
||||
} D3DXVECTOR4, *LPD3DXVECTOR4;
|
||||
|
||||
typedef struct D3DXQUATERNION
|
||||
{
|
||||
float x, y, z, w;
|
||||
} D3DXQUATERNION, *LPD3DXQUATERNION;
|
||||
|
||||
typedef struct D3DXCOLOR
|
||||
{
|
||||
float r, g, b, a;
|
||||
} D3DXCOLOR, *LPD3DXCOLOR;
|
||||
|
||||
typedef struct _D3DCOLORVALUE
|
||||
{
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
} D3DCOLORVALUE;
|
||||
|
||||
typedef D3DXVECTOR3 D3DVECTOR;
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,371 @@
|
|||
#ifndef __INC_METIN2_ITEM_LENGTH_H__
|
||||
#define __INC_METIN2_ITEM_LENGTH_H__
|
||||
|
||||
enum EItemMisc
|
||||
{
|
||||
ITEM_NAME_MAX_LEN = 24,
|
||||
ITEM_VALUES_MAX_NUM = 6,
|
||||
ITEM_SMALL_DESCR_MAX_LEN = 256,
|
||||
ITEM_LIMIT_MAX_NUM = 2,
|
||||
ITEM_APPLY_MAX_NUM = 3,
|
||||
ITEM_SOCKET_MAX_NUM = 3,
|
||||
ITEM_MAX_COUNT = 200,
|
||||
ITEM_ATTRIBUTE_MAX_NUM = 7,
|
||||
ITEM_ATTRIBUTE_MAX_LEVEL = 5,
|
||||
ITEM_AWARD_WHY_MAX_LEN = 50,
|
||||
|
||||
REFINE_MATERIAL_MAX_NUM = 5,
|
||||
|
||||
ITEM_ELK_VNUM = 50026,
|
||||
};
|
||||
|
||||
const BYTE ITEM_SOCKET_REMAIN_SEC = 0;
|
||||
enum EItemValueIdice
|
||||
{
|
||||
ITEM_VALUE_DRAGON_SOUL_POLL_OUT_BONUS_IDX = 0,
|
||||
ITEM_VALUE_CHARGING_AMOUNT_IDX = 0,
|
||||
ITEM_VALUE_SECONDARY_COIN_UNIT_IDX = 0,
|
||||
};
|
||||
enum EItemDragonSoulSockets
|
||||
{
|
||||
ITEM_SOCKET_DRAGON_SOUL_ACTIVE_IDX = 2,
|
||||
ITEM_SOCKET_CHARGING_AMOUNT_IDX = 2,
|
||||
};
|
||||
// 헐 이거 미친거 아니야?
|
||||
// 나중에 소켓 확장하면 어쩌려고 이지랄 -_-;;;
|
||||
enum EItemUniqueSockets
|
||||
{
|
||||
ITEM_SOCKET_UNIQUE_SAVE_TIME = ITEM_SOCKET_MAX_NUM - 2,
|
||||
ITEM_SOCKET_UNIQUE_REMAIN_TIME = ITEM_SOCKET_MAX_NUM - 1
|
||||
};
|
||||
|
||||
enum EItemTypes
|
||||
{
|
||||
ITEM_NONE, //0
|
||||
ITEM_WEAPON, //1//무기
|
||||
ITEM_ARMOR, //2//갑옷
|
||||
ITEM_USE, //3//아이템 사용
|
||||
ITEM_AUTOUSE, //4
|
||||
ITEM_MATERIAL, //5
|
||||
ITEM_SPECIAL, //6 //스페셜 아이템
|
||||
ITEM_TOOL, //7
|
||||
ITEM_LOTTERY, //8//복권
|
||||
ITEM_ELK, //9//돈
|
||||
ITEM_METIN, //10
|
||||
ITEM_CONTAINER, //11
|
||||
ITEM_FISH, //12//낚시
|
||||
ITEM_ROD, //13
|
||||
ITEM_RESOURCE, //14
|
||||
ITEM_CAMPFIRE, //15
|
||||
ITEM_UNIQUE, //16
|
||||
ITEM_SKILLBOOK, //17
|
||||
ITEM_QUEST, //18
|
||||
ITEM_POLYMORPH, //19
|
||||
ITEM_TREASURE_BOX, //20//보물상자
|
||||
ITEM_TREASURE_KEY, //21//보물상자 열쇠
|
||||
ITEM_SKILLFORGET, //22
|
||||
ITEM_GIFTBOX, //23
|
||||
ITEM_PICK, //24
|
||||
ITEM_HAIR, //25//머리
|
||||
ITEM_TOTEM, //26//토템
|
||||
ITEM_BLEND, //27//생성될때 랜덤하게 속성이 붙는 약물
|
||||
ITEM_COSTUME, //28//코스츔 아이템 (2011년 8월 추가된 코스츔 시스템용 아이템)
|
||||
ITEM_DS, //29 //용혼석
|
||||
ITEM_SPECIAL_DS, //30 // 특수한 용혼석 (DS_SLOT에 착용하는 UNIQUE 아이템이라 생각하면 됨)
|
||||
ITEM_EXTRACT, //31 추출도구.
|
||||
ITEM_SECONDARY_COIN, //32 ?? 명도전??
|
||||
ITEM_RING, //33 반지
|
||||
ITEM_BELT, //34 벨트
|
||||
};
|
||||
|
||||
enum EMetinSubTypes
|
||||
{
|
||||
METIN_NORMAL,
|
||||
METIN_GOLD,
|
||||
};
|
||||
|
||||
enum EWeaponSubTypes
|
||||
{
|
||||
WEAPON_SWORD,
|
||||
WEAPON_DAGGER,
|
||||
WEAPON_BOW,
|
||||
WEAPON_TWO_HANDED,
|
||||
WEAPON_BELL,
|
||||
WEAPON_FAN,
|
||||
WEAPON_ARROW,
|
||||
WEAPON_MOUNT_SPEAR,
|
||||
WEAPON_NUM_TYPES,
|
||||
};
|
||||
|
||||
enum EArmorSubTypes
|
||||
{
|
||||
ARMOR_BODY,
|
||||
ARMOR_HEAD,
|
||||
ARMOR_SHIELD,
|
||||
ARMOR_WRIST,
|
||||
ARMOR_FOOTS,
|
||||
ARMOR_NECK,
|
||||
ARMOR_EAR,
|
||||
ARMOR_NUM_TYPES
|
||||
};
|
||||
|
||||
enum ECostumeSubTypes
|
||||
{
|
||||
COSTUME_BODY = ARMOR_BODY, // [중요!!] ECostumeSubTypes enum value는 종류별로 EArmorSubTypes의 그것과 같아야 함.
|
||||
COSTUME_HAIR = ARMOR_HEAD, // 이는 코스츔 아이템에 추가 속성을 붙이겠다는 사업부의 요청에 따라서 기존 로직을 활용하기 위함임.
|
||||
COSTUME_NUM_TYPES,
|
||||
};
|
||||
|
||||
enum EDragonSoulSubType
|
||||
{
|
||||
DS_SLOT1,
|
||||
DS_SLOT2,
|
||||
DS_SLOT3,
|
||||
DS_SLOT4,
|
||||
DS_SLOT5,
|
||||
DS_SLOT6,
|
||||
DS_SLOT_MAX,
|
||||
};
|
||||
|
||||
enum EDragonSoulGradeTypes
|
||||
{
|
||||
DRAGON_SOUL_GRADE_NORMAL,
|
||||
DRAGON_SOUL_GRADE_BRILLIANT,
|
||||
DRAGON_SOUL_GRADE_RARE,
|
||||
DRAGON_SOUL_GRADE_ANCIENT,
|
||||
DRAGON_SOUL_GRADE_LEGENDARY,
|
||||
DRAGON_SOUL_GRADE_MAX,
|
||||
|
||||
};
|
||||
|
||||
enum EDragonSoulStepTypes
|
||||
{
|
||||
DRAGON_SOUL_STEP_LOWEST,
|
||||
DRAGON_SOUL_STEP_LOW,
|
||||
DRAGON_SOUL_STEP_MID,
|
||||
DRAGON_SOUL_STEP_HIGH,
|
||||
DRAGON_SOUL_STEP_HIGHEST,
|
||||
DRAGON_SOUL_STEP_MAX,
|
||||
};
|
||||
#define DRAGON_SOUL_STRENGTH_MAX 7
|
||||
|
||||
enum EDSInventoryMaxNum
|
||||
{
|
||||
DRAGON_SOUL_INVENTORY_MAX_NUM = DS_SLOT_MAX * DRAGON_SOUL_GRADE_MAX * DRAGON_SOUL_BOX_SIZE,
|
||||
};
|
||||
|
||||
enum EFishSubTypes
|
||||
{
|
||||
FISH_ALIVE,
|
||||
FISH_DEAD,
|
||||
};
|
||||
|
||||
enum EResourceSubTypes
|
||||
{
|
||||
RESOURCE_FISHBONE,
|
||||
RESOURCE_WATERSTONEPIECE,
|
||||
RESOURCE_WATERSTONE,
|
||||
RESOURCE_BLOOD_PEARL,
|
||||
RESOURCE_BLUE_PEARL,
|
||||
RESOURCE_WHITE_PEARL,
|
||||
RESOURCE_BUCKET,
|
||||
RESOURCE_CRYSTAL,
|
||||
RESOURCE_GEM,
|
||||
RESOURCE_STONE,
|
||||
RESOURCE_METIN,
|
||||
RESOURCE_ORE,
|
||||
};
|
||||
|
||||
enum EUniqueSubTypes
|
||||
{
|
||||
UNIQUE_NONE,
|
||||
UNIQUE_BOOK,
|
||||
UNIQUE_SPECIAL_RIDE,
|
||||
UNIQUE_SPECIAL_MOUNT_RIDE,
|
||||
};
|
||||
|
||||
enum EUseSubTypes
|
||||
{
|
||||
USE_POTION, // 0
|
||||
USE_TALISMAN,
|
||||
USE_TUNING,
|
||||
USE_MOVE,
|
||||
USE_TREASURE_BOX,
|
||||
USE_MONEYBAG,
|
||||
USE_BAIT,
|
||||
USE_ABILITY_UP,
|
||||
USE_AFFECT,
|
||||
USE_CREATE_STONE,
|
||||
USE_SPECIAL, // 10
|
||||
USE_POTION_NODELAY,
|
||||
USE_CLEAR,
|
||||
USE_INVISIBILITY,
|
||||
USE_DETACHMENT,
|
||||
USE_BUCKET,
|
||||
USE_POTION_CONTINUE,
|
||||
USE_CLEAN_SOCKET,
|
||||
USE_CHANGE_ATTRIBUTE,
|
||||
USE_ADD_ATTRIBUTE,
|
||||
USE_ADD_ACCESSORY_SOCKET, // 20
|
||||
USE_PUT_INTO_ACCESSORY_SOCKET,
|
||||
USE_ADD_ATTRIBUTE2,
|
||||
USE_RECIPE,
|
||||
USE_CHANGE_ATTRIBUTE2,
|
||||
USE_BIND,
|
||||
USE_UNBIND,
|
||||
USE_TIME_CHARGE_PER,
|
||||
USE_TIME_CHARGE_FIX, // 28
|
||||
USE_PUT_INTO_BELT_SOCKET, // 29 벨트 소켓에 사용할 수 있는 아이템
|
||||
USE_PUT_INTO_RING_SOCKET, // 30 반지 소켓에 사용할 수 있는 아이템 (유니크 반지 말고, 새로 추가된 반지 슬롯)
|
||||
};
|
||||
|
||||
enum EExtractSubTypes
|
||||
{
|
||||
EXTRACT_DRAGON_SOUL,
|
||||
EXTRACT_DRAGON_HEART,
|
||||
};
|
||||
|
||||
enum EAutoUseSubTypes
|
||||
{
|
||||
AUTOUSE_POTION,
|
||||
AUTOUSE_ABILITY_UP,
|
||||
AUTOUSE_BOMB,
|
||||
AUTOUSE_GOLD,
|
||||
AUTOUSE_MONEYBAG,
|
||||
AUTOUSE_TREASURE_BOX
|
||||
};
|
||||
|
||||
enum EMaterialSubTypes
|
||||
{
|
||||
MATERIAL_LEATHER,
|
||||
MATERIAL_BLOOD,
|
||||
MATERIAL_ROOT,
|
||||
MATERIAL_NEEDLE,
|
||||
MATERIAL_JEWEL,
|
||||
MATERIAL_DS_REFINE_NORMAL,
|
||||
MATERIAL_DS_REFINE_BLESSED,
|
||||
MATERIAL_DS_REFINE_HOLLY,
|
||||
};
|
||||
|
||||
enum ESpecialSubTypes
|
||||
{
|
||||
SPECIAL_MAP,
|
||||
SPECIAL_KEY,
|
||||
SPECIAL_DOC,
|
||||
SPECIAL_SPIRIT,
|
||||
};
|
||||
|
||||
enum EToolSubTypes
|
||||
{
|
||||
TOOL_FISHING_ROD
|
||||
};
|
||||
|
||||
enum ELotterySubTypes
|
||||
{
|
||||
LOTTERY_TICKET,
|
||||
LOTTERY_INSTANT
|
||||
};
|
||||
|
||||
enum EItemFlag
|
||||
{
|
||||
ITEM_FLAG_REFINEABLE = (1 << 0),
|
||||
ITEM_FLAG_SAVE = (1 << 1),
|
||||
ITEM_FLAG_STACKABLE = (1 << 2), // 여러개 합칠 수 있음
|
||||
ITEM_FLAG_COUNT_PER_1GOLD = (1 << 3),
|
||||
ITEM_FLAG_SLOW_QUERY = (1 << 4),
|
||||
ITEM_FLAG_UNUSED01 = (1 << 5), // UNUSED
|
||||
ITEM_FLAG_UNIQUE = (1 << 6),
|
||||
ITEM_FLAG_MAKECOUNT = (1 << 7),
|
||||
ITEM_FLAG_IRREMOVABLE = (1 << 8),
|
||||
ITEM_FLAG_CONFIRM_WHEN_USE = (1 << 9),
|
||||
ITEM_FLAG_QUEST_USE = (1 << 10),
|
||||
ITEM_FLAG_QUEST_USE_MULTIPLE = (1 << 11),
|
||||
ITEM_FLAG_QUEST_GIVE = (1 << 12),
|
||||
ITEM_FLAG_LOG = (1 << 13),
|
||||
ITEM_FLAG_APPLICABLE = (1 << 14),
|
||||
};
|
||||
|
||||
enum EItemAntiFlag
|
||||
{
|
||||
ITEM_ANTIFLAG_FEMALE = (1 << 0), // 여성 사용 불가
|
||||
ITEM_ANTIFLAG_MALE = (1 << 1), // 남성 사용 불가
|
||||
ITEM_ANTIFLAG_WARRIOR = (1 << 2), // 무사 사용 불가
|
||||
ITEM_ANTIFLAG_ASSASSIN = (1 << 3), // 자객 사용 불가
|
||||
ITEM_ANTIFLAG_SURA = (1 << 4), // 수라 사용 불가
|
||||
ITEM_ANTIFLAG_SHAMAN = (1 << 5), // 무당 사용 불가
|
||||
ITEM_ANTIFLAG_GET = (1 << 6), // 집을 수 없음
|
||||
ITEM_ANTIFLAG_DROP = (1 << 7), // 버릴 수 없음
|
||||
ITEM_ANTIFLAG_SELL = (1 << 8), // 팔 수 없음
|
||||
ITEM_ANTIFLAG_EMPIRE_A = (1 << 9), // A 제국 사용 불가
|
||||
ITEM_ANTIFLAG_EMPIRE_B = (1 << 10), // B 제국 사용 불가
|
||||
ITEM_ANTIFLAG_EMPIRE_C = (1 << 11), // C 제국 사용 불가
|
||||
ITEM_ANTIFLAG_SAVE = (1 << 12), // 저장되지 않음
|
||||
ITEM_ANTIFLAG_GIVE = (1 << 13), // 거래 불가
|
||||
ITEM_ANTIFLAG_PKDROP = (1 << 14), // PK시 떨어지지 않음
|
||||
ITEM_ANTIFLAG_STACK = (1 << 15), // 합칠 수 없음
|
||||
ITEM_ANTIFLAG_MYSHOP = (1 << 16), // 개인 상점에 올릴 수 없음
|
||||
ITEM_ANTIFLAG_SAFEBOX = (1 << 17), // 창고에 넣을 수 없음
|
||||
};
|
||||
|
||||
enum EItemWearableFlag
|
||||
{
|
||||
WEARABLE_BODY = (1 << 0),
|
||||
WEARABLE_HEAD = (1 << 1),
|
||||
WEARABLE_FOOTS = (1 << 2),
|
||||
WEARABLE_WRIST = (1 << 3),
|
||||
WEARABLE_WEAPON = (1 << 4),
|
||||
WEARABLE_NECK = (1 << 5),
|
||||
WEARABLE_EAR = (1 << 6),
|
||||
WEARABLE_UNIQUE = (1 << 7),
|
||||
WEARABLE_SHIELD = (1 << 8),
|
||||
WEARABLE_ARROW = (1 << 9),
|
||||
WEARABLE_HAIR = (1 << 10),
|
||||
WEARABLE_ABILITY = (1 << 11),
|
||||
WEARABLE_COSTUME_BODY = (1 << 12),
|
||||
};
|
||||
|
||||
enum ELimitTypes
|
||||
{
|
||||
LIMIT_NONE,
|
||||
|
||||
LIMIT_LEVEL,
|
||||
LIMIT_STR,
|
||||
LIMIT_DEX,
|
||||
LIMIT_INT,
|
||||
LIMIT_CON,
|
||||
LIMIT_PCBANG,
|
||||
|
||||
/// 착용 여부와 상관 없이 실시간으로 시간 차감 (socket0에 소멸 시간이 박힘: unix_timestamp 타입)
|
||||
LIMIT_REAL_TIME,
|
||||
|
||||
/// 아이템을 맨 처음 사용(혹은 착용) 한 순간부터 리얼타임 타이머 시작
|
||||
/// 최초 사용 전에는 socket0에 사용가능시간(초단위, 0이면 프로토의 limit value값 사용) 값이 쓰여있다가
|
||||
/// 아이템 사용시 socket1에 사용 횟수가 박히고 socket0에 unix_timestamp 타입의 소멸시간이 박힘.
|
||||
LIMIT_REAL_TIME_START_FIRST_USE,
|
||||
|
||||
/// 아이템을 착용 중일 때만 사용 시간이 차감되는 아이템
|
||||
/// socket0에 남은 시간이 초단위로 박힘. (아이템 최초 사용시 해당 값이 0이면 프로토의 limit value값을 socket0에 복사)
|
||||
LIMIT_TIMER_BASED_ON_WEAR,
|
||||
|
||||
LIMIT_MAX_NUM
|
||||
};
|
||||
|
||||
enum EAttrAddonTypes
|
||||
{
|
||||
ATTR_ADDON_NONE,
|
||||
// positive values are reserved for set
|
||||
ATTR_DAMAGE_ADDON = -1,
|
||||
};
|
||||
|
||||
enum ERefineType
|
||||
{
|
||||
REFINE_TYPE_NORMAL,
|
||||
REFINE_TYPE_NOT_USED1,
|
||||
REFINE_TYPE_SCROLL,
|
||||
REFINE_TYPE_HYUNIRON,
|
||||
REFINE_TYPE_MONEY_ONLY,
|
||||
REFINE_TYPE_MUSIN,
|
||||
REFINE_TYPE_BDRAGON,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,788 @@
|
|||
#ifndef __INC_METIN_II_LENGTH_H__
|
||||
#define __INC_METIN_II_LENGTH_H__
|
||||
|
||||
#define WORD_MAX 0xffff
|
||||
enum EMisc
|
||||
{
|
||||
MAX_HOST_LENGTH = 15,
|
||||
IP_ADDRESS_LENGTH = 15,
|
||||
LOGIN_MAX_LEN = 30,
|
||||
PASSWD_MAX_LEN = 16,
|
||||
PLAYER_PER_ACCOUNT = 4,
|
||||
ACCOUNT_STATUS_MAX_LEN = 8,
|
||||
CHARACTER_NAME_MAX_LEN = 24,
|
||||
SHOP_SIGN_MAX_LEN = 32,
|
||||
INVENTORY_MAX_NUM = 90,
|
||||
ABILITY_MAX_NUM = 50,
|
||||
EMPIRE_MAX_NUM = 4,
|
||||
BANWORD_MAX_LEN = 24,
|
||||
SMS_MAX_LEN = 80,
|
||||
MOBILE_MAX_LEN = 32,
|
||||
SOCIAL_ID_MAX_LEN = 18,
|
||||
|
||||
GUILD_NAME_MAX_LEN = 12,
|
||||
|
||||
SHOP_HOST_ITEM_MAX_NUM = 40, /* 호스트의 최대 아이템 개수 */
|
||||
SHOP_GUEST_ITEM_MAX_NUM = 18, /* 게스트의 최대 아이템 개수 */
|
||||
|
||||
SHOP_PRICELIST_MAX_NUM = 40, ///< 개인상점 가격정보 리스트에서 유지할 가격정보의 최대 갯수
|
||||
|
||||
CHAT_MAX_LEN = 512,
|
||||
|
||||
QUICKSLOT_MAX_NUM = 36,
|
||||
|
||||
JOURNAL_MAX_NUM = 2,
|
||||
|
||||
QUERY_MAX_LEN = 8192,
|
||||
|
||||
FILE_MAX_LEN = 128,
|
||||
|
||||
PLAYER_EXP_TABLE_MAX = 120,
|
||||
PLAYER_MAX_LEVEL_CONST = 120,
|
||||
|
||||
GUILD_MAX_LEVEL = 20,
|
||||
MOB_MAX_LEVEL = 100,
|
||||
|
||||
ATTRIBUTE_MAX_VALUE = 20,
|
||||
CHARACTER_PATH_MAX_NUM = 64,
|
||||
SKILL_MAX_NUM = 255,
|
||||
SKILLBOOK_DELAY_MIN = 64800,
|
||||
SKILLBOOK_DELAY_MAX = 108000,
|
||||
SKILL_MAX_LEVEL = 40,
|
||||
|
||||
APPLY_NAME_MAX_LEN = 32,
|
||||
EVENT_FLAG_NAME_MAX_LEN = 32,
|
||||
|
||||
MOB_SKILL_MAX_NUM = 5,
|
||||
|
||||
POINT_MAX_NUM = 255,
|
||||
DRAGON_SOUL_BOX_SIZE = 32,
|
||||
DRAGON_SOUL_BOX_COLUMN_NUM = 8,
|
||||
DRAGON_SOUL_BOX_ROW_NUM = DRAGON_SOUL_BOX_SIZE / DRAGON_SOUL_BOX_COLUMN_NUM,
|
||||
DRAGON_SOUL_REFINE_GRID_SIZE = 15,
|
||||
MAX_AMOUNT_OF_MALL_BONUS = 20,
|
||||
|
||||
WEAR_MAX_NUM = 32,
|
||||
|
||||
//LIMIT_GOLD
|
||||
GOLD_MAX = 2000000000,
|
||||
|
||||
MAX_PASSPOD = 8 ,
|
||||
|
||||
|
||||
//END_LIMIT_GOLD
|
||||
|
||||
OPENID_AUTHKEY_LEN = 32,
|
||||
|
||||
SHOP_TAB_NAME_MAX = 32,
|
||||
SHOP_TAB_COUNT_MAX = 3,
|
||||
|
||||
BELT_INVENTORY_SLOT_WIDTH = 4,
|
||||
BELT_INVENTORY_SLOT_HEIGHT= 4,
|
||||
|
||||
BELT_INVENTORY_SLOT_COUNT = BELT_INVENTORY_SLOT_WIDTH * BELT_INVENTORY_SLOT_HEIGHT,
|
||||
|
||||
|
||||
/**
|
||||
**** 현재까지 할당 된 아이템 영역 정리 (DB상 Item Position) ****
|
||||
+------------------------------------------------------+ 0
|
||||
| 캐릭터 기본 인벤토리 (45칸 * 2페이지) 90칸 |
|
||||
+------------------------------------------------------+ 90 = INVENTORY_MAX_NUM(90)
|
||||
| 캐릭터 장비 창 (착용중인 아이템) 32칸 |
|
||||
+------------------------------------------------------+ 122 = INVENTORY_MAX_NUM(90) + WEAR_MAX_NUM(32)
|
||||
| 용혼석 장비 창 (착용중인 용혼석) 12칸 |
|
||||
+------------------------------------------------------+ 134 = 122 + DS_SLOT_MAX(6) * DRAGON_SOUL_DECK_MAX_NUM(2)
|
||||
| 용혼석 장비 창 예약 (아직 미사용) 18칸 |
|
||||
+------------------------------------------------------+ 152 = 134 + DS_SLOT_MAX(6) * DRAGON_SOUL_DECK_RESERVED_MAX_NUM(3)
|
||||
| 벨트 인벤토리 (벨트 착용시에만 벨트 레벨에 따라 활성)|
|
||||
+------------------------------------------------------+ 168 = 152 + BELT_INVENTORY_SLOT_COUNT(16) = INVENTORY_AND_EQUIP_CELL_MAX
|
||||
| 미사용 |
|
||||
+------------------------------------------------------+ ??
|
||||
*/
|
||||
};
|
||||
|
||||
enum EMatrixCard
|
||||
{
|
||||
MATRIX_CODE_MAX_LEN = 192,
|
||||
MATRIX_ANSWER_MAX_LEN = 8,
|
||||
};
|
||||
|
||||
enum EWearPositions
|
||||
{
|
||||
WEAR_BODY, // 0
|
||||
WEAR_HEAD, // 1
|
||||
WEAR_FOOTS, // 2
|
||||
WEAR_WRIST, // 3
|
||||
WEAR_WEAPON, // 4
|
||||
WEAR_NECK, // 5
|
||||
WEAR_EAR, // 6
|
||||
WEAR_UNIQUE1, // 7
|
||||
WEAR_UNIQUE2, // 8
|
||||
WEAR_ARROW, // 9
|
||||
WEAR_SHIELD, // 10
|
||||
WEAR_ABILITY1, // 11
|
||||
WEAR_ABILITY2, // 12
|
||||
WEAR_ABILITY3, // 13
|
||||
WEAR_ABILITY4, // 14
|
||||
WEAR_ABILITY5, // 15
|
||||
WEAR_ABILITY6, // 16
|
||||
WEAR_ABILITY7, // 17
|
||||
WEAR_ABILITY8, // 18
|
||||
WEAR_COSTUME_BODY, // 19
|
||||
WEAR_COSTUME_HAIR, // 20
|
||||
|
||||
WEAR_RING1, // 21 : 신규 반지슬롯1 (왼쪽)
|
||||
WEAR_RING2, // 22 : 신규 반지슬롯2 (오른쪽)
|
||||
|
||||
WEAR_BELT, // 23 : 신규 벨트슬롯
|
||||
|
||||
WEAR_MAX = 32 //
|
||||
};
|
||||
|
||||
enum EDragonSoulDeckType
|
||||
{
|
||||
DRAGON_SOUL_DECK_0,
|
||||
DRAGON_SOUL_DECK_1,
|
||||
DRAGON_SOUL_DECK_MAX_NUM = 2,
|
||||
|
||||
DRAGON_SOUL_DECK_RESERVED_MAX_NUM = 3, // NOTE: 중요! 아직 사용중이진 않지만, 3페이지 분량을 예약 해 둠. DS DECK을 늘릴 경우 반드시 그 수만큼 RESERVED에서 차감해야 함!
|
||||
};
|
||||
|
||||
enum ESex
|
||||
{
|
||||
SEX_MALE,
|
||||
SEX_FEMALE
|
||||
};
|
||||
|
||||
enum EDirection
|
||||
{
|
||||
DIR_NORTH,
|
||||
DIR_NORTHEAST,
|
||||
DIR_EAST,
|
||||
DIR_SOUTHEAST,
|
||||
DIR_SOUTH,
|
||||
DIR_SOUTHWEST,
|
||||
DIR_WEST,
|
||||
DIR_NORTHWEST,
|
||||
DIR_MAX_NUM
|
||||
};
|
||||
|
||||
#define ABILITY_MAX_LEVEL 10 /* 기술 최대 레벨 */
|
||||
|
||||
enum EAbilityDifficulty
|
||||
{
|
||||
DIFFICULTY_EASY,
|
||||
DIFFICULTY_NORMAL,
|
||||
DIFFICULTY_HARD,
|
||||
DIFFICULTY_VERY_HARD,
|
||||
DIFFICULTY_NUM_TYPES
|
||||
};
|
||||
|
||||
enum EAbilityCategory
|
||||
{
|
||||
CATEGORY_PHYSICAL, /* 신체적 어빌리티 */
|
||||
CATEGORY_MENTAL, /* 정신적 어빌리티 */
|
||||
CATEGORY_ATTRIBUTE, /* 능력 어빌리티 */
|
||||
CATEGORY_NUM_TYPES
|
||||
};
|
||||
|
||||
enum EJobs
|
||||
{
|
||||
JOB_WARRIOR,
|
||||
JOB_ASSASSIN,
|
||||
JOB_SURA,
|
||||
JOB_SHAMAN,
|
||||
JOB_MAX_NUM
|
||||
};
|
||||
|
||||
enum ESkillGroups
|
||||
{
|
||||
SKILL_GROUP_MAX_NUM = 2,
|
||||
};
|
||||
|
||||
enum ERaceFlags
|
||||
{
|
||||
RACE_FLAG_ANIMAL = (1 << 0),
|
||||
RACE_FLAG_UNDEAD = (1 << 1),
|
||||
RACE_FLAG_DEVIL = (1 << 2),
|
||||
RACE_FLAG_HUMAN = (1 << 3),
|
||||
RACE_FLAG_ORC = (1 << 4),
|
||||
RACE_FLAG_MILGYO = (1 << 5),
|
||||
RACE_FLAG_INSECT = (1 << 6),
|
||||
RACE_FLAG_FIRE = (1 << 7),
|
||||
RACE_FLAG_ICE = (1 << 8),
|
||||
RACE_FLAG_DESERT = (1 << 9),
|
||||
RACE_FLAG_TREE = (1 << 10),
|
||||
RACE_FLAG_ATT_ELEC = (1 << 11),
|
||||
RACE_FLAG_ATT_FIRE = (1 << 12),
|
||||
RACE_FLAG_ATT_ICE = (1 << 13),
|
||||
RACE_FLAG_ATT_WIND = (1 << 14),
|
||||
RACE_FLAG_ATT_EARTH = (1 << 15),
|
||||
RACE_FLAG_ATT_DARK = (1 << 16),
|
||||
};
|
||||
|
||||
enum ELoads
|
||||
{
|
||||
LOAD_NONE,
|
||||
LOAD_LIGHT,
|
||||
LOAD_NORMAL,
|
||||
LOAD_HEAVY,
|
||||
LOAD_MASSIVE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
QUICKSLOT_TYPE_NONE,
|
||||
QUICKSLOT_TYPE_ITEM,
|
||||
QUICKSLOT_TYPE_SKILL,
|
||||
QUICKSLOT_TYPE_COMMAND,
|
||||
QUICKSLOT_TYPE_MAX_NUM,
|
||||
};
|
||||
|
||||
enum EParts
|
||||
{
|
||||
PART_MAIN,
|
||||
PART_WEAPON,
|
||||
PART_HEAD,
|
||||
PART_HAIR,
|
||||
|
||||
PART_MAX_NUM,
|
||||
PART_WEAPON_SUB,
|
||||
};
|
||||
|
||||
enum EChatType
|
||||
{
|
||||
CHAT_TYPE_TALKING, /* 그냥 채팅 */
|
||||
CHAT_TYPE_INFO, /* 정보 (아이템을 집었다, 경험치를 얻었다. 등) */
|
||||
CHAT_TYPE_NOTICE, /* 공지사항 */
|
||||
CHAT_TYPE_PARTY, /* 파티말 */
|
||||
CHAT_TYPE_GUILD, /* 길드말 */
|
||||
CHAT_TYPE_COMMAND, /* 일반 명령 */
|
||||
CHAT_TYPE_SHOUT, /* 외치기 */
|
||||
CHAT_TYPE_WHISPER,
|
||||
CHAT_TYPE_BIG_NOTICE,
|
||||
CHAT_TYPE_MONARCH_NOTICE,
|
||||
CHAT_TYPE_MAX_NUM
|
||||
};
|
||||
|
||||
enum EWhisperType
|
||||
{
|
||||
WHISPER_TYPE_NORMAL = 0,
|
||||
WHISPER_TYPE_NOT_EXIST = 1,
|
||||
WHISPER_TYPE_TARGET_BLOCKED = 2,
|
||||
WHISPER_TYPE_SENDER_BLOCKED = 3,
|
||||
WHISPER_TYPE_ERROR = 4,
|
||||
WHISPER_TYPE_GM = 5,
|
||||
WHISPER_TYPE_SYSTEM = 0xFF
|
||||
};
|
||||
|
||||
enum ECharacterPosition
|
||||
{
|
||||
POSITION_GENERAL,
|
||||
POSITION_BATTLE,
|
||||
POSITION_DYING,
|
||||
POSITION_SITTING_CHAIR,
|
||||
POSITION_SITTING_GROUND,
|
||||
POSITION_INTRO,
|
||||
POSITION_MAX_NUM
|
||||
};
|
||||
|
||||
enum EGMLevels
|
||||
{
|
||||
GM_PLAYER,
|
||||
GM_LOW_WIZARD,
|
||||
GM_WIZARD,
|
||||
GM_HIGH_WIZARD,
|
||||
GM_GOD,
|
||||
GM_IMPLEMENTOR
|
||||
};
|
||||
|
||||
enum EMobRank
|
||||
{
|
||||
MOB_RANK_PAWN,
|
||||
MOB_RANK_S_PAWN,
|
||||
MOB_RANK_KNIGHT,
|
||||
MOB_RANK_S_KNIGHT,
|
||||
MOB_RANK_BOSS,
|
||||
MOB_RANK_KING,
|
||||
MOB_RANK_MAX_NUM
|
||||
};
|
||||
|
||||
enum ECharType
|
||||
{
|
||||
CHAR_TYPE_MONSTER,
|
||||
CHAR_TYPE_NPC,
|
||||
CHAR_TYPE_STONE,
|
||||
CHAR_TYPE_WARP,
|
||||
CHAR_TYPE_DOOR,
|
||||
CHAR_TYPE_BUILDING,
|
||||
CHAR_TYPE_PC,
|
||||
CHAR_TYPE_POLYMORPH_PC,
|
||||
CHAR_TYPE_HORSE,
|
||||
CHAR_TYPE_GOTO
|
||||
};
|
||||
|
||||
enum EBattleType
|
||||
{
|
||||
BATTLE_TYPE_MELEE,
|
||||
BATTLE_TYPE_RANGE,
|
||||
BATTLE_TYPE_MAGIC,
|
||||
BATTLE_TYPE_SPECIAL,
|
||||
BATTLE_TYPE_POWER,
|
||||
BATTLE_TYPE_TANKER,
|
||||
BATTLE_TYPE_SUPER_POWER,
|
||||
BATTLE_TYPE_SUPER_TANKER,
|
||||
BATTLE_TYPE_MAX_NUM
|
||||
};
|
||||
|
||||
enum EApplyTypes
|
||||
{
|
||||
APPLY_NONE, // 0
|
||||
APPLY_MAX_HP, // 1
|
||||
APPLY_MAX_SP, // 2
|
||||
APPLY_CON, // 3
|
||||
APPLY_INT, // 4
|
||||
APPLY_STR, // 5
|
||||
APPLY_DEX, // 6
|
||||
APPLY_ATT_SPEED, // 7
|
||||
APPLY_MOV_SPEED, // 8
|
||||
APPLY_CAST_SPEED, // 9
|
||||
APPLY_HP_REGEN, // 10
|
||||
APPLY_SP_REGEN, // 11
|
||||
APPLY_POISON_PCT, // 12
|
||||
APPLY_STUN_PCT, // 13
|
||||
APPLY_SLOW_PCT, // 14
|
||||
APPLY_CRITICAL_PCT, // 15
|
||||
APPLY_PENETRATE_PCT, // 16
|
||||
APPLY_ATTBONUS_HUMAN, // 17
|
||||
APPLY_ATTBONUS_ANIMAL, // 18
|
||||
APPLY_ATTBONUS_ORC, // 19
|
||||
APPLY_ATTBONUS_MILGYO, // 20
|
||||
APPLY_ATTBONUS_UNDEAD, // 21
|
||||
APPLY_ATTBONUS_DEVIL, // 22
|
||||
APPLY_STEAL_HP, // 23
|
||||
APPLY_STEAL_SP, // 24
|
||||
APPLY_MANA_BURN_PCT, // 25
|
||||
APPLY_DAMAGE_SP_RECOVER, // 26
|
||||
APPLY_BLOCK, // 27
|
||||
APPLY_DODGE, // 28
|
||||
APPLY_RESIST_SWORD, // 29
|
||||
APPLY_RESIST_TWOHAND, // 30
|
||||
APPLY_RESIST_DAGGER, // 31
|
||||
APPLY_RESIST_BELL, // 32
|
||||
APPLY_RESIST_FAN, // 33
|
||||
APPLY_RESIST_BOW, // 34
|
||||
APPLY_RESIST_FIRE, // 35
|
||||
APPLY_RESIST_ELEC, // 36
|
||||
APPLY_RESIST_MAGIC, // 37
|
||||
APPLY_RESIST_WIND, // 38
|
||||
APPLY_REFLECT_MELEE, // 39
|
||||
APPLY_REFLECT_CURSE, // 40
|
||||
APPLY_POISON_REDUCE, // 41
|
||||
APPLY_KILL_SP_RECOVER, // 42
|
||||
APPLY_EXP_DOUBLE_BONUS, // 43
|
||||
APPLY_GOLD_DOUBLE_BONUS, // 44
|
||||
APPLY_ITEM_DROP_BONUS, // 45
|
||||
APPLY_POTION_BONUS, // 46
|
||||
APPLY_KILL_HP_RECOVER, // 47
|
||||
APPLY_IMMUNE_STUN, // 48
|
||||
APPLY_IMMUNE_SLOW, // 49
|
||||
APPLY_IMMUNE_FALL, // 50
|
||||
APPLY_SKILL, // 51
|
||||
APPLY_BOW_DISTANCE, // 52
|
||||
APPLY_ATT_GRADE_BONUS, // 53
|
||||
APPLY_DEF_GRADE_BONUS, // 54
|
||||
APPLY_MAGIC_ATT_GRADE, // 55
|
||||
APPLY_MAGIC_DEF_GRADE, // 56
|
||||
APPLY_CURSE_PCT, // 57
|
||||
APPLY_MAX_STAMINA, // 58
|
||||
APPLY_ATTBONUS_WARRIOR, // 59
|
||||
APPLY_ATTBONUS_ASSASSIN, // 60
|
||||
APPLY_ATTBONUS_SURA, // 61
|
||||
APPLY_ATTBONUS_SHAMAN, // 62
|
||||
APPLY_ATTBONUS_MONSTER, // 63
|
||||
APPLY_MALL_ATTBONUS, // 64 공격력 +x%
|
||||
APPLY_MALL_DEFBONUS, // 65 방어력 +x%
|
||||
APPLY_MALL_EXPBONUS, // 66 경험치 +x%
|
||||
APPLY_MALL_ITEMBONUS, // 67 아이템 드롭율 x/10배
|
||||
APPLY_MALL_GOLDBONUS, // 68 돈 드롭율 x/10배
|
||||
APPLY_MAX_HP_PCT, // 69 최대 생명력 +x%
|
||||
APPLY_MAX_SP_PCT, // 70 최대 정신력 +x%
|
||||
APPLY_SKILL_DAMAGE_BONUS, // 71 스킬 데미지 * (100+x)%
|
||||
APPLY_NORMAL_HIT_DAMAGE_BONUS, // 72 평타 데미지 * (100+x)%
|
||||
APPLY_SKILL_DEFEND_BONUS, // 73 스킬 데미지 방어 * (100-x)%
|
||||
APPLY_NORMAL_HIT_DEFEND_BONUS, // 74 평타 데미지 방어 * (100-x)%
|
||||
APPLY_PC_BANG_EXP_BONUS, // 75 PC방 아이템 EXP 보너스
|
||||
APPLY_PC_BANG_DROP_BONUS, // 76 PC방 아이템 드롭율 보너스
|
||||
|
||||
APPLY_EXTRACT_HP_PCT, // 77 사용시 HP 소모
|
||||
|
||||
APPLY_RESIST_WARRIOR, // 78 무사에게 저항
|
||||
APPLY_RESIST_ASSASSIN, // 79 자객에게 저항
|
||||
APPLY_RESIST_SURA, // 80 수라에게 저항
|
||||
APPLY_RESIST_SHAMAN, // 81 무당에게 저항
|
||||
APPLY_ENERGY, // 82 기력
|
||||
APPLY_DEF_GRADE, // 83 방어력. DEF_GRADE_BONUS는 클라에서 두배로 보여지는 의도된 버그(...)가 있다.
|
||||
APPLY_COSTUME_ATTR_BONUS, // 84 코스튬 아이템에 붙은 속성치 보너스
|
||||
APPLY_MAGIC_ATTBONUS_PER, // 85 마법 공격력 +x%
|
||||
APPLY_MELEE_MAGIC_ATTBONUS_PER, // 86 마법 + 밀리 공격력 +x%
|
||||
|
||||
APPLY_RESIST_ICE, // 87 냉기 저항
|
||||
APPLY_RESIST_EARTH, // 88 대지 저항
|
||||
APPLY_RESIST_DARK, // 89 어둠 저항
|
||||
|
||||
APPLY_ANTI_CRITICAL_PCT, //90 크리티컬 저항
|
||||
APPLY_ANTI_PENETRATE_PCT, //91 관통타격 저항
|
||||
|
||||
|
||||
MAX_APPLY_NUM, //
|
||||
};
|
||||
|
||||
enum EOnClickEvents
|
||||
{
|
||||
ON_CLICK_NONE,
|
||||
ON_CLICK_SHOP,
|
||||
ON_CLICK_TALK,
|
||||
ON_CLICK_MAX_NUM
|
||||
};
|
||||
|
||||
enum EOnIdleEvents
|
||||
{
|
||||
ON_IDLE_NONE,
|
||||
ON_IDLE_GENERAL,
|
||||
ON_IDLE_MAX_NUM
|
||||
};
|
||||
|
||||
enum EWindows
|
||||
{
|
||||
RESERVED_WINDOW,
|
||||
INVENTORY,
|
||||
EQUIPMENT,
|
||||
SAFEBOX,
|
||||
MALL,
|
||||
DRAGON_SOUL_INVENTORY,
|
||||
BELT_INVENTORY,
|
||||
#ifdef __AUCTION__
|
||||
AUCTION,
|
||||
#endif
|
||||
GROUND
|
||||
};
|
||||
|
||||
enum EMobSizes
|
||||
{
|
||||
MOBSIZE_RESERVED,
|
||||
MOBSIZE_SMALL,
|
||||
MOBSIZE_MEDIUM,
|
||||
MOBSIZE_BIG
|
||||
};
|
||||
|
||||
enum EAIFlags
|
||||
{
|
||||
AIFLAG_AGGRESSIVE = (1 << 0),
|
||||
AIFLAG_NOMOVE = (1 << 1),
|
||||
AIFLAG_COWARD = (1 << 2),
|
||||
AIFLAG_NOATTACKSHINSU = (1 << 3),
|
||||
AIFLAG_NOATTACKJINNO = (1 << 4),
|
||||
AIFLAG_NOATTACKCHUNJO = (1 << 5),
|
||||
AIFLAG_ATTACKMOB = (1 << 6 ),
|
||||
AIFLAG_BERSERK = (1 << 7),
|
||||
AIFLAG_STONESKIN = (1 << 8),
|
||||
AIFLAG_GODSPEED = (1 << 9),
|
||||
AIFLAG_DEATHBLOW = (1 << 10),
|
||||
AIFLAG_REVIVE = (1 << 11),
|
||||
};
|
||||
|
||||
enum EMobStatType
|
||||
{
|
||||
MOB_STATTYPE_POWER,
|
||||
MOB_STATTYPE_TANKER,
|
||||
MOB_STATTYPE_SUPER_POWER,
|
||||
MOB_STATTYPE_SUPER_TANKER,
|
||||
MOB_STATTYPE_RANGE,
|
||||
MOB_STATTYPE_MAGIC,
|
||||
MOB_STATTYPE_MAX_NUM
|
||||
};
|
||||
|
||||
enum EImmuneFlags
|
||||
{
|
||||
IMMUNE_STUN = (1 << 0),
|
||||
IMMUNE_SLOW = (1 << 1),
|
||||
IMMUNE_FALL = (1 << 2),
|
||||
IMMUNE_CURSE = (1 << 3),
|
||||
IMMUNE_POISON = (1 << 4),
|
||||
IMMUNE_TERROR = (1 << 5),
|
||||
IMMUNE_REFLECT = (1 << 6),
|
||||
};
|
||||
|
||||
enum EMobEnchants
|
||||
{
|
||||
MOB_ENCHANT_CURSE,
|
||||
MOB_ENCHANT_SLOW,
|
||||
MOB_ENCHANT_POISON,
|
||||
MOB_ENCHANT_STUN,
|
||||
MOB_ENCHANT_CRITICAL,
|
||||
MOB_ENCHANT_PENETRATE,
|
||||
MOB_ENCHANTS_MAX_NUM
|
||||
};
|
||||
|
||||
enum EMobResists
|
||||
{
|
||||
MOB_RESIST_SWORD,
|
||||
MOB_RESIST_TWOHAND,
|
||||
MOB_RESIST_DAGGER,
|
||||
MOB_RESIST_BELL,
|
||||
MOB_RESIST_FAN,
|
||||
MOB_RESIST_BOW,
|
||||
MOB_RESIST_FIRE,
|
||||
MOB_RESIST_ELECT,
|
||||
MOB_RESIST_MAGIC,
|
||||
MOB_RESIST_WIND,
|
||||
MOB_RESIST_POISON,
|
||||
MOB_RESISTS_MAX_NUM
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SKILL_ATTR_TYPE_NORMAL = 1,
|
||||
SKILL_ATTR_TYPE_MELEE,
|
||||
SKILL_ATTR_TYPE_RANGE,
|
||||
SKILL_ATTR_TYPE_MAGIC
|
||||
/*
|
||||
SKILL_ATTR_TYPE_FIRE,
|
||||
SKILL_ATTR_TYPE_ICE,
|
||||
SKILL_ATTR_TYPE_ELEC,
|
||||
SKILL_ATTR_TYPE_DARK,
|
||||
*/
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SKILL_NORMAL,
|
||||
SKILL_MASTER,
|
||||
SKILL_GRAND_MASTER,
|
||||
SKILL_PERFECT_MASTER,
|
||||
};
|
||||
|
||||
enum EGuildWarType
|
||||
{
|
||||
GUILD_WAR_TYPE_FIELD,
|
||||
GUILD_WAR_TYPE_BATTLE,
|
||||
GUILD_WAR_TYPE_FLAG,
|
||||
GUILD_WAR_TYPE_MAX_NUM
|
||||
};
|
||||
|
||||
enum EGuildWarState
|
||||
{
|
||||
GUILD_WAR_NONE,
|
||||
GUILD_WAR_SEND_DECLARE,
|
||||
GUILD_WAR_REFUSE,
|
||||
GUILD_WAR_RECV_DECLARE,
|
||||
GUILD_WAR_WAIT_START,
|
||||
GUILD_WAR_CANCEL,
|
||||
GUILD_WAR_ON_WAR,
|
||||
GUILD_WAR_END,
|
||||
GUILD_WAR_OVER,
|
||||
GUILD_WAR_RESERVE,
|
||||
|
||||
GUILD_WAR_DURATION = 30*60, // 1시간
|
||||
GUILD_WAR_WIN_POINT = 1000,
|
||||
GUILD_WAR_LADDER_HALF_PENALTY_TIME = 12*60*60,
|
||||
};
|
||||
|
||||
enum EAttributeSet
|
||||
{
|
||||
ATTRIBUTE_SET_WEAPON,
|
||||
ATTRIBUTE_SET_BODY,
|
||||
ATTRIBUTE_SET_WRIST,
|
||||
ATTRIBUTE_SET_FOOTS,
|
||||
ATTRIBUTE_SET_NECK,
|
||||
ATTRIBUTE_SET_HEAD,
|
||||
ATTRIBUTE_SET_SHIELD,
|
||||
ATTRIBUTE_SET_EAR,
|
||||
ATTRIBUTE_SET_MAX_NUM
|
||||
};
|
||||
|
||||
enum EPrivType
|
||||
{
|
||||
PRIV_NONE,
|
||||
PRIV_ITEM_DROP,
|
||||
PRIV_GOLD_DROP,
|
||||
PRIV_GOLD10_DROP,
|
||||
PRIV_EXP_PCT,
|
||||
MAX_PRIV_NUM,
|
||||
};
|
||||
|
||||
enum EMoneyLogType
|
||||
{
|
||||
MONEY_LOG_RESERVED,
|
||||
MONEY_LOG_MONSTER,
|
||||
MONEY_LOG_SHOP,
|
||||
MONEY_LOG_REFINE,
|
||||
MONEY_LOG_QUEST,
|
||||
MONEY_LOG_GUILD,
|
||||
MONEY_LOG_MISC,
|
||||
MONEY_LOG_MONSTER_KILL,
|
||||
MONEY_LOG_DROP,
|
||||
MONEY_LOG_TYPE_MAX_NUM,
|
||||
};
|
||||
|
||||
enum EPremiumTypes
|
||||
{
|
||||
PREMIUM_EXP, // 경험치가 1.2배
|
||||
PREMIUM_ITEM, // 아이템 드롭율이 2배
|
||||
PREMIUM_SAFEBOX, // 창고가 1칸에서 3칸
|
||||
PREMIUM_AUTOLOOT, // 돈 자동 줍기
|
||||
PREMIUM_FISH_MIND, // 고급 물고기 낚일 확률 상승
|
||||
PREMIUM_MARRIAGE_FAST, // 금실 증가 양을 빠르게합니다.
|
||||
PREMIUM_GOLD, // 돈 드롭율이 1.5배
|
||||
PREMIUM_MAX_NUM = 9
|
||||
};
|
||||
|
||||
enum SPECIAL_EFFECT
|
||||
{
|
||||
SE_NONE,
|
||||
|
||||
SE_HPUP_RED,
|
||||
SE_SPUP_BLUE,
|
||||
SE_SPEEDUP_GREEN,
|
||||
SE_DXUP_PURPLE,
|
||||
SE_CRITICAL,
|
||||
SE_PENETRATE,
|
||||
SE_BLOCK,
|
||||
SE_DODGE,
|
||||
SE_CHINA_FIREWORK,
|
||||
SE_SPIN_TOP,
|
||||
SE_SUCCESS,
|
||||
SE_FAIL,
|
||||
SE_FR_SUCCESS,
|
||||
SE_LEVELUP_ON_14_FOR_GERMANY,
|
||||
SE_LEVELUP_UNDER_15_FOR_GERMANY,
|
||||
SE_PERCENT_DAMAGE1,
|
||||
SE_PERCENT_DAMAGE2,
|
||||
SE_PERCENT_DAMAGE3,
|
||||
|
||||
SE_AUTO_HPUP,
|
||||
SE_AUTO_SPUP,
|
||||
|
||||
SE_EQUIP_RAMADAN_RING, // 라마단 초승달의 반지(71135) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
|
||||
SE_EQUIP_HALLOWEEN_CANDY, // 할로윈 사탕을 착용(-_-;)한 순간에 발동하는 이펙트
|
||||
SE_EQUIP_HAPPINESS_RING, // 크리스마스 행복의 반지(71143) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
|
||||
SE_EQUIP_LOVE_PENDANT, // 발렌타인 사랑의 팬던트(71145) 착용할 때 이펙트 (발동이펙트임, 지속이펙트 아님)
|
||||
} ;
|
||||
|
||||
enum ETeenFlags
|
||||
{
|
||||
TEENFLAG_NONE = 0,
|
||||
TEENFLAG_1HOUR,
|
||||
TEENFLAG_2HOUR,
|
||||
TEENFLAG_3HOUR,
|
||||
TEENFLAG_4HOUR,
|
||||
TEENFLAG_5HOUR,
|
||||
};
|
||||
|
||||
#include "item_length.h"
|
||||
|
||||
// inventory의 position을 나타내는 구조체
|
||||
// int와의 암시적 형변환이 있는 이유는,
|
||||
// 인벤 관련된 모든 함수가 window_type은 받지 않고, cell 하나만 받았기 때문에,(기존에는 인벤이 하나 뿐이어서 inventory type이란게 필요없었기 때문에,)
|
||||
// 인벤 관련 모든 함수 호출부분을 수정하는 것이 난감하기 떄문이다.
|
||||
|
||||
enum EDragonSoulRefineWindowSize
|
||||
{
|
||||
DRAGON_SOUL_REFINE_GRID_MAX = 15,
|
||||
};
|
||||
|
||||
enum EMisc2
|
||||
{
|
||||
DRAGON_SOUL_EQUIP_SLOT_START = INVENTORY_MAX_NUM + WEAR_MAX_NUM,
|
||||
DRAGON_SOUL_EQUIP_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_START + (DS_SLOT_MAX * DRAGON_SOUL_DECK_MAX_NUM),
|
||||
DRAGON_SOUL_EQUIP_RESERVED_SLOT_END = DRAGON_SOUL_EQUIP_SLOT_END + (DS_SLOT_MAX * DRAGON_SOUL_DECK_RESERVED_MAX_NUM),
|
||||
|
||||
BELT_INVENTORY_SLOT_START = DRAGON_SOUL_EQUIP_RESERVED_SLOT_END,
|
||||
BELT_INVENTORY_SLOT_END = BELT_INVENTORY_SLOT_START + BELT_INVENTORY_SLOT_COUNT,
|
||||
|
||||
INVENTORY_AND_EQUIP_SLOT_MAX = BELT_INVENTORY_SLOT_END,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct SItemPos
|
||||
{
|
||||
BYTE window_type;
|
||||
WORD cell;
|
||||
SItemPos ()
|
||||
{
|
||||
window_type = INVENTORY;
|
||||
cell = WORD_MAX;
|
||||
}
|
||||
|
||||
SItemPos (BYTE _window_type, WORD _cell)
|
||||
{
|
||||
window_type = _window_type;
|
||||
cell = _cell;
|
||||
}
|
||||
|
||||
bool IsValidItemPosition() const
|
||||
{
|
||||
switch (window_type)
|
||||
{
|
||||
case RESERVED_WINDOW:
|
||||
return false;
|
||||
case INVENTORY:
|
||||
case EQUIPMENT:
|
||||
case BELT_INVENTORY:
|
||||
return cell < INVENTORY_AND_EQUIP_SLOT_MAX;
|
||||
case DRAGON_SOUL_INVENTORY:
|
||||
return cell < (DRAGON_SOUL_INVENTORY_MAX_NUM);
|
||||
// 동적으로 크기가 정해지는 window는 valid 체크를 할 수가 없다.
|
||||
case SAFEBOX:
|
||||
case MALL:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsEquipPosition() const
|
||||
{
|
||||
return ((INVENTORY == window_type || EQUIPMENT == window_type) && cell >= INVENTORY_MAX_NUM && cell < INVENTORY_MAX_NUM + WEAR_MAX_NUM)
|
||||
|| IsDragonSoulEquipPosition();
|
||||
}
|
||||
|
||||
bool IsDragonSoulEquipPosition() const
|
||||
{
|
||||
return (DRAGON_SOUL_EQUIP_SLOT_START <= cell) && (DRAGON_SOUL_EQUIP_SLOT_END > cell);
|
||||
}
|
||||
|
||||
bool IsBeltInventoryPosition() const
|
||||
{
|
||||
return (BELT_INVENTORY_SLOT_START <= cell) && (BELT_INVENTORY_SLOT_END > cell);
|
||||
}
|
||||
|
||||
bool IsDefaultInventoryPosition() const
|
||||
{
|
||||
return INVENTORY == window_type && cell < INVENTORY_MAX_NUM;
|
||||
}
|
||||
|
||||
bool operator==(const struct SItemPos& rhs) const
|
||||
{
|
||||
return (window_type == rhs.window_type) && (cell == rhs.cell);
|
||||
}
|
||||
bool operator<(const struct SItemPos& rhs) const
|
||||
{
|
||||
return (window_type < rhs.window_type) || ((window_type == rhs.window_type) && (cell < rhs.cell));
|
||||
}
|
||||
} TItemPos;
|
||||
|
||||
const TItemPos NPOS (RESERVED_WINDOW, WORD_MAX);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SHOP_COIN_TYPE_GOLD, // DEFAULT VALUE
|
||||
SHOP_COIN_TYPE_SECONDARY_COIN,
|
||||
} EShopCoinType;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE
|
||||
#define INC_METIN_II_COMMON_NONCOPYABLE_TEMPLATE
|
||||
|
||||
class noncopyable
|
||||
{
|
||||
protected:
|
||||
noncopyable() {}
|
||||
~noncopyable() {}
|
||||
|
||||
private:
|
||||
noncopyable(const noncopyable &);
|
||||
noncopyable& operator = (const noncopyable &);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,185 @@
|
|||
#ifndef __INC_METIN_II_COMMON_POOL_H__
|
||||
#define __INC_METIN_II_COMMON_POOL_H__
|
||||
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
template<typename T>
|
||||
class CPoolNode : public T
|
||||
{
|
||||
public:
|
||||
CPoolNode()
|
||||
{
|
||||
m_pNext = NULL;
|
||||
m_pPrev = NULL;
|
||||
}
|
||||
|
||||
virtual ~CPoolNode()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
CPoolNode<T> * m_pNext;
|
||||
CPoolNode<T> * m_pPrev;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CDynamicPool
|
||||
{
|
||||
public:
|
||||
typedef CPoolNode<T> TNode;
|
||||
|
||||
public:
|
||||
CDynamicPool()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
virtual ~CDynamicPool()
|
||||
{
|
||||
assert(m_pFreeList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear");
|
||||
assert(m_pUsedList==NULL && "CDynamicPool::~CDynamicPool() - NOT Clear");
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
m_nodes = NULL;
|
||||
m_nodeCount = 0;
|
||||
|
||||
m_pFreeList = NULL;
|
||||
m_pUsedList = NULL;
|
||||
}
|
||||
|
||||
void SetName(const char* c_szName)
|
||||
{
|
||||
m_stName = c_szName;
|
||||
}
|
||||
|
||||
DWORD GetCapacity()
|
||||
{
|
||||
return m_nodeCount;
|
||||
}
|
||||
|
||||
T* Alloc()
|
||||
{
|
||||
TNode* pnewNode;
|
||||
|
||||
if (m_pFreeList)
|
||||
{
|
||||
pnewNode = m_pFreeList;
|
||||
m_pFreeList = m_pFreeList->m_pNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
pnewNode = AllocNode();
|
||||
}
|
||||
|
||||
if (!pnewNode)
|
||||
return NULL;
|
||||
|
||||
if (!m_pUsedList)
|
||||
{
|
||||
m_pUsedList = pnewNode;
|
||||
m_pUsedList->m_pPrev = m_pUsedList->m_pNext = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pUsedList->m_pPrev = pnewNode;
|
||||
pnewNode->m_pNext = m_pUsedList;
|
||||
pnewNode->m_pPrev = NULL;
|
||||
m_pUsedList = pnewNode;
|
||||
}
|
||||
//Tracef("%s Pool Alloc %p\n", m_stName.c_str(), pnewNode);
|
||||
return (T*) pnewNode;
|
||||
}
|
||||
|
||||
void Free(T * pdata)
|
||||
{
|
||||
TNode* pfreeNode = (TNode*) pdata;
|
||||
|
||||
if (pfreeNode == m_pUsedList)
|
||||
{
|
||||
if (NULL != (m_pUsedList = m_pUsedList->m_pNext))
|
||||
m_pUsedList->m_pPrev = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pfreeNode->m_pNext)
|
||||
pfreeNode->m_pNext->m_pPrev = pfreeNode->m_pPrev;
|
||||
|
||||
if (pfreeNode->m_pPrev)
|
||||
pfreeNode->m_pPrev->m_pNext = pfreeNode->m_pNext;
|
||||
}
|
||||
|
||||
pfreeNode->m_pPrev = NULL;
|
||||
pfreeNode->m_pNext = m_pFreeList;
|
||||
m_pFreeList = pfreeNode;
|
||||
//Tracef("%s Pool Free\n", m_stName.c_str());
|
||||
}
|
||||
|
||||
void FreeAll()
|
||||
{
|
||||
TNode * pcurNode;
|
||||
TNode * pnextNode;
|
||||
|
||||
pcurNode = m_pUsedList;
|
||||
|
||||
while (pcurNode)
|
||||
{
|
||||
pnextNode = pcurNode->m_pNext;
|
||||
Free(pcurNode);
|
||||
pcurNode = pnextNode;
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
TNode* pcurNode;
|
||||
TNode* pnextNode;
|
||||
|
||||
DWORD count = 0;
|
||||
|
||||
pcurNode = m_pFreeList;
|
||||
while (pcurNode)
|
||||
{
|
||||
pnextNode = pcurNode->m_pNext;
|
||||
delete pcurNode;
|
||||
pcurNode = pnextNode;
|
||||
++count;
|
||||
}
|
||||
m_pFreeList = NULL;
|
||||
|
||||
pcurNode = m_pUsedList;
|
||||
while (pcurNode)
|
||||
{
|
||||
pnextNode = pcurNode->m_pNext;
|
||||
delete pcurNode;
|
||||
pcurNode = pnextNode;
|
||||
++count;
|
||||
}
|
||||
|
||||
m_pUsedList = NULL;
|
||||
|
||||
assert(count==m_nodeCount && "CDynamicPool::Clear()");
|
||||
|
||||
m_nodeCount=0;
|
||||
}
|
||||
|
||||
protected:
|
||||
TNode* AllocNode()
|
||||
{
|
||||
++m_nodeCount;
|
||||
return new TNode;
|
||||
}
|
||||
|
||||
protected:
|
||||
TNode * m_nodes;
|
||||
TNode * m_pFreeList;
|
||||
TNode * m_pUsedList;
|
||||
|
||||
DWORD m_nodeCount;
|
||||
std::string m_stName;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef __INC_SERVICE_H__
|
||||
#define __INC_SERVICE_H__
|
||||
|
||||
#define _IMPROVED_PACKET_ENCRYPTION_ // 패킷 암호화 개선
|
||||
//#define __AUCTION__
|
||||
#define __PET_SYSTEM__
|
||||
#define __UDP_BLOCK__
|
||||
#endif
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef __INC_SINGLETON_H__
|
||||
#define __INC_SINGLETON_H__
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
template <typename T> class singleton
|
||||
{
|
||||
public:
|
||||
static T * ms_singleton;
|
||||
|
||||
singleton()
|
||||
{
|
||||
assert(!ms_singleton);
|
||||
long offset = (long) (T*) 1 - (long) (singleton <T>*) (T*) 1;
|
||||
ms_singleton = (T*) ((long) this + offset);
|
||||
}
|
||||
|
||||
virtual ~singleton()
|
||||
{
|
||||
assert(ms_singleton);
|
||||
ms_singleton = 0;
|
||||
}
|
||||
|
||||
static T & instance()
|
||||
{
|
||||
assert(ms_singleton);
|
||||
return (*ms_singleton);
|
||||
}
|
||||
|
||||
static T & Instance()
|
||||
{
|
||||
assert(ms_singleton);
|
||||
return (*ms_singleton);
|
||||
}
|
||||
|
||||
static T * instance_ptr()
|
||||
{
|
||||
return (ms_singleton);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> T * singleton <T>::ms_singleton = NULL;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,154 @@
|
|||
#ifndef __INC_METIN_II_STL_H__
|
||||
#define __INC_METIN_II_STL_H__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <set>
|
||||
#ifdef __GNUC__
|
||||
#include <ext/functional>
|
||||
#endif
|
||||
|
||||
#ifndef itertype
|
||||
#define itertype(v) typeof((v).begin())
|
||||
#endif
|
||||
|
||||
inline void stl_lowers(std::string& rstRet)
|
||||
{
|
||||
for (size_t i = 0; i < rstRet.length(); ++i)
|
||||
rstRet[i] = tolower(rstRet[i]);
|
||||
}
|
||||
|
||||
struct stringhash
|
||||
{
|
||||
size_t operator () (const std::string & str) const
|
||||
{
|
||||
const unsigned char * s = (const unsigned char*) str.c_str();
|
||||
const unsigned char * end = s + str.size();
|
||||
size_t h = 0;
|
||||
|
||||
while (s < end)
|
||||
{
|
||||
h *= 16777619;
|
||||
h ^= *(s++);
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
};
|
||||
|
||||
// code from tr1/functional_hash.h
|
||||
template<typename T>
|
||||
struct hash;
|
||||
|
||||
template<typename _Tp>
|
||||
struct hash<_Tp*>
|
||||
: public std::unary_function<_Tp*, std::size_t>
|
||||
{
|
||||
std::size_t
|
||||
operator()(_Tp* __p) const
|
||||
{ return reinterpret_cast<std::size_t>(__p); }
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <class container, class Pred>
|
||||
void erase_if (container & a, typename container::iterator first, typename container::iterator past, Pred pred)
|
||||
{
|
||||
while (first != past)
|
||||
if (pred(*first))
|
||||
a.erase(first++);
|
||||
else
|
||||
++first;
|
||||
}
|
||||
|
||||
template <class container>
|
||||
void wipe(container & a)
|
||||
{
|
||||
typename container::iterator first, past;
|
||||
|
||||
first = a.begin();
|
||||
past = a.end();
|
||||
|
||||
while (first != past)
|
||||
delete *(first++);
|
||||
|
||||
a.clear();
|
||||
}
|
||||
|
||||
template <class container>
|
||||
void wipe_second(container & a)
|
||||
{
|
||||
typename container::iterator first, past;
|
||||
|
||||
first = a.begin();
|
||||
past = a.end();
|
||||
|
||||
while (first != past)
|
||||
{
|
||||
delete first->second;
|
||||
++first;
|
||||
}
|
||||
|
||||
a.clear();
|
||||
}
|
||||
|
||||
template <typename T> T MIN(T a, T b)
|
||||
{
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
template <typename T> T MAX(T a, T b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
template <typename T> T MINMAX(T min, T value, T max)
|
||||
{
|
||||
T tv;
|
||||
|
||||
tv = (min > value ? min : value);
|
||||
return (max < tv) ? max : tv;
|
||||
}
|
||||
|
||||
template <class _Ty>
|
||||
class void_mem_fun_t : public unary_function<_Ty *, void>
|
||||
{
|
||||
public:
|
||||
explicit void_mem_fun_t(void (_Ty::*_Pm)()) : _Ptr(_Pm)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(_Ty* p) const
|
||||
{
|
||||
((p->*_Ptr)());
|
||||
}
|
||||
|
||||
private:
|
||||
void (_Ty::*_Ptr)();
|
||||
};
|
||||
|
||||
template<class _Ty> inline
|
||||
void_mem_fun_t<_Ty> void_mem_fun(void (_Ty::*_Pm)())
|
||||
{ return (void_mem_fun_t<_Ty>(_Pm)); }
|
||||
|
||||
template<class _Ty>
|
||||
class void_mem_fun_ref_t : public unary_function<_Ty, void>
|
||||
{
|
||||
public:
|
||||
explicit void_mem_fun_ref_t(void (_Ty::*_Pm)()) : _Ptr(_Pm) {}
|
||||
void operator()(_Ty& x) const
|
||||
{ return ((x.*_Ptr)()); }
|
||||
private:
|
||||
void (_Ty::*_Ptr)();
|
||||
};
|
||||
|
||||
template<class _Ty> inline
|
||||
void_mem_fun_ref_t<_Ty> void_mem_fun_ref(void (_Ty::*_Pm)())
|
||||
{ return (void_mem_fun_ref_t< _Ty>(_Pm)); }
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,20 @@
|
|||
/*********************************************************************
|
||||
* date : 2007.06.07
|
||||
* file : teen_packet.h
|
||||
* author : mhh
|
||||
* description :
|
||||
*/
|
||||
|
||||
#ifndef _teen_packet_h_
|
||||
#define _teen_packet_h_
|
||||
|
||||
#define HEADER_GT_LOGIN 0x10
|
||||
#define HEADER_GT_LOGOUT 0x11
|
||||
|
||||
|
||||
#define HEADER_TG_TEEN_NOTICE 0x12
|
||||
#define HEADER_TG_FORCE_LOGOUT 0x13
|
||||
#define HEADER_TG_LOGIN_NOTICE 0x14
|
||||
|
||||
#endif /* _teen_packet_h_ */
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/*----- atoi function -----*/
|
||||
inline bool str_to_number (bool& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (strtol(in, NULL, 10) != 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (char& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (char) strtol(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (unsigned char& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (unsigned char) strtoul(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (short& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (short) strtol(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (unsigned short& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (unsigned short) strtoul(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (int& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (int) strtol(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (unsigned int& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (unsigned int) strtoul(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (long& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (long) strtol(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (unsigned long& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (unsigned long) strtoul(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (long long& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (long long) strtoull(in, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (float& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (float) strtof(in, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool str_to_number (double& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (double) strtod(in, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
inline bool str_to_number (long double& out, const char *in)
|
||||
{
|
||||
if (0==in || 0==in[0]) return false;
|
||||
|
||||
out = (long double) strtold(in, NULL);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*----- atoi function -----*/
|
|
@ -0,0 +1,37 @@
|
|||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(db CXX)
|
||||
|
||||
file(GLOB_RECURSE sources
|
||||
src/*.cpp src/*.h
|
||||
)
|
||||
|
||||
|
||||
include_directories(${PROJECT_BINARY_DIR}/src/)
|
||||
|
||||
include_directories(src/)
|
||||
|
||||
# Find dependencies
|
||||
find_package(libmysql REQUIRED)
|
||||
find_package(Boost REQUIRED)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${sources})
|
||||
|
||||
# Link dependencies if found
|
||||
if (libmysql_FOUND)
|
||||
target_link_libraries (${PROJECT_NAME} ${MYSQL_LIBRARIES})
|
||||
endif (libmysql_FOUND)
|
||||
|
||||
if (Boost_FOUND)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES} ${Boost_SYSTEM_LIBRARY})
|
||||
endif (Boost_FOUND)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
# Pthreads
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package (Threads REQUIRED)
|
||||
target_link_libraries (${PROJECT_NAME} Threads::Threads)
|
||||
endif (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} libpoly libsql libthecore)
|
|
@ -0,0 +1,719 @@
|
|||
#include "stdafx.h"
|
||||
#ifdef __AUCTION__
|
||||
|
||||
#include "DBManager.h"
|
||||
#include "Peer.h"
|
||||
#include "AuctionManager.h"
|
||||
|
||||
void MyBidBoard::Boot (CPeer* peer)
|
||||
{
|
||||
peer->EncodeWORD(sizeof(DWORD) + sizeof(DWORD) + sizeof(int));
|
||||
peer->EncodeWORD(Size());
|
||||
|
||||
for (TMyBidBoard::iterator pc_it = pc_map.begin(); pc_it != pc_map.end(); pc_it++)
|
||||
{
|
||||
TItemMap* item_map = pc_it->second;
|
||||
for (TItemMap::iterator it = item_map->begin(); it != item_map->end(); it++)
|
||||
{
|
||||
peer->Encode(&(pc_it->first), sizeof(DWORD));
|
||||
peer->Encode(&(it->first), sizeof(DWORD));
|
||||
peer->Encode(&(it->second), sizeof(int));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t MyBidBoard::Size ()
|
||||
{
|
||||
size_t size = 0;
|
||||
for (TMyBidBoard::iterator it = pc_map.begin(); it != pc_map.end(); it++)
|
||||
{
|
||||
size += it->second->size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int MyBidBoard::GetMoney (DWORD player_id, DWORD item_id)
|
||||
{
|
||||
TMyBidBoard::iterator pc_it = pc_map.find (player_id);
|
||||
if (pc_it == pc_map.end())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
TItemMap* item_map = pc_it->second;
|
||||
TItemMap::iterator it = item_map->find (item_id);
|
||||
if (it == item_map->end())
|
||||
return -1;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool MyBidBoard::Delete (DWORD player_id, DWORD item_id)
|
||||
{
|
||||
TMyBidBoard::iterator pc_it = pc_map.find (player_id);
|
||||
if (pc_it == pc_map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
TItemMap* item_map = pc_it->second;
|
||||
TItemMap::iterator it = item_map->find (item_id);
|
||||
if (it == item_map->end())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
item_map->erase(it);
|
||||
}
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM my_bid WHERE player_id = %d and item_id = %d", player_id, item_id);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MyBidBoard::Insert (DWORD player_id, DWORD item_id, int money)
|
||||
{
|
||||
TMyBidBoard::iterator pc_it = pc_map.find (player_id);
|
||||
TItemMap* item_map;
|
||||
if (pc_it == pc_map.end())
|
||||
{
|
||||
item_map = new TItemMap();
|
||||
pc_map.insert (TMyBidBoard::value_type (player_id, item_map));
|
||||
}
|
||||
else
|
||||
item_map = pc_it->second;
|
||||
|
||||
TItemMap::iterator it = item_map->find (item_id);
|
||||
if (it == item_map->end())
|
||||
{
|
||||
item_map->insert (TItemMap::value_type (item_id, money));
|
||||
}
|
||||
else
|
||||
{
|
||||
it->second = money;
|
||||
}
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO my_bid VALUES (%d, %d, %d)", player_id, item_id, money);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
}
|
||||
|
||||
AuctionManager::AuctionManager()
|
||||
{
|
||||
}
|
||||
|
||||
AuctionManager::~AuctionManager()
|
||||
{
|
||||
}
|
||||
|
||||
void AuctionManager::Initialize()
|
||||
{ auction_item_cache_map.clear();
|
||||
LoadAuctionItem();
|
||||
LoadAuctionInfo();
|
||||
LoadSaleInfo();
|
||||
LoadWishInfo();
|
||||
LoadMyBidInfo();
|
||||
}
|
||||
|
||||
void AuctionManager::LoadAuctionItem()
|
||||
{
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT id, owner_id, count, vnum, socket0, socket1, socket2, "
|
||||
"attrtype0, attrvalue0, "
|
||||
"attrtype1, attrvalue1, "
|
||||
"attrtype2, attrvalue2, "
|
||||
"attrtype3, attrvalue3, "
|
||||
"attrtype4, attrvalue4, "
|
||||
"attrtype5, attrvalue5, "
|
||||
"attrtype6, attrvalue6 "
|
||||
"FROM item WHERE window = 'AUCTION'");
|
||||
|
||||
SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
MYSQL_RES *res = msg->Get()->pSQLResult;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int rows;
|
||||
|
||||
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
TPlayerItem item;
|
||||
|
||||
int cur = 0;
|
||||
|
||||
str_to_number(item.id, row[cur++]);
|
||||
str_to_number(item.owner, row[cur++]);
|
||||
item.window = AUCTION;
|
||||
str_to_number(item.count, row[cur++]);
|
||||
str_to_number(item.vnum, row[cur++]);
|
||||
str_to_number(item.alSockets[0], row[cur++]);
|
||||
str_to_number(item.alSockets[1], row[cur++]);
|
||||
str_to_number(item.alSockets[2], row[cur++]);
|
||||
|
||||
for (int j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++)
|
||||
{
|
||||
str_to_number(item.aAttr[j].bType, row[cur++]);
|
||||
str_to_number(item.aAttr[j].sValue, row[cur++]);
|
||||
}
|
||||
InsertItemCache(&item, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void AuctionManager::LoadAuctionInfo()
|
||||
{
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"select * from auction");
|
||||
|
||||
SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
MYSQL_RES *res = msg->Get()->pSQLResult;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int rows;
|
||||
|
||||
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
TAuctionItemInfo auctionItemInfo;
|
||||
|
||||
int cur = 0;
|
||||
|
||||
str_to_number(auctionItemInfo.item_num, row[cur++]);
|
||||
str_to_number(auctionItemInfo.offer_price, row[cur++]);
|
||||
str_to_number(auctionItemInfo.price, row[cur++]);
|
||||
str_to_number(auctionItemInfo.offer_id, row[cur++]);
|
||||
thecore_memcpy (auctionItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1);
|
||||
cur++;
|
||||
str_to_number(auctionItemInfo.empire, row[cur++]);
|
||||
str_to_number(auctionItemInfo.expired_time, row[cur++]);
|
||||
str_to_number(auctionItemInfo.item_id, row[cur++]);
|
||||
str_to_number(auctionItemInfo.bidder_id, row[cur++]);
|
||||
|
||||
InsertAuctionItemInfoCache(&auctionItemInfo, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void AuctionManager::LoadSaleInfo()
|
||||
{
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"select * from sale");
|
||||
|
||||
SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
MYSQL_RES *res = msg->Get()->pSQLResult;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int rows;
|
||||
|
||||
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
TSaleItemInfo saleItemInfo;
|
||||
|
||||
int cur = 0;
|
||||
|
||||
str_to_number(saleItemInfo.item_num, row[cur++]);
|
||||
str_to_number(saleItemInfo.offer_price, row[cur++]);
|
||||
str_to_number(saleItemInfo.price, row[cur++]);
|
||||
str_to_number(saleItemInfo.offer_id, row[cur++]);
|
||||
thecore_memcpy (saleItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1);
|
||||
cur++;
|
||||
str_to_number(saleItemInfo.empire, row[cur++]);
|
||||
str_to_number(saleItemInfo.expired_time, row[cur++]);
|
||||
str_to_number(saleItemInfo.item_id, row[cur++]);
|
||||
str_to_number(saleItemInfo.wisher_id, row[cur++]);
|
||||
|
||||
InsertSaleItemInfoCache(&saleItemInfo, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
void AuctionManager::LoadWishInfo()
|
||||
{
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"select * from wish");
|
||||
|
||||
SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
MYSQL_RES *res = msg->Get()->pSQLResult;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int rows;
|
||||
|
||||
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
TWishItemInfo wishItemInfo;
|
||||
|
||||
int cur = 0;
|
||||
|
||||
str_to_number(wishItemInfo.item_num, row[cur++]);
|
||||
str_to_number(wishItemInfo.offer_price, row[cur++]);
|
||||
str_to_number(wishItemInfo.price, row[cur++]);
|
||||
str_to_number(wishItemInfo.offer_id, row[cur++]);
|
||||
thecore_memcpy (wishItemInfo.shown_name, (char*)row[cur], strlen((char*)row[cur]) +1);
|
||||
cur++;
|
||||
str_to_number(wishItemInfo.empire, row[cur++]);
|
||||
str_to_number(wishItemInfo.expired_time, row[cur++]);
|
||||
|
||||
InsertWishItemInfoCache(&wishItemInfo, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void AuctionManager::LoadMyBidInfo ()
|
||||
{
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"select * from my_bid");
|
||||
|
||||
SQLMsg *msg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
MYSQL_RES *res = msg->Get()->pSQLResult;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int rows;
|
||||
|
||||
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res);
|
||||
|
||||
int cur = 0;
|
||||
DWORD player_id;
|
||||
DWORD item_id;
|
||||
int money;
|
||||
|
||||
str_to_number(player_id, row[cur++]);
|
||||
str_to_number(item_id, row[cur++]);
|
||||
str_to_number(money, row[cur++]);
|
||||
|
||||
InsertMyBid (player_id, item_id, money);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inline CItemCache* AuctionManager::GetItemCache(DWORD item_id)
|
||||
{
|
||||
TItemCacheMap::iterator it = auction_item_cache_map.find (item_id);
|
||||
if (it == auction_item_cache_map.end())
|
||||
return NULL;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void AuctionManager::Boot(CPeer* peer)
|
||||
{
|
||||
peer->EncodeWORD(sizeof(TPlayerItem));
|
||||
peer->EncodeWORD(auction_item_cache_map.size());
|
||||
|
||||
itertype(auction_item_cache_map) auction_item_cache_map_it = auction_item_cache_map.begin();
|
||||
|
||||
while (auction_item_cache_map_it != auction_item_cache_map.end())
|
||||
peer->Encode((auction_item_cache_map_it++)->second->Get(), sizeof(TPlayerItem));
|
||||
|
||||
Auction.Boot(peer);
|
||||
Sale.Boot(peer);
|
||||
Wish.Boot(peer);
|
||||
MyBid.Boot(peer);
|
||||
}
|
||||
|
||||
bool AuctionManager::InsertItemCache(CItemCache *item_cache, bool bSkipQuery)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_cache->Get(false)->id);
|
||||
if (c != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auction_item_cache_map.insert(TItemCacheMap::value_type(item_cache->Get(true)->id, item_cache));
|
||||
item_cache->OnFlush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuctionManager::InsertItemCache(TPlayerItem * pNew, bool bSkipQuery)
|
||||
{
|
||||
CItemCache* c = GetItemCache (pNew->id);
|
||||
if (c != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = new CItemCache();
|
||||
|
||||
c->Put(pNew, bSkipQuery);
|
||||
|
||||
auction_item_cache_map.insert(TItemCacheMap::value_type(pNew->id, c));
|
||||
c->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AuctionManager::DeleteItemCache(DWORD item_id)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c->Delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::EnrollInAuction(CItemCache* item_cache, TAuctionItemInfo &item_info)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_info.item_id);
|
||||
if (c != NULL)
|
||||
{
|
||||
sys_err ("item id : %d is already in AuctionManager", item_info.item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (!Auction.InsertItemInfo (&item_info))
|
||||
{
|
||||
sys_err ("item id : %d is already in AuctionBoard", item_info.item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
item_cache->Get()->window = AUCTION;
|
||||
item_cache->Get()->pos = 9999999;
|
||||
|
||||
InsertItemCache (item_cache);
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::EnrollInSale(CItemCache* item_cache, TSaleItemInfo &item_info)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_info.item_id);
|
||||
if (c != NULL)
|
||||
{
|
||||
sys_err ("item id : %d is already in AuctionManager", item_info.item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (!Wish.GetItemInfoCache (WishBoard::Key (item_info.item_num, item_info.wisher_id)))
|
||||
{
|
||||
sys_err ("item_num : %d, wisher_id : %d is not in wish auction.", item_info.item_num, item_info.wisher_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (!Sale.InsertItemInfo (&item_info))
|
||||
{
|
||||
sys_err ("item id : %d is already in SaleBoard", item_info.item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
item_cache->Get()->window = AUCTION;
|
||||
item_cache->Get()->pos = 999999;
|
||||
|
||||
InsertItemCache (item_cache);
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::EnrollInWish(TWishItemInfo &item_info)
|
||||
{
|
||||
if (!Wish.InsertItemInfo (&item_info))
|
||||
{
|
||||
sys_err ("wisher_id : %d, item_num : %d is already in WishBoard", item_info.offer_id, item_info.item_num);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::Bid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (MyBid.GetMoney (bidder_id, item_id) != 0)
|
||||
{
|
||||
return AUCTION_ALREADY_IN;
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id);
|
||||
TAuctionItemInfo* item_info = item_cache->Get(false);
|
||||
|
||||
if (item_info->is_expired())
|
||||
{
|
||||
return AUCTION_EXPIRED;
|
||||
}
|
||||
|
||||
if ((double)bid_price < (double)item_info->get_bid_price() * 1.05)
|
||||
{
|
||||
return AUCTION_NOT_ENOUGH_MONEY;
|
||||
}
|
||||
|
||||
item_info->set_bid_price(bid_price);
|
||||
item_info->bidder_id = bidder_id;
|
||||
item_info->set_bidder_name (bidder_name);
|
||||
item_cache->OnFlush();
|
||||
|
||||
InsertMyBid (bidder_id, item_id, bid_price);
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::Impur(DWORD purchaser_id, const char* purchaser_name, DWORD item_id)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id);
|
||||
TAuctionItemInfo* item_info = item_cache->Get(false);
|
||||
|
||||
if (item_info->is_expired())
|
||||
{
|
||||
return AUCTION_EXPIRED;
|
||||
}
|
||||
|
||||
// 즉구 해버렸으므로, 경매는 끝났다.
|
||||
item_info->expired_time = 0;
|
||||
item_info->bidder_id = purchaser_id;
|
||||
item_info->set_bidder_name (purchaser_name);
|
||||
item_info->set_bid_price (item_info->get_impur_price());
|
||||
item_cache->OnFlush();
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::GetAuctionedItem (DWORD actor_id, DWORD item_id, TPlayerItem& item)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache* item_info_cache = Auction.GetItemInfoCache(item_id);
|
||||
if (item_info_cache == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
TAuctionItemInfo* item_info = item_info_cache->Get(false);
|
||||
|
||||
if (!item_info->is_expired())
|
||||
{
|
||||
return AUCTION_NOT_EXPIRED;
|
||||
}
|
||||
|
||||
thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem));
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::BuySoldItem (DWORD actor_id, DWORD item_id, TPlayerItem& item)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
CSaleItemInfoCache* item_info_cache = Sale.GetItemInfoCache(item_id);
|
||||
if (item_info_cache == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
TSaleItemInfo* item_info = item_info_cache->Get(false);
|
||||
|
||||
thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem));
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::CancelAuction (DWORD actor_id, DWORD item_id, TPlayerItem& item)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache* item_info_cache = Auction.GetItemInfoCache(item_id);
|
||||
if (item_info_cache == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
TAuctionItemInfo* item_info = item_info_cache->Get(false);
|
||||
|
||||
thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem));
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::CancelWish (DWORD actor_id, DWORD item_num)
|
||||
{
|
||||
if (!Wish.DeleteItemInfoCache (WishBoard::Key (actor_id, item_num)))
|
||||
{
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::CancelSale (DWORD actor_id, DWORD item_id, TPlayerItem& item)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
CSaleItemInfoCache* item_info_cache = Sale.GetItemInfoCache(item_id);
|
||||
if (item_info_cache == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
TSaleItemInfo* item_info = item_info_cache->Get(false);
|
||||
|
||||
thecore_memcpy(&item, c->Get(), sizeof(TPlayerItem));
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::DeleteAuctionItem (DWORD actor_id, DWORD item_id)
|
||||
{
|
||||
if (DeleteItemCache (item_id) == false)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (Auction.DeleteItemInfoCache (item_id) == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::DeleteSaleItem (DWORD actor_id, DWORD item_id)
|
||||
{
|
||||
if (DeleteItemCache (item_id) == false)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
if (Sale.DeleteItemInfoCache (item_id) == NULL)
|
||||
{
|
||||
sys_err ("how can this accident happen?");
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::ReBid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price)
|
||||
{
|
||||
CItemCache* c = GetItemCache (item_id);
|
||||
if (c == NULL)
|
||||
{
|
||||
sys_err ("item id : %d does not exist in auction.", item_id);
|
||||
return AUCTION_FAIL;
|
||||
}
|
||||
|
||||
int money = MyBid.GetMoney (bidder_id, item_id);
|
||||
if (money == -1)
|
||||
{
|
||||
return AUCTION_NOT_IN;
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache* item_cache = Auction.GetItemInfoCache(item_id);
|
||||
TAuctionItemInfo* item_info = item_cache->Get(false);
|
||||
|
||||
if (item_info->is_expired())
|
||||
{
|
||||
return AUCTION_EXPIRED;
|
||||
}
|
||||
|
||||
if ((double)(bid_price + money) < (double)item_info->get_bid_price() * 1.05)
|
||||
{
|
||||
return AUCTION_NOT_ENOUGH_MONEY;
|
||||
}
|
||||
|
||||
item_info->set_bid_price(bid_price + money);
|
||||
item_info->bidder_id = bidder_id;
|
||||
item_info->set_bidder_name (bidder_name);
|
||||
item_cache->OnFlush();
|
||||
|
||||
InsertMyBid (bidder_id, item_id, money + bid_price);
|
||||
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
|
||||
AuctionResult AuctionManager::BidCancel (DWORD bidder_id, DWORD item_id)
|
||||
{
|
||||
if (MyBid.Delete (bidder_id, item_id))
|
||||
{
|
||||
return AUCTION_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AUCTION_NOT_IN;
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,370 @@
|
|||
#ifdef __AUCTION__
|
||||
|
||||
#ifndef __INC_AUCTION_MANAGER_H__
|
||||
#define __INC_AUCTION_MANAGER_H__
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "Cache.h"
|
||||
#include "../../common/auction_table.h"
|
||||
|
||||
class CItemCache;
|
||||
class CAuctionItemInfoCache;
|
||||
class CSaleItemInfoCache;
|
||||
class CWishItemInfoCache;
|
||||
|
||||
template<>
|
||||
class hash<std::pair <DWORD, DWORD> >
|
||||
{ // hash functor
|
||||
public:
|
||||
typedef std::pair <DWORD, DWORD> _Kty;
|
||||
|
||||
size_t operator()(const _Kty& _Keyval) const
|
||||
{ // hash _Keyval to size_t value by pseudorandomizing transform
|
||||
ldiv_t _Qrem = ldiv((size_t)_Keyval.first + (size_t)_Keyval.second, 127773);
|
||||
|
||||
_Qrem.rem = 16807 * _Qrem.rem - 2836 * _Qrem.quot;
|
||||
if (_Qrem.rem < 0)
|
||||
_Qrem.rem += 2147483647;
|
||||
return ((size_t)_Qrem.rem);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class AuctionBoard
|
||||
{
|
||||
public:
|
||||
typedef DWORD Key;
|
||||
typedef CAuctionItemInfoCache ItemInfoCache;
|
||||
typedef TAuctionItemInfo ItemInfo;
|
||||
|
||||
|
||||
public:
|
||||
AuctionBoard() {}
|
||||
~AuctionBoard() {}
|
||||
|
||||
void Boot(CPeer* peer)
|
||||
{
|
||||
peer->EncodeWORD(sizeof(ItemInfo));
|
||||
peer->EncodeWORD(item_cache_map.size());
|
||||
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.begin();
|
||||
|
||||
while (it != item_cache_map.end())
|
||||
peer->Encode((it++)->second->Get(), sizeof(ItemInfo));
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
return item_cache_map.size();
|
||||
}
|
||||
|
||||
ItemInfoCache* GetItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return NULL;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool DeleteItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
it->second->Delete();
|
||||
item_cache_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_id));
|
||||
if (c != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = new ItemInfoCache();
|
||||
|
||||
c->Put(pNew, bSkipQuery);
|
||||
|
||||
item_cache_map.insert(TItemInfoCacheMap::value_type(pNew->item_id, c));
|
||||
c->Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
typedef boost::unordered_map <Key, ItemInfoCache *> TItemInfoCacheMap;
|
||||
TItemInfoCacheMap item_cache_map;
|
||||
};
|
||||
|
||||
class SaleBoard
|
||||
{
|
||||
public:
|
||||
typedef DWORD Key;
|
||||
typedef CSaleItemInfoCache ItemInfoCache;
|
||||
typedef TSaleItemInfo ItemInfo;
|
||||
|
||||
SaleBoard() {}
|
||||
~SaleBoard() {}
|
||||
|
||||
void Boot(CPeer* peer)
|
||||
{
|
||||
peer->EncodeWORD(sizeof(ItemInfo));
|
||||
peer->EncodeWORD(item_cache_map.size());
|
||||
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.begin();
|
||||
|
||||
while (it != item_cache_map.end())
|
||||
peer->Encode((it++)->second->Get(), sizeof(ItemInfo));
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
return item_cache_map.size();
|
||||
}
|
||||
|
||||
ItemInfoCache* GetItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return NULL;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool DeleteItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
it->second->Delete();
|
||||
item_cache_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_id));
|
||||
if (c != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = new ItemInfoCache();
|
||||
|
||||
c->Put(pNew, bSkipQuery);
|
||||
|
||||
item_cache_map.insert(TItemInfoCacheMap::value_type(pNew->item_id, c));
|
||||
c->Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::unordered_map <Key, ItemInfoCache *> TItemInfoCacheMap;
|
||||
TItemInfoCacheMap item_cache_map;
|
||||
};
|
||||
|
||||
class WishBoard
|
||||
{
|
||||
public:
|
||||
typedef std::pair <DWORD, DWORD> Key;
|
||||
typedef CWishItemInfoCache ItemInfoCache;
|
||||
typedef TWishItemInfo ItemInfo;
|
||||
|
||||
WishBoard() {}
|
||||
virtual ~WishBoard() {}
|
||||
|
||||
void Boot(CPeer* peer)
|
||||
{
|
||||
peer->EncodeWORD(sizeof(ItemInfo));
|
||||
peer->EncodeWORD(item_cache_map.size());
|
||||
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.begin();
|
||||
|
||||
while (it != item_cache_map.end())
|
||||
peer->Encode((it++)->second->Get(), sizeof(ItemInfo));
|
||||
}
|
||||
|
||||
size_t Size()
|
||||
{
|
||||
return item_cache_map.size();
|
||||
}
|
||||
|
||||
ItemInfoCache* GetItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return NULL;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool DeleteItemInfoCache (Key key)
|
||||
{
|
||||
TItemInfoCacheMap::iterator it = item_cache_map.find (key);
|
||||
if (it == item_cache_map.end())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
it->second->Delete();
|
||||
item_cache_map.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool InsertItemInfo (ItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
ItemInfoCache* c = GetItemInfoCache (Key (pNew->item_num, pNew->offer_id));
|
||||
if (c != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
c = new ItemInfoCache();
|
||||
|
||||
c->Put(pNew, bSkipQuery);
|
||||
|
||||
item_cache_map.insert(TItemInfoCacheMap::value_type(Key (pNew->item_num, pNew->offer_id), c));
|
||||
c->Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
typedef boost::unordered_map <Key, ItemInfoCache *> TItemInfoCacheMap;
|
||||
TItemInfoCacheMap item_cache_map;
|
||||
};
|
||||
|
||||
// pc가 입찰에 참여했던 경매를 관리.
|
||||
class MyBidBoard
|
||||
{
|
||||
public:
|
||||
MyBidBoard() {}
|
||||
~MyBidBoard() {}
|
||||
|
||||
void Boot(CPeer* peer);
|
||||
size_t Size();
|
||||
|
||||
int GetMoney (DWORD player_id, DWORD item_id);
|
||||
bool Delete (DWORD player_id, DWORD item_id);
|
||||
// 이미 있으면 덮어 씌운다.
|
||||
void Insert (DWORD player_id, DWORD item_id, int money);
|
||||
|
||||
private:
|
||||
typedef std::map <DWORD, int> TItemMap;
|
||||
typedef boost::unordered_map <DWORD, TItemMap*> TMyBidBoard;
|
||||
TMyBidBoard pc_map;
|
||||
};
|
||||
|
||||
class AuctionManager : public singleton <AuctionManager>
|
||||
{
|
||||
private:
|
||||
// auction에 등록된 아이템들.
|
||||
typedef boost::unordered_map<DWORD, CItemCache *> TItemCacheMap;
|
||||
TItemCacheMap auction_item_cache_map;
|
||||
|
||||
// auction에 등록된 정보 중 가격, 등등 아이템 테이블에 포함되지 않는 정보들을 관리하는 것들
|
||||
AuctionBoard Auction;
|
||||
SaleBoard Sale;
|
||||
WishBoard Wish;
|
||||
MyBidBoard MyBid;
|
||||
|
||||
public:
|
||||
AuctionManager();
|
||||
~AuctionManager();
|
||||
|
||||
void Initialize ();
|
||||
void LoadAuctionItem ();
|
||||
|
||||
void LoadAuctionInfo ();
|
||||
void LoadSaleInfo ();
|
||||
void LoadWishInfo ();
|
||||
void LoadMyBidInfo ();
|
||||
|
||||
void Boot(CPeer* peer);
|
||||
|
||||
bool InsertItemCache (CItemCache *item_cache, bool bSkipQuery = false);
|
||||
bool InsertItemCache (TPlayerItem * pNew, bool bSkipQuery = false);
|
||||
bool DeleteItemCache (DWORD item_id);
|
||||
CItemCache* GetItemCache (DWORD item_id);
|
||||
|
||||
size_t GetAuctionItemSize()
|
||||
{
|
||||
return auction_item_cache_map.size();
|
||||
}
|
||||
size_t GetAuctionSize()
|
||||
{
|
||||
return Auction.Size();
|
||||
}
|
||||
size_t GetSaleSize()
|
||||
{
|
||||
return Sale.Size();
|
||||
}
|
||||
size_t GetWishSize()
|
||||
{
|
||||
return Wish.Size();
|
||||
}
|
||||
size_t GetMyBidSize()
|
||||
{
|
||||
return MyBid.Size();
|
||||
}
|
||||
|
||||
void InsertAuctionItemInfoCache (TAuctionItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
Auction.InsertItemInfo (pNew, bSkipQuery);
|
||||
}
|
||||
CAuctionItemInfoCache* GetAuctionItemInfoCache (DWORD item_id)
|
||||
{
|
||||
return Auction.GetItemInfoCache(item_id);
|
||||
}
|
||||
|
||||
void InsertSaleItemInfoCache (TSaleItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
Sale.InsertItemInfo (pNew, bSkipQuery);
|
||||
}
|
||||
CSaleItemInfoCache* GetSaleItemInfoCache (DWORD item_id)
|
||||
{
|
||||
return Sale.GetItemInfoCache(item_id);
|
||||
}
|
||||
|
||||
void InsertWishItemInfoCache (TWishItemInfo *pNew, bool bSkipQuery = false)
|
||||
{
|
||||
Wish.InsertItemInfo (pNew, bSkipQuery);
|
||||
}
|
||||
CWishItemInfoCache* GetWishItemInfoCache (DWORD item_id, DWORD wisher_id)
|
||||
{
|
||||
return Wish.GetItemInfoCache(WishBoard::Key (item_id, wisher_id));
|
||||
}
|
||||
|
||||
void InsertMyBid (DWORD player_id, DWORD item_id, DWORD money)
|
||||
{
|
||||
MyBid.Insert (player_id, item_id, money);
|
||||
}
|
||||
|
||||
AuctionResult EnrollInAuction(CItemCache* item_cache, TAuctionItemInfo &item_info);
|
||||
AuctionResult EnrollInSale(CItemCache* item_cache, TSaleItemInfo &item_info);
|
||||
AuctionResult EnrollInWish(TWishItemInfo &item_info);
|
||||
AuctionResult Bid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price);
|
||||
AuctionResult Impur(DWORD purchaser_id, const char* purchaser_name, DWORD item_id);
|
||||
AuctionResult GetAuctionedItem (DWORD actor_id, DWORD item_id, TPlayerItem& item);
|
||||
AuctionResult BuySoldItem (DWORD actor_id, DWORD item_id, TPlayerItem& item);
|
||||
AuctionResult CancelAuction (DWORD actor_id, DWORD item_id, TPlayerItem& item);
|
||||
AuctionResult CancelWish (DWORD actor_id, DWORD item_num);
|
||||
AuctionResult CancelSale (DWORD actor_id, DWORD item_id, TPlayerItem& item);
|
||||
AuctionResult DeleteAuctionItem (DWORD actor_id, DWORD item_id);
|
||||
AuctionResult DeleteSaleItem (DWORD actor_id, DWORD item_id);
|
||||
AuctionResult ReBid(DWORD bidder_id, const char* bidder_name, DWORD item_id, DWORD bid_price);
|
||||
AuctionResult BidCancel (DWORD bidder_id, DWORD item_id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,214 @@
|
|||
// vim:ts=4 sw=4
|
||||
/*********************************************************************
|
||||
* date : 2007.05.31
|
||||
* file : BlockCountry.cpp
|
||||
* author : mhh
|
||||
* description :
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "BlockCountry.h"
|
||||
|
||||
#include "DBManager.h"
|
||||
|
||||
#define DO_ALL_BLOCK_IP(iter) \
|
||||
for ((iter) = m_block_ip.begin(); (iter) != m_block_ip.end(); ++(iter))
|
||||
|
||||
#define DO_ALL_BLOCK_EXCEPTION(iter) \
|
||||
for ((iter) = m_block_exception.begin(); (iter) != m_block_exception.end(); ++(iter))
|
||||
|
||||
CBlockCountry::CBlockCountry()
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
CBlockCountry::~CBlockCountry()
|
||||
{
|
||||
BLOCK_IP *block_ip;
|
||||
BLOCK_IP_VECTOR::iterator iter;
|
||||
|
||||
DO_ALL_BLOCK_IP(iter)
|
||||
{
|
||||
block_ip = *iter;
|
||||
delete block_ip;
|
||||
}
|
||||
|
||||
m_block_ip.clear();
|
||||
}
|
||||
|
||||
|
||||
bool CBlockCountry::Load()
|
||||
{
|
||||
// load blocked ip
|
||||
{
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT IP_FROM, IP_TO, COUNTRY_NAME FROM iptocountry");
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_ACCOUNT);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_err(" DirectQuery failed(%s)", szQuery);
|
||||
delete pMsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n)
|
||||
{
|
||||
BLOCK_IP *block_ip = new BLOCK_IP;
|
||||
block_ip->ip_from = strtoul(row[0], NULL, 10);
|
||||
block_ip->ip_to = strtoul(row[1], NULL, 10);
|
||||
strlcpy(block_ip->country, row[2], sizeof(block_ip->country));
|
||||
|
||||
m_block_ip.push_back(block_ip);
|
||||
sys_log(0, "BLOCKED_IP : %u - %u", block_ip->ip_from, block_ip->ip_to);
|
||||
|
||||
}
|
||||
delete pMsg;
|
||||
}
|
||||
|
||||
|
||||
// load block exception account
|
||||
{
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT login FROM block_exception");
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_ACCOUNT);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_err(" DirectQuery failed(%s)", szQuery);
|
||||
delete pMsg;
|
||||
return true;
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n)
|
||||
{
|
||||
const char *login = row[0];
|
||||
|
||||
m_block_exception.push_back(strdup(login));
|
||||
|
||||
sys_log(0, "BLOCK_EXCEPTION = %s", login);
|
||||
|
||||
}
|
||||
delete pMsg;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBlockCountry::IsBlockedCountryIp(const char *user_ip)
|
||||
{
|
||||
BLOCK_IP* block_ip;
|
||||
BLOCK_IP_VECTOR::iterator iter;
|
||||
struct in_addr st_addr;
|
||||
|
||||
#ifndef __WIN32__
|
||||
if (0 == inet_aton(user_ip, &st_addr))
|
||||
#else
|
||||
unsigned long in_address;
|
||||
in_address = inet_addr(user_ip);
|
||||
st_addr.s_addr = in_address;
|
||||
if (INADDR_NONE == in_address)
|
||||
#endif
|
||||
return true; // 아이피가 괴상하니 일단 블럭처리
|
||||
|
||||
DO_ALL_BLOCK_IP(iter)
|
||||
{
|
||||
block_ip = *iter;
|
||||
|
||||
if (st_addr.s_addr >= block_ip->ip_from && st_addr.s_addr <= block_ip->ip_to)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CBlockCountry::SendBlockedCountryIp(CPeer *peer)
|
||||
{
|
||||
sys_log(0, "SendBlockedCountryIp start");
|
||||
BLOCK_IP *block_ip;
|
||||
BLOCK_IP_VECTOR::iterator iter;
|
||||
TPacketBlockCountryIp packet;
|
||||
|
||||
DO_ALL_BLOCK_IP(iter)
|
||||
{
|
||||
block_ip = *iter;
|
||||
|
||||
packet.ip_from = block_ip->ip_from;
|
||||
packet.ip_to = block_ip->ip_to;
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_BLOCK_COUNTRY_IP, 0, sizeof(TPacketBlockCountryIp));
|
||||
peer->Encode(&packet, sizeof(packet));
|
||||
}
|
||||
|
||||
sys_log(0, "[DONE] CBlockCountry::SendBlockedCountryIp() : count = %d",
|
||||
m_block_ip.size());
|
||||
sys_log(0, "SendBlockedCountryIp end");
|
||||
} /* end of CBlockCountry::SendBlockedCountryIp() */
|
||||
|
||||
|
||||
void CBlockCountry::SendBlockException(CPeer *peer)
|
||||
{
|
||||
BLOCK_EXCEPTION_VECTOR::iterator iter;
|
||||
|
||||
DO_ALL_BLOCK_EXCEPTION(iter)
|
||||
{
|
||||
const char *login = *iter;
|
||||
|
||||
this->SendBlockExceptionOne(peer, login, BLOCK_EXCEPTION_CMD_ADD);
|
||||
}
|
||||
} /* end of CBlockCountry::SendBlockException() */
|
||||
|
||||
void CBlockCountry::SendBlockExceptionOne(CPeer *peer, const char *login, BYTE cmd)
|
||||
{
|
||||
if (NULL == peer || NULL == login)
|
||||
return;
|
||||
|
||||
if (BLOCK_EXCEPTION_CMD_ADD != cmd && BLOCK_EXCEPTION_CMD_DEL != cmd)
|
||||
return;
|
||||
|
||||
TPacketBlockException packet;
|
||||
|
||||
packet.cmd = cmd;
|
||||
strlcpy(packet.login, login, sizeof(packet.login));
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_BLOCK_EXCEPTION, 0, sizeof(TPacketBlockException));
|
||||
peer->Encode(&packet, sizeof(packet));
|
||||
}
|
||||
|
||||
void CBlockCountry::AddBlockException(const char *login)
|
||||
{
|
||||
BLOCK_EXCEPTION_VECTOR::iterator iter;
|
||||
DO_ALL_BLOCK_EXCEPTION(iter)
|
||||
{
|
||||
const char *saved_login = *iter;
|
||||
|
||||
if (!strcmp(saved_login, login))
|
||||
return;
|
||||
}
|
||||
|
||||
m_block_exception.push_back(strdup(login));
|
||||
return;
|
||||
}
|
||||
|
||||
void CBlockCountry::DelBlockException(const char *login)
|
||||
{
|
||||
BLOCK_EXCEPTION_VECTOR::iterator iter;
|
||||
DO_ALL_BLOCK_EXCEPTION(iter)
|
||||
{
|
||||
const char *saved_login = *iter;
|
||||
|
||||
if (!strcmp(saved_login, login))
|
||||
{
|
||||
::free((void*)saved_login);
|
||||
m_block_exception.erase(iter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// vim: ts=4 sw=4
|
||||
// Date : 2007.05.31
|
||||
// File : BlockCountry.h
|
||||
// Author : mhh
|
||||
// Description :
|
||||
|
||||
#ifndef __INC_METIN_II_BLOCKCOUNTRY_H__
|
||||
#define __INC_METIN_II_BLOCKCOUNTRY_H__
|
||||
|
||||
#include "Peer.h"
|
||||
|
||||
#define MAX_COUNTRY_NAME_LENGTH 50
|
||||
|
||||
class CBlockCountry : public singleton<CBlockCountry>
|
||||
{
|
||||
private:
|
||||
struct BLOCK_IP
|
||||
{
|
||||
DWORD ip_from;
|
||||
DWORD ip_to;
|
||||
char country[MAX_COUNTRY_NAME_LENGTH + 1];
|
||||
};
|
||||
|
||||
typedef std::vector<BLOCK_IP*> BLOCK_IP_VECTOR;
|
||||
BLOCK_IP_VECTOR m_block_ip;
|
||||
|
||||
typedef std::vector<const char*> BLOCK_EXCEPTION_VECTOR;
|
||||
BLOCK_EXCEPTION_VECTOR m_block_exception;
|
||||
|
||||
public:
|
||||
CBlockCountry();
|
||||
~CBlockCountry();
|
||||
|
||||
public:
|
||||
bool Load();
|
||||
bool IsBlockedCountryIp(const char *user_ip);
|
||||
void SendBlockedCountryIp(CPeer *peer);
|
||||
void SendBlockException(CPeer *peer);
|
||||
void SendBlockExceptionOne(CPeer *peer, const char *login, BYTE cmd);
|
||||
void AddBlockException(const char *login);
|
||||
void DelBlockException(const char *login);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,365 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "Cache.h"
|
||||
|
||||
#include "QID.h"
|
||||
#include "ClientManager.h"
|
||||
#ifdef __AUCTION__
|
||||
#include "AuctionManager.h"
|
||||
#endif
|
||||
#include "Main.h"
|
||||
|
||||
extern CPacketInfo g_item_info;
|
||||
extern int g_iPlayerCacheFlushSeconds;
|
||||
extern int g_iItemCacheFlushSeconds;
|
||||
extern int g_test_server;
|
||||
// MYSHOP_PRICE_LIST
|
||||
extern int g_iItemPriceListTableCacheFlushSeconds;
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
//
|
||||
extern int g_item_count;
|
||||
const int auctionMinFlushSec = 1800;
|
||||
|
||||
CItemCache::CItemCache()
|
||||
{
|
||||
m_expireTime = MIN(1800, g_iItemCacheFlushSeconds);
|
||||
}
|
||||
|
||||
CItemCache::~CItemCache()
|
||||
{
|
||||
}
|
||||
|
||||
// 이거 이상한데...
|
||||
// Delete를 했으면, Cache도 해제해야 하는것 아닌가???
|
||||
// 근데 Cache를 해제하는 부분이 없어.
|
||||
// 못 찾은 건가?
|
||||
// 이렇게 해놓으면, 계속 시간이 될 때마다 아이템을 계속 지워...
|
||||
// 이미 사라진 아이템인데... 확인사살??????
|
||||
// fixme
|
||||
// by rtsummit
|
||||
void CItemCache::Delete()
|
||||
{
|
||||
if (m_data.vnum == 0)
|
||||
return;
|
||||
|
||||
//char szQuery[QUERY_MAX_LEN];
|
||||
//szQuery[QUERY_MAX_LEN] = '\0';
|
||||
if (g_test_server)
|
||||
sys_log(0, "ItemCache::Delete : DELETE %u", m_data.id);
|
||||
|
||||
m_data.vnum = 0;
|
||||
m_bNeedQuery = true;
|
||||
m_lastUpdateTime = time(0);
|
||||
OnFlush();
|
||||
|
||||
//m_bNeedQuery = false;
|
||||
//m_lastUpdateTime = time(0) - m_expireTime; // 바로 타임아웃 되도록 하자.
|
||||
}
|
||||
|
||||
void CItemCache::OnFlush()
|
||||
{
|
||||
if (m_data.vnum == 0) // vnum이 0이면 삭제하라고 표시된 것이다.
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM item%s WHERE id=%u", GetTablePostfix(), m_data.id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_DESTROY, 0, NULL);
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "ItemCache::Flush : DELETE %u %s", m_data.id, szQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
long alSockets[ITEM_SOCKET_MAX_NUM];
|
||||
TPlayerItemAttribute aAttr[ITEM_ATTRIBUTE_MAX_NUM];
|
||||
bool isSocket = false, isAttr = false;
|
||||
|
||||
memset(&alSockets, 0, sizeof(long) * ITEM_SOCKET_MAX_NUM);
|
||||
memset(&aAttr, 0, sizeof(TPlayerItemAttribute) * ITEM_ATTRIBUTE_MAX_NUM);
|
||||
|
||||
TPlayerItem * p = &m_data;
|
||||
|
||||
if (memcmp(alSockets, p->alSockets, sizeof(long) * ITEM_SOCKET_MAX_NUM))
|
||||
isSocket = true;
|
||||
|
||||
if (memcmp(aAttr, p->aAttr, sizeof(TPlayerItemAttribute) * ITEM_ATTRIBUTE_MAX_NUM))
|
||||
isAttr = true;
|
||||
|
||||
char szColumns[QUERY_MAX_LEN];
|
||||
char szValues[QUERY_MAX_LEN];
|
||||
char szUpdate[QUERY_MAX_LEN];
|
||||
|
||||
int iLen = snprintf(szColumns, sizeof(szColumns), "id, owner_id, window, pos, count, vnum");
|
||||
|
||||
int iValueLen = snprintf(szValues, sizeof(szValues), "%u, %u, %d, %d, %u, %u",
|
||||
p->id, p->owner, p->window, p->pos, p->count, p->vnum);
|
||||
|
||||
int iUpdateLen = snprintf(szUpdate, sizeof(szUpdate), "owner_id=%u, window=%d, pos=%d, count=%u, vnum=%u",
|
||||
p->owner, p->window, p->pos, p->count, p->vnum);
|
||||
|
||||
if (isSocket)
|
||||
{
|
||||
iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen, ", socket0, socket1, socket2");
|
||||
iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen,
|
||||
", %lu, %lu, %lu", p->alSockets[0], p->alSockets[1], p->alSockets[2]);
|
||||
iUpdateLen += snprintf(szUpdate + iUpdateLen, sizeof(szUpdate) - iUpdateLen,
|
||||
", socket0=%lu, socket1=%lu, socket2=%lu", p->alSockets[0], p->alSockets[1], p->alSockets[2]);
|
||||
}
|
||||
|
||||
if (isAttr)
|
||||
{
|
||||
iLen += snprintf(szColumns + iLen, sizeof(szColumns) - iLen,
|
||||
", attrtype0, attrvalue0, attrtype1, attrvalue1, attrtype2, attrvalue2, attrtype3, attrvalue3"
|
||||
", attrtype4, attrvalue4, attrtype5, attrvalue5, attrtype6, attrvalue6");
|
||||
|
||||
iValueLen += snprintf(szValues + iValueLen, sizeof(szValues) - iValueLen,
|
||||
", %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
|
||||
p->aAttr[0].bType, p->aAttr[0].sValue,
|
||||
p->aAttr[1].bType, p->aAttr[1].sValue,
|
||||
p->aAttr[2].bType, p->aAttr[2].sValue,
|
||||
p->aAttr[3].bType, p->aAttr[3].sValue,
|
||||
p->aAttr[4].bType, p->aAttr[4].sValue,
|
||||
p->aAttr[5].bType, p->aAttr[5].sValue,
|
||||
p->aAttr[6].bType, p->aAttr[6].sValue);
|
||||
|
||||
iUpdateLen += snprintf(szUpdate + iUpdateLen, sizeof(szUpdate) - iUpdateLen,
|
||||
", attrtype0=%d, attrvalue0=%d"
|
||||
", attrtype1=%d, attrvalue1=%d"
|
||||
", attrtype2=%d, attrvalue2=%d"
|
||||
", attrtype3=%d, attrvalue3=%d"
|
||||
", attrtype4=%d, attrvalue4=%d"
|
||||
", attrtype5=%d, attrvalue5=%d"
|
||||
", attrtype6=%d, attrvalue6=%d",
|
||||
p->aAttr[0].bType, p->aAttr[0].sValue,
|
||||
p->aAttr[1].bType, p->aAttr[1].sValue,
|
||||
p->aAttr[2].bType, p->aAttr[2].sValue,
|
||||
p->aAttr[3].bType, p->aAttr[3].sValue,
|
||||
p->aAttr[4].bType, p->aAttr[4].sValue,
|
||||
p->aAttr[5].bType, p->aAttr[5].sValue,
|
||||
p->aAttr[6].bType, p->aAttr[6].sValue);
|
||||
}
|
||||
|
||||
char szItemQuery[QUERY_MAX_LEN + QUERY_MAX_LEN];
|
||||
snprintf(szItemQuery, sizeof(szItemQuery), "REPLACE INTO item%s (%s) VALUES(%s)", GetTablePostfix(), szColumns, szValues);
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "ItemCache::Flush :REPLACE (%s)", szItemQuery);
|
||||
|
||||
CDBManager::instance().ReturnQuery(szItemQuery, QID_ITEM_SAVE, 0, NULL);
|
||||
|
||||
//g_item_info.Add(p->vnum);
|
||||
++g_item_count;
|
||||
}
|
||||
|
||||
m_bNeedQuery = false;
|
||||
}
|
||||
|
||||
//
|
||||
// CPlayerTableCache
|
||||
//
|
||||
CPlayerTableCache::CPlayerTableCache()
|
||||
{
|
||||
m_expireTime = MIN(1800, g_iPlayerCacheFlushSeconds);
|
||||
}
|
||||
|
||||
CPlayerTableCache::~CPlayerTableCache()
|
||||
{
|
||||
}
|
||||
|
||||
void CPlayerTableCache::OnFlush()
|
||||
{
|
||||
if (g_test_server)
|
||||
sys_log(0, "PlayerTableCache::Flush : %s", m_data.name);
|
||||
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
CreatePlayerSaveQuery(szQuery, sizeof(szQuery), &m_data);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_SAVE, 0, NULL);
|
||||
}
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
//
|
||||
// CItemPriceListTableCache class implementation
|
||||
//
|
||||
|
||||
const int CItemPriceListTableCache::s_nMinFlushSec = 1800;
|
||||
|
||||
CItemPriceListTableCache::CItemPriceListTableCache()
|
||||
{
|
||||
m_expireTime = MIN(s_nMinFlushSec, g_iItemPriceListTableCacheFlushSeconds);
|
||||
}
|
||||
|
||||
void CItemPriceListTableCache::UpdateList(const TItemPriceListTable* pUpdateList)
|
||||
{
|
||||
//
|
||||
// 이미 캐싱된 아이템과 중복된 아이템을 찾고 중복되지 않는 이전 정보는 tmpvec 에 넣는다.
|
||||
//
|
||||
|
||||
std::vector<TItemPriceInfo> tmpvec;
|
||||
|
||||
for (uint idx = 0; idx < m_data.byCount; ++idx)
|
||||
{
|
||||
const TItemPriceInfo* pos = pUpdateList->aPriceInfo;
|
||||
for (; pos != pUpdateList->aPriceInfo + pUpdateList->byCount && m_data.aPriceInfo[idx].dwVnum != pos->dwVnum; ++pos)
|
||||
;
|
||||
|
||||
if (pos == pUpdateList->aPriceInfo + pUpdateList->byCount)
|
||||
tmpvec.push_back(m_data.aPriceInfo[idx]);
|
||||
}
|
||||
|
||||
//
|
||||
// pUpdateList 를 m_data 에 복사하고 남은 공간을 tmpvec 의 앞에서 부터 남은 만큼 복사한다.
|
||||
//
|
||||
|
||||
if (pUpdateList->byCount > SHOP_PRICELIST_MAX_NUM)
|
||||
{
|
||||
sys_err("Count overflow!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_data.byCount = pUpdateList->byCount;
|
||||
|
||||
thecore_memcpy(m_data.aPriceInfo, pUpdateList->aPriceInfo, sizeof(TItemPriceInfo) * pUpdateList->byCount);
|
||||
|
||||
int nDeletedNum; // 삭제된 가격정보의 갯수
|
||||
|
||||
if (pUpdateList->byCount < SHOP_PRICELIST_MAX_NUM)
|
||||
{
|
||||
size_t sizeAddOldDataSize = SHOP_PRICELIST_MAX_NUM - pUpdateList->byCount;
|
||||
|
||||
if (tmpvec.size() < sizeAddOldDataSize)
|
||||
sizeAddOldDataSize = tmpvec.size();
|
||||
|
||||
thecore_memcpy(m_data.aPriceInfo + pUpdateList->byCount, &tmpvec[0], sizeof(TItemPriceInfo) * sizeAddOldDataSize);
|
||||
m_data.byCount += sizeAddOldDataSize;
|
||||
|
||||
nDeletedNum = tmpvec.size() - sizeAddOldDataSize;
|
||||
}
|
||||
else
|
||||
nDeletedNum = tmpvec.size();
|
||||
|
||||
m_bNeedQuery = true;
|
||||
|
||||
sys_log(0,
|
||||
"ItemPriceListTableCache::UpdateList : OwnerID[%u] Update [%u] Items, Delete [%u] Items, Total [%u] Items",
|
||||
m_data.dwOwnerID, pUpdateList->byCount, nDeletedNum, m_data.byCount);
|
||||
}
|
||||
|
||||
void CItemPriceListTableCache::OnFlush()
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
//
|
||||
// 이 캐시의 소유자에 대한 기존에 DB 에 저장된 아이템 가격정보를 모두 삭제한다.
|
||||
//
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM myshop_pricelist%s WHERE owner_id = %u", GetTablePostfix(), m_data.dwOwnerID);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_DESTROY, 0, NULL);
|
||||
|
||||
//
|
||||
// 캐시의 내용을 모두 DB 에 쓴다.
|
||||
//
|
||||
|
||||
for (int idx = 0; idx < m_data.byCount; ++idx)
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"INSERT INTO myshop_pricelist%s(owner_id, item_vnum, price) VALUES(%u, %u, %u)",
|
||||
GetTablePostfix(), m_data.dwOwnerID, m_data.aPriceInfo[idx].dwVnum, m_data.aPriceInfo[idx].dwPrice);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_ITEMPRICE_SAVE, 0, NULL);
|
||||
}
|
||||
|
||||
sys_log(0, "ItemPriceListTableCache::Flush : OwnerID[%u] Update [%u]Items", m_data.dwOwnerID, m_data.byCount);
|
||||
|
||||
m_bNeedQuery = false;
|
||||
}
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
#ifdef __AUCTION__
|
||||
CAuctionItemInfoCache::CAuctionItemInfoCache()
|
||||
{
|
||||
m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds);
|
||||
}
|
||||
|
||||
CAuctionItemInfoCache::~CAuctionItemInfoCache()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void CAuctionItemInfoCache::Delete()
|
||||
{
|
||||
if (m_data.item_num == 0)
|
||||
return;
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "CAuctionItemInfoCache::Delete : DELETE %u", m_data.item_id);
|
||||
|
||||
m_data.item_num = 0;
|
||||
m_bNeedQuery = true;
|
||||
m_lastUpdateTime = time(0);
|
||||
OnFlush();
|
||||
delete this;
|
||||
}
|
||||
|
||||
void CAuctionItemInfoCache::OnFlush()
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
if (m_data.item_num == 0)
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM auction where item_id = %d", m_data.item_id);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO auction VALUES (%u, %d, %d, %u, \"%s\", %u, %u, %u, %u)",
|
||||
m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time,
|
||||
m_data.item_id, m_data.bidder_id);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
}
|
||||
}
|
||||
|
||||
CSaleItemInfoCache::CSaleItemInfoCache()
|
||||
{
|
||||
m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds);
|
||||
}
|
||||
|
||||
CSaleItemInfoCache::~CSaleItemInfoCache()
|
||||
{
|
||||
}
|
||||
|
||||
void CSaleItemInfoCache::Delete()
|
||||
{
|
||||
}
|
||||
|
||||
void CSaleItemInfoCache::OnFlush()
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO sale VALUES (%u, %d, %d, %u, \"%s\", %u, %u, %u, %u)",
|
||||
m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time,
|
||||
m_data.item_id, m_data.wisher_id);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
}
|
||||
|
||||
CWishItemInfoCache::CWishItemInfoCache()
|
||||
{
|
||||
m_expireTime = MIN (auctionMinFlushSec, g_iItemCacheFlushSeconds);
|
||||
}
|
||||
|
||||
CWishItemInfoCache::~CWishItemInfoCache()
|
||||
{
|
||||
}
|
||||
|
||||
void CWishItemInfoCache::Delete()
|
||||
{
|
||||
}
|
||||
|
||||
void CWishItemInfoCache::OnFlush()
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO wish VALUES (%u, %d, %d, %u, \"%s\", %u, %d)",
|
||||
m_data.item_num, m_data.offer_price, m_data.price, m_data.offer_id, m_data.shown_name, (DWORD)m_data.empire, (DWORD)m_data.expired_time);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,97 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_DB_CACHE_H__
|
||||
#define __INC_DB_CACHE_H__
|
||||
|
||||
#include "../../common/cache.h"
|
||||
#include "../../common/auction_table.h"
|
||||
|
||||
class CItemCache : public cache<TPlayerItem>
|
||||
{
|
||||
public:
|
||||
CItemCache();
|
||||
virtual ~CItemCache();
|
||||
|
||||
void Delete();
|
||||
virtual void OnFlush();
|
||||
};
|
||||
|
||||
class CPlayerTableCache : public cache<TPlayerTable>
|
||||
{
|
||||
public:
|
||||
CPlayerTableCache();
|
||||
virtual ~CPlayerTableCache();
|
||||
|
||||
virtual void OnFlush();
|
||||
|
||||
DWORD GetLastUpdateTime() { return m_lastUpdateTime; }
|
||||
};
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
/**
|
||||
* @class CItemPriceListTableCache
|
||||
* @brief 개인상점의 아이템 가격정보 리스트에 대한 캐시 class
|
||||
* @version 05/06/10 Bang2ni - First release.
|
||||
*/
|
||||
class CItemPriceListTableCache : public cache< TItemPriceListTable >
|
||||
{
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
* 캐시 만료 시간을 설정한다.
|
||||
*/
|
||||
CItemPriceListTableCache(void);
|
||||
|
||||
/// 리스트 갱신
|
||||
/**
|
||||
* @param [in] pUpdateList 갱신할 리스트
|
||||
*
|
||||
* 캐시된 가격정보를 갱신한다.
|
||||
* 가격정보 리스트가 가득 찼을 경우 기존에 캐싱된 정보들을 뒤에서 부터 삭제한다.
|
||||
*/
|
||||
void UpdateList(const TItemPriceListTable* pUpdateList);
|
||||
|
||||
/// 가격정보를 DB 에 기록한다.
|
||||
virtual void OnFlush(void);
|
||||
|
||||
private:
|
||||
|
||||
static const int s_nMinFlushSec; ///< Minimum cache expire time
|
||||
};
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
#ifdef __AUCTION__
|
||||
|
||||
class CAuctionItemInfoCache : public cache <TAuctionItemInfo>
|
||||
{
|
||||
public:
|
||||
typedef TWishItemInfo value_type;
|
||||
CAuctionItemInfoCache();
|
||||
virtual ~CAuctionItemInfoCache();
|
||||
|
||||
void Delete();
|
||||
virtual void OnFlush();
|
||||
};
|
||||
|
||||
class CSaleItemInfoCache : public cache <TSaleItemInfo>
|
||||
{
|
||||
public:
|
||||
typedef TWishItemInfo value_type;
|
||||
CSaleItemInfoCache();
|
||||
virtual ~CSaleItemInfoCache();
|
||||
|
||||
void Delete();
|
||||
virtual void OnFlush();
|
||||
};
|
||||
|
||||
class CWishItemInfoCache : public cache <TWishItemInfo>
|
||||
{
|
||||
public:
|
||||
typedef TWishItemInfo value_type;
|
||||
CWishItemInfoCache();
|
||||
virtual ~CWishItemInfoCache();
|
||||
|
||||
void Delete();
|
||||
virtual void OnFlush();
|
||||
};
|
||||
#endif
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,578 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_CLIENTMANAGER_H__
|
||||
#define __INC_CLIENTMANAGER_H__
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
|
||||
#include "../../common/stl.h"
|
||||
#include "../../common/building.h"
|
||||
#include "../../common/auction_table.h"
|
||||
|
||||
#include "Peer.h"
|
||||
#include "DBManager.h"
|
||||
#include "LoginData.h"
|
||||
|
||||
class CPlayerTableCache;
|
||||
class CItemCache;
|
||||
class CItemPriceListTableCache;
|
||||
|
||||
class CPacketInfo
|
||||
{
|
||||
public:
|
||||
void Add(int header);
|
||||
void Reset();
|
||||
|
||||
std::map<int, int> m_map_info;
|
||||
};
|
||||
|
||||
size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab);
|
||||
|
||||
class CClientManager : public CNetBase, public singleton<CClientManager>
|
||||
{
|
||||
public:
|
||||
typedef std::list<CPeer *> TPeerList;
|
||||
typedef boost::unordered_map<DWORD, CPlayerTableCache *> TPlayerTableCacheMap;
|
||||
typedef boost::unordered_map<DWORD, CItemCache *> TItemCacheMap;
|
||||
typedef boost::unordered_set<CItemCache *, boost::hash<CItemCache*> > TItemCacheSet;
|
||||
typedef boost::unordered_map<DWORD, TItemCacheSet *> TItemCacheSetPtrMap;
|
||||
typedef boost::unordered_map<DWORD, CItemPriceListTableCache*> TItemPriceListCacheMap;
|
||||
typedef boost::unordered_map<short, BYTE> TChannelStatusMap;
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
/// 아이템 가격정보 리스트 요청 정보
|
||||
/**
|
||||
* first: Peer handle
|
||||
* second: 요청한 플레이어의 ID
|
||||
*/
|
||||
typedef std::pair< DWORD, DWORD > TItemPricelistReqInfo;
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
class ClientHandleInfo
|
||||
{
|
||||
public:
|
||||
DWORD dwHandle;
|
||||
DWORD account_id;
|
||||
DWORD player_id;
|
||||
BYTE account_index;
|
||||
char login[LOGIN_MAX_LEN + 1];
|
||||
char safebox_password[SAFEBOX_PASSWORD_MAX_LEN + 1];
|
||||
char ip[MAX_HOST_LENGTH + 1];
|
||||
|
||||
TAccountTable * pAccountTable;
|
||||
TSafeboxTable * pSafebox;
|
||||
|
||||
ClientHandleInfo(DWORD argHandle, DWORD dwPID = 0)
|
||||
{
|
||||
dwHandle = argHandle;
|
||||
pSafebox = NULL;
|
||||
pAccountTable = NULL;
|
||||
player_id = dwPID;
|
||||
};
|
||||
//독일선물기능용 생성자
|
||||
ClientHandleInfo(DWORD argHandle, DWORD dwPID, DWORD accountId)
|
||||
{
|
||||
dwHandle = argHandle;
|
||||
pSafebox = NULL;
|
||||
pAccountTable = NULL;
|
||||
player_id = dwPID;
|
||||
account_id = accountId;
|
||||
};
|
||||
|
||||
~ClientHandleInfo()
|
||||
{
|
||||
if (pSafebox)
|
||||
{
|
||||
delete pSafebox;
|
||||
pSafebox = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
CClientManager();
|
||||
~CClientManager();
|
||||
|
||||
bool Initialize();
|
||||
time_t GetCurrentTime();
|
||||
|
||||
void MainLoop();
|
||||
void Quit();
|
||||
|
||||
void SetTablePostfix(const char* c_pszTablePostfix);
|
||||
void SetPlayerIDStart(int iIDStart);
|
||||
int GetPlayerIDStart() { return m_iPlayerIDStart; }
|
||||
|
||||
int GetPlayerDeleteLevelLimit() { return m_iPlayerDeleteLevelLimit; }
|
||||
|
||||
void SetChinaEventServer(bool flag) { m_bChinaEventServer = flag; }
|
||||
bool IsChinaEventServer() { return m_bChinaEventServer; }
|
||||
|
||||
DWORD GetUserCount(); // 접속된 사용자 수를 리턴 한다.
|
||||
|
||||
void SendAllGuildSkillRechargePacket();
|
||||
void SendTime();
|
||||
|
||||
CPlayerTableCache * GetPlayerCache(DWORD id);
|
||||
void PutPlayerCache(TPlayerTable * pNew);
|
||||
|
||||
void CreateItemCacheSet(DWORD dwID);
|
||||
TItemCacheSet * GetItemCacheSet(DWORD dwID);
|
||||
void FlushItemCacheSet(DWORD dwID);
|
||||
|
||||
CItemCache * GetItemCache(DWORD id);
|
||||
void PutItemCache(TPlayerItem * pNew, bool bSkipQuery = false);
|
||||
bool DeleteItemCache(DWORD id);
|
||||
|
||||
void UpdatePlayerCache();
|
||||
void UpdateItemCache();
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
/// 가격정보 리스트 캐시를 가져온다.
|
||||
/**
|
||||
* @param [in] dwID 가격정보 리스트의 소유자.(플레이어 ID)
|
||||
* @return 가격정보 리스트 캐시의 포인터
|
||||
*/
|
||||
CItemPriceListTableCache* GetItemPriceListCache(DWORD dwID);
|
||||
|
||||
/// 가격정보 리스트 캐시를 넣는다.
|
||||
/**
|
||||
* @param [in] pItemPriceList 캐시에 넣을 아이템 가격정보 리스트
|
||||
*
|
||||
* 캐시가 이미 있으면 Update 가 아닌 replace 한다.
|
||||
*/
|
||||
void PutItemPriceListCache(const TItemPriceListTable* pItemPriceList);
|
||||
|
||||
|
||||
/// Flush 시간이 만료된 아이템 가격정보 리스트 캐시를 Flush 해주고 캐시에서 삭제한다.
|
||||
void UpdateItemPriceListCache(void);
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
|
||||
void SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable);
|
||||
|
||||
void SetCacheFlushCountLimit(int iLimit);
|
||||
|
||||
template <class Func>
|
||||
Func for_each_peer(Func f);
|
||||
|
||||
CPeer * GetAnyPeer();
|
||||
|
||||
void ForwardPacket(BYTE header, const void* data, int size, BYTE bChannel = 0, CPeer * except = NULL);
|
||||
|
||||
void SendNotice(const char * c_pszFormat, ...);
|
||||
|
||||
char* GetCommand(char* str); //독일선물기능에서 명령어 얻는 함수
|
||||
void ItemAward(CPeer * peer, char* login); //독일 선물 기능
|
||||
|
||||
protected:
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
bool InitializeTables();
|
||||
bool InitializeShopTable();
|
||||
bool InitializeMobTable();
|
||||
bool InitializeItemTable();
|
||||
bool InitializeQuestItemTable();
|
||||
bool InitializeSkillTable();
|
||||
bool InitializeRefineTable();
|
||||
bool InitializeBanwordTable();
|
||||
bool InitializeItemAttrTable();
|
||||
bool InitializeItemRareTable();
|
||||
bool InitializeLandTable();
|
||||
bool InitializeObjectProto();
|
||||
bool InitializeObjectTable();
|
||||
bool InitializeMonarch();
|
||||
|
||||
// mob_proto.txt, item_proto.txt에서 읽은 mob_proto, item_proto를 real db에 반영.
|
||||
// item_proto, mob_proto를 db에 반영하지 않아도, 게임 돌아가는데는 문제가 없지만,
|
||||
// 운영툴 등에서 db의 item_proto, mob_proto를 읽어 쓰기 때문에 문제가 발생한다.
|
||||
bool MirrorMobTableIntoDB();
|
||||
bool MirrorItemTableIntoDB();
|
||||
|
||||
void AddPeer(socket_t fd);
|
||||
void RemovePeer(CPeer * pPeer);
|
||||
CPeer * GetPeer(IDENT ident);
|
||||
|
||||
int AnalyzeQueryResult(SQLMsg * msg);
|
||||
int AnalyzeErrorMsg(CPeer * peer, SQLMsg * msg);
|
||||
|
||||
int Process();
|
||||
|
||||
void ProcessPackets(CPeer * peer);
|
||||
|
||||
CLoginData * GetLoginData(DWORD dwKey);
|
||||
CLoginData * GetLoginDataByLogin(const char * c_pszLogin);
|
||||
CLoginData * GetLoginDataByAID(DWORD dwAID);
|
||||
|
||||
void InsertLoginData(CLoginData * pkLD);
|
||||
void DeleteLoginData(CLoginData * pkLD);
|
||||
|
||||
bool InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP);
|
||||
bool DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle);
|
||||
bool FindLogonAccount(const char * c_pszLogin);
|
||||
|
||||
void GuildCreate(CPeer * peer, DWORD dwGuildID);
|
||||
void GuildSkillUpdate(CPeer * peer, TPacketGuildSkillUpdate* p);
|
||||
void GuildExpUpdate(CPeer * peer, TPacketGuildExpUpdate* p);
|
||||
void GuildAddMember(CPeer * peer, TPacketGDGuildAddMember* p);
|
||||
void GuildChangeGrade(CPeer * peer, TPacketGuild* p);
|
||||
void GuildRemoveMember(CPeer * peer, TPacketGuild* p);
|
||||
void GuildChangeMemberData(CPeer * peer, TPacketGuildChangeMemberData* p);
|
||||
void GuildDisband(CPeer * peer, TPacketGuild * p);
|
||||
void GuildWar(CPeer * peer, TPacketGuildWar * p);
|
||||
void GuildWarScore(CPeer * peer, TPacketGuildWarScore * p);
|
||||
void GuildChangeLadderPoint(TPacketGuildLadderPoint* p);
|
||||
void GuildUseSkill(TPacketGuildUseSkill* p);
|
||||
void GuildDepositMoney(TPacketGDGuildMoney* p);
|
||||
void GuildWithdrawMoney(CPeer* peer, TPacketGDGuildMoney* p);
|
||||
void GuildWithdrawMoneyGiveReply(TPacketGDGuildMoneyWithdrawGiveReply* p);
|
||||
void GuildWarBet(TPacketGDGuildWarBet * p);
|
||||
void GuildChangeMaster(TPacketChangeGuildMaster* p);
|
||||
|
||||
void SetGuildWarEndTime(DWORD guild_id1, DWORD guild_id2, time_t tEndTime);
|
||||
|
||||
void QUERY_BOOT(CPeer * peer, TPacketGDBoot * p);
|
||||
|
||||
void QUERY_LOGIN(CPeer * peer, DWORD dwHandle, SLoginPacket* data);
|
||||
void QUERY_LOGOUT(CPeer * peer, DWORD dwHandle, const char *);
|
||||
|
||||
void RESULT_LOGIN(CPeer * peer, SQLMsg *msg);
|
||||
|
||||
void QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket*);
|
||||
void RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID);
|
||||
void RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo);
|
||||
void RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID);
|
||||
void RESULT_QUEST_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID);
|
||||
void RESULT_AFFECT_LOAD(CPeer * pkPeer, MYSQL_RES * pRes, DWORD dwHandle);
|
||||
|
||||
// PLAYER_INDEX_CREATE_BUG_FIX
|
||||
void RESULT_PLAYER_INDEX_CREATE(CPeer *pkPeer, SQLMsg *msg);
|
||||
// END_PLAYER_INDEX_CREATE_BUG_FIX
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
/// 가격정보 로드 쿼리에 대한 Result 처리
|
||||
/**
|
||||
* @param peer 가격정보를 요청한 Game server 의 peer 객체 포인터
|
||||
* @param pMsg 쿼리의 Result 로 받은 객체의 포인터
|
||||
*
|
||||
* 로드된 가격정보 리스트를 캐시에 저장하고 peer 에게 리스트를 보내준다.
|
||||
*/
|
||||
void RESULT_PRICELIST_LOAD(CPeer* peer, SQLMsg* pMsg);
|
||||
|
||||
/// 가격정보 업데이트를 위한 로드 쿼리에 대한 Result 처리
|
||||
/**
|
||||
* @param pMsg 쿼리의 Result 로 받은 객체의 포인터
|
||||
*
|
||||
* 로드된 정보로 가격정보 리스트 캐시를 만들고 업데이트 받은 가격정보로 업데이트 한다.
|
||||
*/
|
||||
void RESULT_PRICELIST_LOAD_FOR_UPDATE(SQLMsg* pMsg);
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
void QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable*);
|
||||
|
||||
void __QUERY_PLAYER_CREATE(CPeer * peer, DWORD dwHandle, TPlayerCreatePacket *);
|
||||
void __QUERY_PLAYER_DELETE(CPeer * peer, DWORD dwHandle, TPlayerDeletePacket *);
|
||||
void __RESULT_PLAYER_DELETE(CPeer * peer, SQLMsg* msg);
|
||||
|
||||
void QUERY_PLAYER_COUNT(CPeer * pkPeer, TPlayerCountPacket *);
|
||||
|
||||
void QUERY_ITEM_SAVE(CPeer * pkPeer, const char * c_pData);
|
||||
void QUERY_ITEM_DESTROY(CPeer * pkPeer, const char * c_pData);
|
||||
void QUERY_ITEM_FLUSH(CPeer * pkPeer, const char * c_pData);
|
||||
|
||||
|
||||
void QUERY_QUEST_SAVE(CPeer * pkPeer, TQuestTable *, DWORD dwLen);
|
||||
void QUERY_ADD_AFFECT(CPeer * pkPeer, TPacketGDAddAffect * p);
|
||||
void QUERY_REMOVE_AFFECT(CPeer * pkPeer, TPacketGDRemoveAffect * p);
|
||||
|
||||
void QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafeboxLoadPacket *, bool bMall);
|
||||
void QUERY_SAFEBOX_SAVE(CPeer * pkPeer, TSafeboxTable * pTable);
|
||||
void QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p);
|
||||
void QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p);
|
||||
|
||||
void RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg);
|
||||
void RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg);
|
||||
void RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg);
|
||||
void RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg);
|
||||
|
||||
void QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpireSelectPacket * p);
|
||||
void QUERY_SETUP(CPeer * pkPeer, DWORD dwHandle, const char * c_pData);
|
||||
|
||||
void SendPartyOnSetup(CPeer * peer);
|
||||
|
||||
void QUERY_HIGHSCORE_REGISTER(CPeer * peer, TPacketGDHighscore* data);
|
||||
void RESULT_HIGHSCORE_REGISTER(CPeer * pkPeer, SQLMsg * msg);
|
||||
|
||||
void QUERY_FLUSH_CACHE(CPeer * pkPeer, const char * c_pData);
|
||||
|
||||
void QUERY_PARTY_CREATE(CPeer * peer, TPacketPartyCreate* p);
|
||||
void QUERY_PARTY_DELETE(CPeer * peer, TPacketPartyDelete* p);
|
||||
void QUERY_PARTY_ADD(CPeer * peer, TPacketPartyAdd* p);
|
||||
void QUERY_PARTY_REMOVE(CPeer * peer, TPacketPartyRemove* p);
|
||||
void QUERY_PARTY_STATE_CHANGE(CPeer * peer, TPacketPartyStateChange* p);
|
||||
void QUERY_PARTY_SET_MEMBER_LEVEL(CPeer * peer, TPacketPartySetMemberLevel* p);
|
||||
|
||||
void QUERY_RELOAD_PROTO();
|
||||
|
||||
void QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p);
|
||||
void GetPlayerFromRes(TPlayerTable * player_table, MYSQL_RES* res);
|
||||
|
||||
void QUERY_SMS(CPeer * pkPeer, TPacketGDSMS * p);
|
||||
void QUERY_LOGIN_KEY(CPeer * pkPeer, TPacketGDLoginKey * p);
|
||||
|
||||
void AddGuildPriv(TPacketGiveGuildPriv* p);
|
||||
void AddEmpirePriv(TPacketGiveEmpirePriv* p);
|
||||
void AddCharacterPriv(TPacketGiveCharacterPriv* p);
|
||||
|
||||
void MoneyLog(TPacketMoneyLog* p);
|
||||
|
||||
void QUERY_AUTH_LOGIN(CPeer * pkPeer, DWORD dwHandle, TPacketGDAuthLogin * p);
|
||||
|
||||
void QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p);
|
||||
void RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg);
|
||||
|
||||
void ChargeCash(const TRequestChargeCash * p);
|
||||
|
||||
void LoadEventFlag();
|
||||
void SetEventFlag(TPacketSetEventFlag* p);
|
||||
void SendEventFlagsOnSetup(CPeer* peer);
|
||||
|
||||
void BillingExpire(TPacketBillingExpire * p);
|
||||
void BillingCheck(const char * data);
|
||||
|
||||
void SendAllLoginToBilling();
|
||||
void SendLoginToBilling(CLoginData * pkLD, bool bLogin);
|
||||
|
||||
// 결혼
|
||||
void MarriageAdd(TPacketMarriageAdd * p);
|
||||
void MarriageUpdate(TPacketMarriageUpdate * p);
|
||||
void MarriageRemove(TPacketMarriageRemove * p);
|
||||
|
||||
void WeddingRequest(TPacketWeddingRequest * p);
|
||||
void WeddingReady(TPacketWeddingReady * p);
|
||||
void WeddingEnd(TPacketWeddingEnd * p);
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
// 개인상점 가격정보
|
||||
|
||||
/// 아이템 가격정보 리스트 업데이트 패킷(HEADER_GD_MYSHOP_PRICELIST_UPDATE) 처리함수
|
||||
/**
|
||||
* @param [in] pPacket 패킷 데이터의 포인터
|
||||
*/
|
||||
void MyshopPricelistUpdate(const TPacketMyshopPricelistHeader* pPacket);
|
||||
|
||||
/// 아이템 가격정보 리스트 요청 패킷(HEADER_GD_MYSHOP_PRICELIST_REQ) 처리함수
|
||||
/**
|
||||
* @param peer 패킷을 보낸 Game server 의 peer 객체의 포인터
|
||||
* @param [in] dwHandle 가격정보를 요청한 peer 의 핸들
|
||||
* @param [in] dwPlayerID 가격정보 리스트를 요청한 플레이어의 ID
|
||||
*/
|
||||
void MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD dwPlayerID);
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
// Building
|
||||
void CreateObject(TPacketGDCreateObject * p);
|
||||
void DeleteObject(DWORD dwID);
|
||||
void UpdateLand(DWORD * pdw);
|
||||
|
||||
// VCard
|
||||
void VCard(TPacketGDVCard * p);
|
||||
void VCardProcess();
|
||||
|
||||
// BLOCK_CHAT
|
||||
void BlockChat(TPacketBlockChat * p);
|
||||
// END_OF_BLOCK_CHAT
|
||||
|
||||
private:
|
||||
int m_looping;
|
||||
socket_t m_fdAccept; // 접속 받는 소켓
|
||||
TPeerList m_peerList;
|
||||
|
||||
CPeer * m_pkAuthPeer;
|
||||
|
||||
// LoginKey, LoginData pair
|
||||
typedef boost::unordered_map<DWORD, CLoginData *> TLoginDataByLoginKey;
|
||||
TLoginDataByLoginKey m_map_pkLoginData;
|
||||
|
||||
// Login LoginData pair
|
||||
typedef boost::unordered_map<std::string, CLoginData *> TLoginDataByLogin;
|
||||
TLoginDataByLogin m_map_pkLoginDataByLogin;
|
||||
|
||||
// AccountID LoginData pair
|
||||
typedef boost::unordered_map<DWORD, CLoginData *> TLoginDataByAID;
|
||||
TLoginDataByAID m_map_pkLoginDataByAID;
|
||||
|
||||
// Login LoginData pair (실제 로그인 되어있는 계정)
|
||||
typedef boost::unordered_map<std::string, CLoginData *> TLogonAccountMap;
|
||||
TLogonAccountMap m_map_kLogonAccount;
|
||||
|
||||
int m_iPlayerIDStart;
|
||||
int m_iPlayerDeleteLevelLimit;
|
||||
int m_iPlayerDeleteLevelLimitLower;
|
||||
bool m_bChinaEventServer;
|
||||
|
||||
std::vector<TMobTable> m_vec_mobTable;
|
||||
std::vector<TItemTable> m_vec_itemTable;
|
||||
std::map<DWORD, TItemTable *> m_map_itemTableByVnum;
|
||||
|
||||
int m_iShopTableSize;
|
||||
TShopTable * m_pShopTable;
|
||||
|
||||
int m_iRefineTableSize;
|
||||
TRefineTable* m_pRefineTable;
|
||||
|
||||
std::vector<TSkillTable> m_vec_skillTable;
|
||||
std::vector<TBanwordTable> m_vec_banwordTable;
|
||||
std::vector<TItemAttrTable> m_vec_itemAttrTable;
|
||||
std::vector<TItemAttrTable> m_vec_itemRareTable;
|
||||
|
||||
std::vector<building::TLand> m_vec_kLandTable;
|
||||
std::vector<building::TObjectProto> m_vec_kObjectProto;
|
||||
std::map<DWORD, building::TObject *> m_map_pkObjectTable;
|
||||
|
||||
std::queue<TPacketGDVCard> m_queue_vcard;
|
||||
|
||||
bool m_bShutdowned;
|
||||
|
||||
TPlayerTableCacheMap m_map_playerCache; // 플레이어 id가 key
|
||||
|
||||
TItemCacheMap m_map_itemCache; // 아이템 id가 key
|
||||
TItemCacheSetPtrMap m_map_pkItemCacheSetPtr; // 플레이어 id가 key, 이 플레이어가 어떤 아이템 캐쉬를 가지고 있나?
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
/// 플레이어별 아이템 가격정보 리스트 map. key: 플레이어 ID, value: 가격정보 리스트 캐시
|
||||
TItemPriceListCacheMap m_mapItemPriceListCache; ///< 플레이어별 아이템 가격정보 리스트
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
TChannelStatusMap m_mChannelStatus;
|
||||
|
||||
struct TPartyInfo
|
||||
{
|
||||
BYTE bRole;
|
||||
BYTE bLevel;
|
||||
|
||||
TPartyInfo() :bRole(0), bLevel(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<DWORD, TPartyInfo> TPartyMember;
|
||||
typedef std::map<DWORD, TPartyMember> TPartyMap;
|
||||
typedef std::map<BYTE, TPartyMap> TPartyChannelMap;
|
||||
TPartyChannelMap m_map_pkChannelParty;
|
||||
|
||||
typedef std::map<std::string, long> TEventFlagMap;
|
||||
TEventFlagMap m_map_lEventFlag;
|
||||
|
||||
BYTE m_bLastHeader;
|
||||
int m_iCacheFlushCount;
|
||||
int m_iCacheFlushCountLimit;
|
||||
|
||||
private :
|
||||
TItemIDRangeTable m_itemRange;
|
||||
|
||||
public :
|
||||
bool InitializeNowItemID();
|
||||
DWORD GetItemID();
|
||||
DWORD GainItemID();
|
||||
TItemIDRangeTable GetItemRange() { return m_itemRange; }
|
||||
|
||||
//BOOT_LOCALIZATION
|
||||
public:
|
||||
/* 로컬 정보 초기화
|
||||
**/
|
||||
bool InitializeLocalization();
|
||||
|
||||
private:
|
||||
std::vector<tLocale> m_vec_Locale;
|
||||
//END_BOOT_LOCALIZATION
|
||||
//ADMIN_MANAGER
|
||||
|
||||
bool __GetAdminInfo(const char *szIP, std::vector<tAdminInfo> & rAdminVec);
|
||||
bool __GetHostInfo(std::vector<std::string> & rIPVec);
|
||||
//END_ADMIN_MANAGER
|
||||
|
||||
|
||||
//RELOAD_ADMIN
|
||||
void ReloadAdmin(CPeer * peer, TPacketReloadAdmin * p);
|
||||
//END_RELOAD_ADMIN
|
||||
void BreakMarriage(CPeer * peer, const char * data);
|
||||
|
||||
struct TLogoutPlayer
|
||||
{
|
||||
DWORD pid;
|
||||
time_t time;
|
||||
|
||||
bool operator < (const TLogoutPlayer & r)
|
||||
{
|
||||
return (pid < r.pid);
|
||||
}
|
||||
};
|
||||
|
||||
typedef boost::unordered_map<DWORD, TLogoutPlayer*> TLogoutPlayerMap;
|
||||
TLogoutPlayerMap m_map_logout;
|
||||
|
||||
void InsertLogoutPlayer(DWORD pid);
|
||||
void DeleteLogoutPlayer(DWORD pid);
|
||||
void UpdateLogoutPlayer();
|
||||
void UpdateItemCacheSet(DWORD pid);
|
||||
|
||||
void FlushPlayerCacheSet(DWORD pid);
|
||||
|
||||
//MONARCH
|
||||
void Election(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void Candidacy(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void ComeToVote(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void RMCandidacy(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void SetMonarch(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
void RMMonarch(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
|
||||
|
||||
void DecMonarchMoney(CPeer * peer, DWORD dwHandle, const char * p);
|
||||
//END_MONARCH
|
||||
|
||||
void ChangeMonarchLord(CPeer* peer, DWORD dwHandle, TPacketChangeMonarchLord* info);
|
||||
void BlockException(TPacketBlockException *data);
|
||||
|
||||
void SendSpareItemIDRange(CPeer* peer);
|
||||
|
||||
void UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer);
|
||||
void AckHorseName(DWORD dwPID, CPeer* peer);
|
||||
void DeleteLoginKey(TPacketDC *data);
|
||||
void ResetLastPlayerID(const TPacketNeedLoginLogInfo* data);
|
||||
//delete gift notify icon
|
||||
void DeleteAwardId(TPacketDeleteAwardID* data);
|
||||
void UpdateChannelStatus(TChannelStatus* pData);
|
||||
void RequestChannelStatus(CPeer* peer, DWORD dwHandle);
|
||||
#ifdef __AUCTION__
|
||||
void EnrollInAuction (CPeer * peer, DWORD owner_id, AuctionEnrollProductInfo* data);
|
||||
void EnrollInSale (CPeer * peer, DWORD owner_id, AuctionEnrollSaleInfo* data);
|
||||
void EnrollInWish (CPeer * peer, DWORD wisher_id, AuctionEnrollWishInfo* data);
|
||||
void AuctionBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data);
|
||||
void AuctionImpur (CPeer * peer, DWORD purchaser_id, AuctionImpurInfo* data);
|
||||
void AuctionGetAuctionedItem (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionBuySoldItem (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionCancelAuction (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionCancelWish (CPeer * peer, DWORD actor_id, DWORD item_num);
|
||||
void AuctionCancelSale (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionDeleteAuctionItem (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionDeleteSaleItem (CPeer * peer, DWORD actor_id, DWORD item_id);
|
||||
void AuctionReBid (CPeer * peer, DWORD bidder_id, AuctionBidInfo* data);
|
||||
void AuctionBidCancel (CPeer * peer, DWORD bidder_id, DWORD item_id);
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class Func>
|
||||
Func CClientManager::for_each_peer(Func f)
|
||||
{
|
||||
TPeerList::iterator it;
|
||||
for (it = m_peerList.begin(); it!=m_peerList.end();++it)
|
||||
{
|
||||
f(*it);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,77 @@
|
|||
// vim:ts=4 sw=4
|
||||
#include "stdafx.h"
|
||||
#include "ClientManager.h"
|
||||
#include "Main.h"
|
||||
#include "Config.h"
|
||||
#include "DBManager.h"
|
||||
#include "QID.h"
|
||||
|
||||
void CClientManager::LoadEventFlag()
|
||||
{
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT szName, lValue FROM quest%s WHERE dwPID = 0", GetTablePostfix());
|
||||
std::auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult* pRes = pmsg->Get();
|
||||
if (pRes->uiNumRows)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
while ((row = mysql_fetch_row(pRes->pSQLResult)))
|
||||
{
|
||||
TPacketSetEventFlag p;
|
||||
strlcpy(p.szFlagName, row[0], sizeof(p.szFlagName));
|
||||
str_to_number(p.lValue, row[1]);
|
||||
sys_log(0, "EventFlag Load %s %d", p.szFlagName, p.lValue);
|
||||
m_map_lEventFlag.insert(std::make_pair(std::string(p.szFlagName), p.lValue));
|
||||
ForwardPacket(HEADER_DG_SET_EVENT_FLAG, &p, sizeof(TPacketSetEventFlag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::SetEventFlag(TPacketSetEventFlag* p)
|
||||
{
|
||||
ForwardPacket(HEADER_DG_SET_EVENT_FLAG, p, sizeof(TPacketSetEventFlag));
|
||||
|
||||
bool bChanged = false;
|
||||
|
||||
typeof(m_map_lEventFlag.begin()) it = m_map_lEventFlag.find(p->szFlagName);
|
||||
if (it == m_map_lEventFlag.end())
|
||||
{
|
||||
bChanged = true;
|
||||
m_map_lEventFlag.insert(std::make_pair(std::string(p->szFlagName), p->lValue));
|
||||
}
|
||||
else if (it->second != p->lValue)
|
||||
{
|
||||
bChanged = true;
|
||||
it->second = p->lValue;
|
||||
}
|
||||
|
||||
if (bChanged)
|
||||
{
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(0, '%s', '', %ld)",
|
||||
GetTablePostfix(), p->szFlagName, p->lValue);
|
||||
szQuery[1023] = '\0';
|
||||
|
||||
//CDBManager::instance().ReturnQuery(szQuery, QID_QUEST_SAVE, 0, NULL);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
sys_log(0, "HEADER_GD_SET_EVENT_FLAG : Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue);
|
||||
return;
|
||||
}
|
||||
sys_log(0, "HEADER_GD_SET_EVENT_FLAG : No Changed CClientmanager::SetEventFlag(%s %d) ", p->szFlagName, p->lValue);
|
||||
}
|
||||
|
||||
void CClientManager::SendEventFlagsOnSetup(CPeer* peer)
|
||||
{
|
||||
typeof(m_map_lEventFlag.begin()) it;
|
||||
for (it = m_map_lEventFlag.begin(); it != m_map_lEventFlag.end(); ++it)
|
||||
{
|
||||
TPacketSetEventFlag p;
|
||||
strlcpy(p.szFlagName, it->first.c_str(), sizeof(p.szFlagName));
|
||||
p.lValue = it->second;
|
||||
peer->EncodeHeader(HEADER_DG_SET_EVENT_FLAG, 0, sizeof(TPacketSetEventFlag));
|
||||
peer->Encode(&p, sizeof(TPacketSetEventFlag));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,244 @@
|
|||
// vim:ts=4 sw=4
|
||||
#include "stdafx.h"
|
||||
#include "ClientManager.h"
|
||||
#include "Main.h"
|
||||
#include "Config.h"
|
||||
#include "DBManager.h"
|
||||
#include "QID.h"
|
||||
#include "GuildManager.h"
|
||||
|
||||
|
||||
void CClientManager::GuildCreate(CPeer * peer, DWORD dwGuildID)
|
||||
{
|
||||
sys_log(0, "GuildCreate %u", dwGuildID);
|
||||
ForwardPacket(HEADER_DG_GUILD_LOAD, &dwGuildID, sizeof(DWORD));
|
||||
|
||||
CGuildManager::instance().Load(dwGuildID);
|
||||
}
|
||||
|
||||
void CClientManager::GuildChangeGrade(CPeer* peer, TPacketGuild* p)
|
||||
{
|
||||
sys_log(0, "GuildChangeGrade %u %u", p->dwGuild, p->dwInfo);
|
||||
ForwardPacket(HEADER_DG_GUILD_CHANGE_GRADE, p, sizeof(TPacketGuild));
|
||||
}
|
||||
|
||||
void CClientManager::GuildAddMember(CPeer* peer, TPacketGDGuildAddMember * p)
|
||||
{
|
||||
CGuildManager::instance().TouchGuild(p->dwGuild);
|
||||
sys_log(0, "GuildAddMember %u %u", p->dwGuild, p->dwPID);
|
||||
|
||||
char szQuery[512];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"INSERT INTO guild_member%s VALUES(%u, %u, %d, 0, 0)",
|
||||
GetTablePostfix(), p->dwPID, p->dwGuild, p->bGrade);
|
||||
|
||||
std::auto_ptr<SQLMsg> pmsg_insert(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT pid, grade, is_general, offer, level, job, name FROM guild_member%s, player%s WHERE guild_id = %u and pid = id and pid = %u", GetTablePostfix(), GetTablePostfix(), p->dwGuild, p->dwPID);
|
||||
|
||||
std::auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
if (pmsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_err("Query failed when getting guild member data %s", pmsg->stQuery.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
|
||||
if (!row[0] || !row[1])
|
||||
return;
|
||||
|
||||
TPacketDGGuildMember dg;
|
||||
|
||||
dg.dwGuild = p->dwGuild;
|
||||
str_to_number(dg.dwPID, row[0]);
|
||||
str_to_number(dg.bGrade, row[1]);
|
||||
str_to_number(dg.isGeneral, row[2]);
|
||||
str_to_number(dg.dwOffer, row[3]);
|
||||
str_to_number(dg.bLevel, row[4]);
|
||||
str_to_number(dg.bJob, row[5]);
|
||||
strlcpy(dg.szName, row[6], sizeof(dg.szName));
|
||||
|
||||
ForwardPacket(HEADER_DG_GUILD_ADD_MEMBER, &dg, sizeof(TPacketDGGuildMember));
|
||||
}
|
||||
|
||||
void CClientManager::GuildRemoveMember(CPeer* peer, TPacketGuild* p)
|
||||
{
|
||||
sys_log(0, "GuildRemoveMember %u %u", p->dwGuild, p->dwInfo);
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE pid=%u and guild_id=%u", GetTablePostfix(), p->dwInfo, p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) VALUES(%u, 'guild_manage', 'withdraw_time', %u)", GetTablePostfix(), p->dwInfo, (DWORD) GetCurrentTime());
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
ForwardPacket(HEADER_DG_GUILD_REMOVE_MEMBER, p, sizeof(TPacketGuild));
|
||||
}
|
||||
|
||||
void CClientManager::GuildSkillUpdate(CPeer* peer, TPacketGuildSkillUpdate* p)
|
||||
{
|
||||
sys_log(0, "GuildSkillUpdate %d", p->amount);
|
||||
ForwardPacket(HEADER_DG_GUILD_SKILL_UPDATE, p, sizeof(TPacketGuildSkillUpdate));
|
||||
}
|
||||
|
||||
void CClientManager::GuildExpUpdate(CPeer* peer, TPacketGuildExpUpdate* p)
|
||||
{
|
||||
sys_log(0, "GuildExpUpdate %d", p->amount);
|
||||
ForwardPacket(HEADER_DG_GUILD_EXP_UPDATE, p, sizeof(TPacketGuildExpUpdate), 0, peer);
|
||||
}
|
||||
|
||||
void CClientManager::GuildChangeMemberData(CPeer* peer, TPacketGuildChangeMemberData* p)
|
||||
{
|
||||
sys_log(0, "GuildChangeMemberData %u %u %d %d", p->pid, p->offer, p->level, p->grade);
|
||||
ForwardPacket(HEADER_DG_GUILD_CHANGE_MEMBER_DATA, p, sizeof(TPacketGuildChangeMemberData), 0, peer);
|
||||
}
|
||||
|
||||
void CClientManager::GuildDisband(CPeer* peer, TPacketGuild* p)
|
||||
{
|
||||
sys_log(0, "GuildDisband %u", p->dwGuild);
|
||||
|
||||
char szQuery[512];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild%s WHERE id=%u", GetTablePostfix(), p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_grade%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO quest%s (dwPID, szName, szState, lValue) SELECT pid, 'guild_manage', 'withdraw_time', %u FROM guild_member%s WHERE guild_id = %u", GetTablePostfix(), (DWORD) GetCurrentTime(), GetTablePostfix(), p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_member%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM guild_comment%s WHERE guild_id=%u", GetTablePostfix(), p->dwGuild);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
ForwardPacket(HEADER_DG_GUILD_DISBAND, p, sizeof(TPacketGuild));
|
||||
}
|
||||
|
||||
const char* __GetWarType(int n)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0 :
|
||||
return "패왕";
|
||||
case 1 :
|
||||
return "맹장";
|
||||
case 2 :
|
||||
return "수호";
|
||||
default :
|
||||
return "없는 번호";
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::GuildWar(CPeer* peer, TPacketGuildWar* p)
|
||||
{
|
||||
switch (p->bWar)
|
||||
{
|
||||
case GUILD_WAR_SEND_DECLARE:
|
||||
sys_log(0, "GuildWar: GUILD_WAR_SEND_DECLARE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().AddDeclare(p->bType, p->dwGuildFrom, p->dwGuildTo);
|
||||
break;
|
||||
|
||||
case GUILD_WAR_REFUSE:
|
||||
sys_log(0, "GuildWar: GUILD_WAR_REFUSE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo);
|
||||
break;
|
||||
/*
|
||||
case GUILD_WAR_WAIT_START:
|
||||
CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo);
|
||||
|
||||
if (!CGuildManager::instance().WaitStart(p))
|
||||
p->bWar = GUILD_WAR_CANCEL;
|
||||
|
||||
break;
|
||||
*/
|
||||
|
||||
case GUILD_WAR_WAIT_START:
|
||||
sys_log(0, "GuildWar: GUILD_WAR_WAIT_START type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
case GUILD_WAR_RESERVE: // 길드전 예약
|
||||
if (p->bWar != GUILD_WAR_WAIT_START)
|
||||
sys_log(0, "GuildWar: GUILD_WAR_RESERVE type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo);
|
||||
|
||||
if (!CGuildManager::instance().ReserveWar(p))
|
||||
p->bWar = GUILD_WAR_CANCEL;
|
||||
else
|
||||
p->bWar = GUILD_WAR_RESERVE;
|
||||
|
||||
break;
|
||||
|
||||
case GUILD_WAR_ON_WAR: // 길드전을 시작 시킨다. (필드전은 바로 시작 됨)
|
||||
sys_log(0, "GuildWar: GUILD_WAR_ON_WAR type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().RemoveDeclare(p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().StartWar(p->bType, p->dwGuildFrom, p->dwGuildTo);
|
||||
break;
|
||||
|
||||
case GUILD_WAR_OVER: // 길드전 정상 종료
|
||||
sys_log(0, "GuildWar: GUILD_WAR_OVER type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().RecvWarOver(p->dwGuildFrom, p->dwGuildTo, p->bType, p->lWarPrice);
|
||||
break;
|
||||
|
||||
case GUILD_WAR_END: // 길드전 비정상 종료
|
||||
sys_log(0, "GuildWar: GUILD_WAR_END type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().RecvWarEnd(p->dwGuildFrom, p->dwGuildTo);
|
||||
return; // NOTE: RecvWarEnd에서 패킷을 보내므로 따로 브로드캐스팅 하지 않는다.
|
||||
|
||||
case GUILD_WAR_CANCEL :
|
||||
sys_log(0, "GuildWar: GUILD_WAR_CANCEL type(%s) guild(%d - %d)", __GetWarType(p->bType), p->dwGuildFrom, p->dwGuildTo);
|
||||
CGuildManager::instance().CancelWar(p->dwGuildFrom, p->dwGuildTo);
|
||||
break;
|
||||
}
|
||||
|
||||
ForwardPacket(HEADER_DG_GUILD_WAR, p, sizeof(TPacketGuildWar));
|
||||
}
|
||||
|
||||
void CClientManager::GuildWarScore(CPeer* peer, TPacketGuildWarScore * p)
|
||||
{
|
||||
CGuildManager::instance().UpdateScore(p->dwGuildGainPoint, p->dwGuildOpponent, p->lScore, p->lBetScore);
|
||||
}
|
||||
|
||||
void CClientManager::GuildChangeLadderPoint(TPacketGuildLadderPoint* p)
|
||||
{
|
||||
sys_log(0, "GuildChangeLadderPoint Recv %u %d", p->dwGuild, p->lChange);
|
||||
CGuildManager::instance().ChangeLadderPoint(p->dwGuild, p->lChange);
|
||||
}
|
||||
|
||||
void CClientManager::GuildUseSkill(TPacketGuildUseSkill* p)
|
||||
{
|
||||
sys_log(0, "GuildUseSkill Recv %u %d", p->dwGuild, p->dwSkillVnum);
|
||||
CGuildManager::instance().UseSkill(p->dwGuild, p->dwSkillVnum, p->dwCooltime);
|
||||
SendGuildSkillUsable(p->dwGuild, p->dwSkillVnum, false);
|
||||
}
|
||||
|
||||
void CClientManager::SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable)
|
||||
{
|
||||
sys_log(0, "SendGuildSkillUsable Send %u %d %s", guild_id, dwSkillVnum, bUsable?"true":"false");
|
||||
|
||||
TPacketGuildSkillUsableChange p;
|
||||
|
||||
p.dwGuild = guild_id;
|
||||
p.dwSkillVnum = dwSkillVnum;
|
||||
p.bUsable = bUsable;
|
||||
|
||||
ForwardPacket(HEADER_DG_GUILD_SKILL_USABLE_CHANGE, &p, sizeof(TPacketGuildSkillUsableChange));
|
||||
}
|
||||
|
||||
void CClientManager::GuildChangeMaster(TPacketChangeGuildMaster* p)
|
||||
{
|
||||
if (CGuildManager::instance().ChangeMaster(p->dwGuildID, p->idFrom, p->idTo) == true)
|
||||
{
|
||||
TPacketChangeGuildMaster packet;
|
||||
packet.dwGuildID = p->dwGuildID;
|
||||
packet.idFrom = 0;
|
||||
packet.idTo = 0;
|
||||
|
||||
ForwardPacket(HEADER_DG_ACK_CHANGE_GUILD_MASTER, &packet, sizeof(packet));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// vim:ts=4 sw=4
|
||||
#include "stdafx.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
void CClientManager::UpdateHorseName(TPacketUpdateHorseName* data, CPeer* peer)
|
||||
{
|
||||
char szQuery[512];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO horse_name VALUES(%u, '%s')", data->dwPlayerID, data->szHorseName);
|
||||
|
||||
std::auto_ptr<SQLMsg> pmsg_insert(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
ForwardPacket(HEADER_DG_UPDATE_HORSE_NAME, data, sizeof(TPacketUpdateHorseName), 0, peer);
|
||||
}
|
||||
|
||||
void CClientManager::AckHorseName(DWORD dwPID, CPeer* peer)
|
||||
{
|
||||
char szQuery[512];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT name FROM horse_name WHERE id = %u", dwPID);
|
||||
|
||||
std::auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
TPacketUpdateHorseName packet;
|
||||
packet.dwPlayerID = dwPID;
|
||||
|
||||
if (pmsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
memset(packet.szHorseName, 0, sizeof (packet.szHorseName));
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
strlcpy(packet.szHorseName, row[0], sizeof(packet.szHorseName));
|
||||
}
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_ACK_HORSE_NAME, 0, sizeof(TPacketUpdateHorseName));
|
||||
peer->Encode(&packet, sizeof(TPacketUpdateHorseName));
|
||||
}
|
||||
|
|
@ -0,0 +1,544 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "ClientManager.h"
|
||||
|
||||
#include "Main.h"
|
||||
#include "Config.h"
|
||||
#include "QID.h"
|
||||
#include "Cache.h"
|
||||
|
||||
extern std::string g_stLocale;
|
||||
extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab);
|
||||
extern int g_test_server;
|
||||
extern int g_log;
|
||||
|
||||
bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP)
|
||||
{
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin));
|
||||
|
||||
itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin);
|
||||
|
||||
if (m_map_kLogonAccount.end() != it)
|
||||
return false;
|
||||
|
||||
CLoginData * pkLD = GetLoginDataByLogin(c_pszLogin);
|
||||
|
||||
if (!pkLD)
|
||||
return false;
|
||||
|
||||
pkLD->SetConnectedPeerHandle(dwHandle);
|
||||
pkLD->SetIP(c_pszIP);
|
||||
|
||||
m_map_kLogonAccount.insert(TLogonAccountMap::value_type(szLogin, pkLD));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CClientManager::DeleteLogonAccount(const char * c_pszLogin, DWORD dwHandle)
|
||||
{
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin));
|
||||
|
||||
itertype(m_map_kLogonAccount) it = m_map_kLogonAccount.find(szLogin);
|
||||
|
||||
if (it == m_map_kLogonAccount.end())
|
||||
return false;
|
||||
|
||||
CLoginData * pkLD = it->second;
|
||||
|
||||
if (pkLD->GetConnectedPeerHandle() != dwHandle)
|
||||
{
|
||||
sys_err("%s tried to logout in other peer handle %lu, current handle %lu", szLogin, dwHandle, pkLD->GetConnectedPeerHandle());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pkLD->IsPlay())
|
||||
{
|
||||
pkLD->SetPlay(false);
|
||||
SendLoginToBilling(pkLD, false);
|
||||
}
|
||||
|
||||
if (pkLD->IsDeleted())
|
||||
{
|
||||
delete pkLD;
|
||||
}
|
||||
|
||||
m_map_kLogonAccount.erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CClientManager::FindLogonAccount(const char * c_pszLogin)
|
||||
{
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
trim_and_lower(c_pszLogin, szLogin, sizeof(szLogin));
|
||||
|
||||
if (m_map_kLogonAccount.end() == m_map_kLogonAccount.find(szLogin))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketGDLoginByKey * p)
|
||||
{
|
||||
#ifdef ENABLE_LIMIT_TIME
|
||||
static int s_updateCount = 0;
|
||||
static int s_curTime = time(0);
|
||||
if (s_updateCount > 100)
|
||||
{
|
||||
s_curTime = time(0);
|
||||
s_updateCount = 0;
|
||||
}
|
||||
++s_updateCount;
|
||||
|
||||
if (s_curTime >= GLOBAL_LIMIT_TIME)
|
||||
{
|
||||
sys_err("Server life time expired.");
|
||||
exit(0);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
CLoginData * pkLoginData = GetLoginData(p->dwLoginKey);
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
trim_and_lower(p->szLogin, szLogin, sizeof(szLogin));
|
||||
|
||||
if (!pkLoginData)
|
||||
{
|
||||
sys_log(0, "LOGIN_BY_KEY key not exist %s %lu", szLogin, p->dwLoginKey);
|
||||
pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
TAccountTable & r = pkLoginData->GetAccountRef();
|
||||
|
||||
if (FindLogonAccount(r.login))
|
||||
{
|
||||
sys_log(0, "LOGIN_BY_KEY already login %s %lu", r.login, p->dwLoginKey);
|
||||
TPacketDGLoginAlready ptog;
|
||||
strlcpy(ptog.szLogin, szLogin, sizeof(ptog.szLogin));
|
||||
pkPeer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, dwHandle, sizeof(TPacketDGLoginAlready));
|
||||
pkPeer->Encode(&ptog, sizeof(TPacketDGLoginAlready));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(r.login, szLogin))
|
||||
{
|
||||
sys_log(0, "LOGIN_BY_KEY login differ %s %lu input %s", r.login, p->dwLoginKey, szLogin);
|
||||
pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(pkLoginData->GetClientKey(), p->adwClientKey, sizeof(DWORD) * 4))
|
||||
{
|
||||
const DWORD * pdwClientKey = pkLoginData->GetClientKey();
|
||||
|
||||
sys_log(0, "LOGIN_BY_KEY client key differ %s %lu %lu %lu %lu, %lu %lu %lu %lu",
|
||||
r.login,
|
||||
p->adwClientKey[0], p->adwClientKey[1], p->adwClientKey[2], p->adwClientKey[3],
|
||||
pdwClientKey[0], pdwClientKey[1], pdwClientKey[2], pdwClientKey[3]);
|
||||
|
||||
pkPeer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, dwHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
TAccountTable * pkTab = new TAccountTable;
|
||||
memset(pkTab, 0, sizeof(TAccountTable));
|
||||
|
||||
pkTab->id = r.id;
|
||||
trim_and_lower(r.login, pkTab->login, sizeof(pkTab->login));
|
||||
strlcpy(pkTab->passwd, r.passwd, sizeof(pkTab->passwd));
|
||||
strlcpy(pkTab->social_id, r.social_id, sizeof(pkTab->social_id));
|
||||
strlcpy(pkTab->status, "OK", sizeof(pkTab->status));
|
||||
|
||||
ClientHandleInfo * info = new ClientHandleInfo(dwHandle);
|
||||
info->pAccountTable = pkTab;
|
||||
strlcpy(info->ip, p->szIP, sizeof(info->ip));
|
||||
|
||||
sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip);
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info);
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg)
|
||||
{
|
||||
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
|
||||
ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData;
|
||||
|
||||
if (msg->uiSQLErrno != 0)
|
||||
{
|
||||
peer->EncodeReturn(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle);
|
||||
delete info;
|
||||
return;
|
||||
}
|
||||
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
if (msg->Get()->uiNumRows == 0)
|
||||
{
|
||||
DWORD account_id = info->pAccountTable->id;
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), account_id);
|
||||
std::auto_ptr<SQLMsg> pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER));
|
||||
|
||||
sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN_BY_KEY FAIL player_index's NULL : ID:%d", account_id);
|
||||
|
||||
// PLAYER_INDEX_CREATE_BUG_FIX
|
||||
//snprintf(szQuery, sizeof(szQuery), "INSERT IGNORE INTO player_index%s (id) VALUES(%lu)", GetTablePostfix(), info->pAccountTable->id);
|
||||
snprintf(szQuery, sizeof(szQuery), "INSERT INTO player_index%s (id) VALUES(%u)", GetTablePostfix(), info->pAccountTable->id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_INDEX_CREATE, peer->GetHandle(), info);
|
||||
// END_PLAYER_INDEX_CREATE_BUF_FIX
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
|
||||
|
||||
int col = 0;
|
||||
|
||||
for (; col < PLAYER_PER_ACCOUNT; ++col)
|
||||
str_to_number(info->pAccountTable->players[col].dwID, row[col]);
|
||||
|
||||
str_to_number(info->pAccountTable->bEmpire, row[col++]);
|
||||
info->account_index = 1;
|
||||
|
||||
extern std::string g_stLocale;
|
||||
if (g_stLocale == "gb2312")
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u",
|
||||
GetTablePostfix(), info->pAccountTable->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u",
|
||||
GetTablePostfix(), info->pAccountTable->id);
|
||||
}
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN, peer->GetHandle(), info);
|
||||
}
|
||||
|
||||
// PLAYER_INDEX_CREATE_BUG_FIX
|
||||
void CClientManager::RESULT_PLAYER_INDEX_CREATE(CPeer * pkPeer, SQLMsg * msg)
|
||||
{
|
||||
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
|
||||
ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData;
|
||||
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(),
|
||||
info->pAccountTable->id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info);
|
||||
}
|
||||
// END_PLAYER_INDEX_CREATE_BUG_FIX
|
||||
|
||||
TAccountTable * CreateAccountTableFromRes(MYSQL_RES * res)
|
||||
{
|
||||
char input_pwd[PASSWD_MAX_LEN + 1];
|
||||
MYSQL_ROW row = NULL;
|
||||
DWORD col;
|
||||
|
||||
row = mysql_fetch_row(res);
|
||||
col = 0;
|
||||
|
||||
TAccountTable * pkTab = new TAccountTable;
|
||||
memset(pkTab, 0, sizeof(TAccountTable));
|
||||
|
||||
// 첫번째 컬럼 것만 참고 한다 (JOIN QUERY를 위한 것 임)
|
||||
strlcpy(input_pwd, row[col++], sizeof(input_pwd));
|
||||
str_to_number(pkTab->id, row[col++]);
|
||||
strlcpy(pkTab->login, row[col++], sizeof(pkTab->login));
|
||||
strlcpy(pkTab->passwd, row[col++], sizeof(pkTab->passwd));
|
||||
strlcpy(pkTab->social_id, row[col++], sizeof(pkTab->social_id));
|
||||
str_to_number(pkTab->bEmpire, row[col++]);
|
||||
|
||||
for (int j = 0; j < PLAYER_PER_ACCOUNT; ++j)
|
||||
str_to_number(pkTab->players[j].dwID, row[col++]);
|
||||
|
||||
strlcpy(pkTab->status, row[col++], sizeof(pkTab->status));
|
||||
|
||||
if (strcmp(pkTab->passwd, input_pwd))
|
||||
{
|
||||
delete pkTab;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkTab;
|
||||
}
|
||||
|
||||
void CreateAccountPlayerDataFromRes(MYSQL_RES * pRes, TAccountTable * pkTab)
|
||||
{
|
||||
if (!pRes)
|
||||
return;
|
||||
|
||||
for (DWORD i = 0; i < mysql_num_rows(pRes); ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pRes);
|
||||
int col = 0;
|
||||
|
||||
DWORD player_id = 0;
|
||||
!row[col++] ? 0 : str_to_number(player_id, row[col - 1]);
|
||||
|
||||
if (!player_id)
|
||||
continue;
|
||||
|
||||
int j;
|
||||
|
||||
for (j = 0; j < PLAYER_PER_ACCOUNT; ++j)
|
||||
{
|
||||
if (pkTab->players[j].dwID == player_id)
|
||||
{
|
||||
CPlayerTableCache * pc = CClientManager::instance().GetPlayerCache(player_id);
|
||||
TPlayerTable * pt = pc ? pc->Get(false) : NULL;
|
||||
|
||||
if (pt)
|
||||
{
|
||||
strlcpy(pkTab->players[j].szName, pt->name, sizeof(pkTab->players[j].szName));
|
||||
|
||||
pkTab->players[j].byJob = pt->job;
|
||||
pkTab->players[j].byLevel = pt->level;
|
||||
pkTab->players[j].dwPlayMinutes = pt->playtime;
|
||||
pkTab->players[j].byST = pt->st;
|
||||
pkTab->players[j].byHT = pt->ht;
|
||||
pkTab->players[j].byDX = pt->dx;
|
||||
pkTab->players[j].byIQ = pt->iq;
|
||||
pkTab->players[j].wMainPart = pt->parts[PART_MAIN];
|
||||
pkTab->players[j].wHairPart = pt->parts[PART_HAIR];
|
||||
pkTab->players[j].x = pt->x;
|
||||
pkTab->players[j].y = pt->y;
|
||||
pkTab->players[j].skill_group = pt->skill_group;
|
||||
pkTab->players[j].bChangeName = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!row[col++])
|
||||
*pkTab->players[j].szName = '\0';
|
||||
else
|
||||
strlcpy(pkTab->players[j].szName, row[col - 1], sizeof(pkTab->players[j].szName));
|
||||
|
||||
pkTab->players[j].byJob = 0;
|
||||
pkTab->players[j].byLevel = 0;
|
||||
pkTab->players[j].dwPlayMinutes = 0;
|
||||
pkTab->players[j].byST = 0;
|
||||
pkTab->players[j].byHT = 0;
|
||||
pkTab->players[j].byDX = 0;
|
||||
pkTab->players[j].byIQ = 0;
|
||||
pkTab->players[j].wMainPart = 0;
|
||||
pkTab->players[j].wHairPart = 0;
|
||||
pkTab->players[j].x = 0;
|
||||
pkTab->players[j].y = 0;
|
||||
pkTab->players[j].skill_group = 0;
|
||||
pkTab->players[j].bChangeName = 0;
|
||||
|
||||
str_to_number(pkTab->players[j].byJob, row[col++]);
|
||||
str_to_number(pkTab->players[j].byLevel, row[col++]);
|
||||
str_to_number(pkTab->players[j].dwPlayMinutes, row[col++]);
|
||||
str_to_number(pkTab->players[j].byST, row[col++]);
|
||||
str_to_number(pkTab->players[j].byHT, row[col++]);
|
||||
str_to_number(pkTab->players[j].byDX, row[col++]);
|
||||
str_to_number(pkTab->players[j].byIQ, row[col++]);
|
||||
str_to_number(pkTab->players[j].wMainPart, row[col++]);
|
||||
str_to_number(pkTab->players[j].wHairPart, row[col++]);
|
||||
str_to_number(pkTab->players[j].x, row[col++]);
|
||||
str_to_number(pkTab->players[j].y, row[col++]);
|
||||
str_to_number(pkTab->players[j].skill_group, row[col++]);
|
||||
str_to_number(pkTab->players[j].bChangeName, row[col++]);
|
||||
}
|
||||
|
||||
sys_log(0, "%s %lu %lu hair %u",
|
||||
pkTab->players[j].szName, pkTab->players[j].x, pkTab->players[j].y, pkTab->players[j].wHairPart);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (j == PLAYER_PER_ACCOUNT)
|
||||
sys_err("cannot find player_id on this account (login: %s id %lu account %lu %lu %lu)",
|
||||
pkTab->login, player_id,
|
||||
pkTab->players[0].dwID,
|
||||
pkTab->players[1].dwID,
|
||||
pkTab->players[2].dwID);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg)
|
||||
{
|
||||
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
|
||||
ClientHandleInfo * info = (ClientHandleInfo *) qi->pvData;
|
||||
|
||||
if (info->account_index == 0)
|
||||
{
|
||||
// 계정이 없네?
|
||||
if (msg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN: no account");
|
||||
peer->EncodeHeader(HEADER_DG_LOGIN_NOT_EXIST, info->dwHandle, 0);
|
||||
delete info;
|
||||
return;
|
||||
}
|
||||
|
||||
info->pAccountTable = CreateAccountTableFromRes(msg->Get()->pSQLResult);
|
||||
|
||||
if (!info->pAccountTable)
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN: no account : WRONG_PASSWD");
|
||||
peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle);
|
||||
delete info;
|
||||
}
|
||||
else
|
||||
{
|
||||
++info->account_index;
|
||||
|
||||
char queryStr[512];
|
||||
extern std::string g_stLocale;
|
||||
if (g_stLocale == "gb2312")
|
||||
{
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u",
|
||||
GetTablePostfix(), info->pAccountTable->id);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player%s WHERE account_id=%u",
|
||||
GetTablePostfix(), info->pAccountTable->id);
|
||||
}
|
||||
|
||||
CDBManager::instance().ReturnQuery(queryStr, QID_LOGIN, peer->GetHandle(), info);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!info->pAccountTable) // 이럴리는 없겠지만;;
|
||||
{
|
||||
peer->EncodeReturn(HEADER_DG_LOGIN_WRONG_PASSWD, info->dwHandle);
|
||||
delete info;
|
||||
return;
|
||||
}
|
||||
|
||||
// 다른 컨넥션이 이미 로그인 해버렸다면.. 이미 접속했다고 보내야 한다.
|
||||
if (!InsertLogonAccount(info->pAccountTable->login, peer->GetHandle(), info->ip))
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN: already logon %s", info->pAccountTable->login);
|
||||
|
||||
TPacketDGLoginAlready p;
|
||||
strlcpy(p.szLogin, info->pAccountTable->login, sizeof(p.szLogin));
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_LOGIN_ALREADY, info->dwHandle, sizeof(TPacketDGLoginAlready));
|
||||
peer->Encode(&p, sizeof(p));
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN: login success %s rows: %lu", info->pAccountTable->login, msg->Get()->uiNumRows);
|
||||
|
||||
if (msg->Get()->uiNumRows > 0)
|
||||
CreateAccountPlayerDataFromRes(msg->Get()->pSQLResult, info->pAccountTable);
|
||||
|
||||
//PREVENT_COPY_ITEM
|
||||
CLoginData * p = GetLoginDataByLogin(info->pAccountTable->login);
|
||||
memcpy(&p->GetAccountRef(), info->pAccountTable, sizeof(TAccountTable));
|
||||
|
||||
//END_PREVENT_COPY_ITEM
|
||||
peer->EncodeHeader(HEADER_DG_LOGIN_SUCCESS, info->dwHandle, sizeof(TAccountTable));
|
||||
peer->Encode(info->pAccountTable, sizeof(TAccountTable));
|
||||
|
||||
}
|
||||
|
||||
delete info->pAccountTable;
|
||||
info->pAccountTable = NULL;
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data)
|
||||
{
|
||||
TLogoutPacket* packet = (TLogoutPacket*)data;
|
||||
|
||||
if (!*packet->login)
|
||||
return;
|
||||
|
||||
CLoginData * pLoginData = GetLoginDataByLogin(packet->login);
|
||||
|
||||
if (pLoginData == NULL)
|
||||
return;
|
||||
|
||||
int pid[PLAYER_PER_ACCOUNT];
|
||||
|
||||
for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n)
|
||||
{
|
||||
if (pLoginData->GetAccountRef().players[n].dwID == 0)
|
||||
{
|
||||
if (g_test_server)
|
||||
sys_log(0, "LOGOUT %s %d", packet->login, pLoginData->GetAccountRef().players[n].dwID);
|
||||
continue;
|
||||
}
|
||||
|
||||
pid[n] = pLoginData->GetAccountRef().players[n].dwID;
|
||||
|
||||
if (g_log)
|
||||
sys_log(0, "LOGOUT InsertLogoutPlayer %s %d", packet->login, pid[n]);
|
||||
|
||||
InsertLogoutPlayer(pid[n]);
|
||||
}
|
||||
|
||||
if (DeleteLogonAccount(packet->login, peer->GetHandle()))
|
||||
{
|
||||
if (g_log)
|
||||
sys_log(0, "LOGOUT %s ", packet->login);
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p)
|
||||
{
|
||||
char queryStr[QUERY_MAX_LEN];
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u",
|
||||
GetTablePostfix(), p->name, p->pid);
|
||||
else
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid);
|
||||
|
||||
std::auto_ptr<SQLMsg> pMsg(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER));
|
||||
|
||||
if (pMsg->Get()->uiNumRows)
|
||||
{
|
||||
if (!pMsg->Get()->pSQLResult)
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
|
||||
if (*row[0] != '0')
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid);
|
||||
|
||||
std::auto_ptr<SQLMsg> pMsg0(CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER));
|
||||
|
||||
TPacketDGChangeName pdg;
|
||||
peer->EncodeHeader(HEADER_DG_CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName));
|
||||
pdg.pid = p->pid;
|
||||
strlcpy(pdg.name, p->name, sizeof(pdg.name));
|
||||
peer->Encode(&pdg, sizeof(TPacketDGChangeName));
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
// vim:ts=4 sw=4
|
||||
#include "stdafx.h"
|
||||
#include "ClientManager.h"
|
||||
#include "Config.h"
|
||||
#include "DBManager.h"
|
||||
#include "QID.h"
|
||||
|
||||
void CClientManager::QUERY_PARTY_CREATE(CPeer* peer, TPacketPartyCreate* p)
|
||||
{
|
||||
TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
|
||||
if (pm.find(p->dwLeaderPID) == pm.end())
|
||||
{
|
||||
pm.insert(make_pair(p->dwLeaderPID, TPartyMember()));
|
||||
ForwardPacket(HEADER_DG_PARTY_CREATE, p, sizeof(TPacketPartyCreate), peer->GetChannel(), peer);
|
||||
sys_log(0, "PARTY Create [%lu]", p->dwLeaderPID);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_err("PARTY Create - Already exists [%lu]", p->dwLeaderPID);
|
||||
}
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_PARTY_DELETE(CPeer* peer, TPacketPartyDelete* p)
|
||||
{
|
||||
TPartyMap& pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
itertype(pm) it = pm.find(p->dwLeaderPID);
|
||||
|
||||
if (it == pm.end())
|
||||
{
|
||||
sys_err("PARTY Delete - Non exists [%lu]", p->dwLeaderPID);
|
||||
return;
|
||||
}
|
||||
|
||||
pm.erase(it);
|
||||
ForwardPacket(HEADER_DG_PARTY_DELETE, p, sizeof(TPacketPartyDelete), peer->GetChannel(), peer);
|
||||
sys_log(0, "PARTY Delete [%lu]", p->dwLeaderPID);
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_PARTY_ADD(CPeer* peer, TPacketPartyAdd* p)
|
||||
{
|
||||
TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
itertype(pm) it = pm.find(p->dwLeaderPID);
|
||||
|
||||
if (it == pm.end())
|
||||
{
|
||||
sys_err("PARTY Add - Non exists [%lu]", p->dwLeaderPID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (it->second.find(p->dwPID) == it->second.end())
|
||||
{
|
||||
it->second.insert(std::make_pair(p->dwPID, TPartyInfo()));
|
||||
ForwardPacket(HEADER_DG_PARTY_ADD, p, sizeof(TPacketPartyAdd), peer->GetChannel(), peer);
|
||||
sys_log(0, "PARTY Add [%lu] to [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
}
|
||||
else
|
||||
sys_err("PARTY Add - Already [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_PARTY_REMOVE(CPeer* peer, TPacketPartyRemove* p)
|
||||
{
|
||||
TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
itertype(pm) it = pm.find(p->dwLeaderPID);
|
||||
|
||||
if (it == pm.end())
|
||||
{
|
||||
sys_err("PARTY Remove - Non exists [%lu] cannot remove [%lu]",p->dwLeaderPID, p->dwPID);
|
||||
return;
|
||||
}
|
||||
|
||||
itertype(it->second) pit = it->second.find(p->dwPID);
|
||||
|
||||
if (pit != it->second.end())
|
||||
{
|
||||
it->second.erase(pit);
|
||||
ForwardPacket(HEADER_DG_PARTY_REMOVE, p, sizeof(TPacketPartyRemove), peer->GetChannel(), peer);
|
||||
sys_log(0, "PARTY Remove [%lu] to [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
}
|
||||
else
|
||||
sys_err("PARTY Remove - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_PARTY_STATE_CHANGE(CPeer* peer, TPacketPartyStateChange* p)
|
||||
{
|
||||
TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
itertype(pm) it = pm.find(p->dwLeaderPID);
|
||||
|
||||
if (it == pm.end())
|
||||
{
|
||||
sys_err("PARTY StateChange - Non exists [%lu] cannot state change [%lu]",p->dwLeaderPID, p->dwPID);
|
||||
return;
|
||||
}
|
||||
|
||||
itertype(it->second) pit = it->second.find(p->dwPID);
|
||||
|
||||
if (pit == it->second.end())
|
||||
{
|
||||
sys_err("PARTY StateChange - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->bFlag)
|
||||
pit->second.bRole = p->bRole;
|
||||
else
|
||||
pit->second.bRole = 0;
|
||||
|
||||
ForwardPacket(HEADER_DG_PARTY_STATE_CHANGE, p, sizeof(TPacketPartyStateChange), peer->GetChannel(), peer);
|
||||
sys_log(0, "PARTY StateChange [%lu] at [%lu] from %d %d",p->dwPID, p->dwLeaderPID, p->bRole, p->bFlag);
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_PARTY_SET_MEMBER_LEVEL(CPeer* peer, TPacketPartySetMemberLevel* p)
|
||||
{
|
||||
TPartyMap & pm = m_map_pkChannelParty[peer->GetChannel()];
|
||||
itertype(pm) it = pm.find(p->dwLeaderPID);
|
||||
|
||||
if (it == pm.end())
|
||||
{
|
||||
sys_err("PARTY SetMemberLevel - Non exists [%lu] cannot level change [%lu]",p->dwLeaderPID, p->dwPID);
|
||||
return;
|
||||
}
|
||||
|
||||
itertype(it->second) pit = it->second.find(p->dwPID);
|
||||
|
||||
if (pit == it->second.end())
|
||||
{
|
||||
sys_err("PARTY SetMemberLevel - Cannot find [%lu] in party [%lu]", p->dwPID, p->dwLeaderPID);
|
||||
return;
|
||||
}
|
||||
|
||||
pit->second.bLevel = p->bLevel;
|
||||
|
||||
ForwardPacket(HEADER_DG_PARTY_SET_MEMBER_LEVEL, p, sizeof(TPacketPartySetMemberLevel), peer->GetChannel());
|
||||
sys_log(0, "PARTY SetMemberLevel pid [%lu] level %d",p->dwPID, p->bLevel);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,250 @@
|
|||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
|
||||
CConfig::CConfig()
|
||||
{
|
||||
}
|
||||
|
||||
CConfig::~CConfig()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void CConfig::Destroy()
|
||||
{
|
||||
m_valueMap.clear();
|
||||
}
|
||||
|
||||
void CConfig::NextLine(FILE *fp)
|
||||
{
|
||||
int c;
|
||||
|
||||
while ((c = fgetc(fp)) != EOF)
|
||||
{
|
||||
if (c == '\n')
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool CConfig::GetWord(FILE *fp, char *tar)
|
||||
{
|
||||
int i = 0;
|
||||
int c;
|
||||
|
||||
int semicolon_mode = 0;
|
||||
|
||||
while ((c = fgetc(fp)) != EOF)
|
||||
{
|
||||
if (c == 13)
|
||||
continue;
|
||||
|
||||
if (semicolon_mode)
|
||||
{
|
||||
if (c == '"')
|
||||
{
|
||||
tar[i] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
tar[i++] = c;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
if (c == '"')
|
||||
{
|
||||
semicolon_mode = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == ' ' || c == '\t' || c == '\n')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ((c == ' ' || c == '\t' || c == '\n'))
|
||||
{
|
||||
// ÅÜ.
|
||||
tar[i] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
tar[i] = c;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return (i != 0);
|
||||
}
|
||||
|
||||
bool CConfig::GetLine(FILE* fp, char*dest)
|
||||
{
|
||||
int c;
|
||||
int i = 0;
|
||||
while ((c = fgetc(fp)) != EOF)
|
||||
{
|
||||
if (c == '\n')
|
||||
return true;
|
||||
dest[i] = c;
|
||||
++i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::LoadFile(const char* filename)
|
||||
{
|
||||
char szTmp[256];
|
||||
char comment[256];
|
||||
|
||||
FILE * fp = fopen(filename, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
return false;
|
||||
|
||||
int mode = 0;
|
||||
|
||||
while (GetWord(fp, szTmp))
|
||||
{
|
||||
if (strcmp(szTmp, "//") == 0)
|
||||
{
|
||||
NextLine(fp);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case 0:
|
||||
strlcpy(comment, szTmp, sizeof(comment));
|
||||
++mode;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (*szTmp == '=')
|
||||
++mode;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
mode = 0;
|
||||
m_valueMap.insert(TValueMap::value_type(comment, szTmp));
|
||||
break;
|
||||
}
|
||||
|
||||
// ITEM_ID_RANGE
|
||||
if (mode == 2 && strcmp(comment, "ITEM_ID_RANGE") == 0)
|
||||
{
|
||||
GetLine(fp, szTmp);
|
||||
m_valueMap.insert(TValueMap::value_type(comment, szTmp));
|
||||
mode = 0;
|
||||
|
||||
}
|
||||
// ITEM_ID_RANGE_END
|
||||
}
|
||||
|
||||
|
||||
// ÆÄÀÏ ´Ý´Â ºÎºÐ.
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string * CConfig::Search(const char* key)
|
||||
{
|
||||
itertype(m_valueMap) i = m_valueMap.find(key);
|
||||
|
||||
if (i == m_valueMap.end())
|
||||
return NULL;
|
||||
else
|
||||
return (&i->second);
|
||||
}
|
||||
|
||||
bool CConfig::GetParam(const char*key, int index, DWORD *Param)
|
||||
{
|
||||
std::string * pstStr = Search(key);
|
||||
if (!pstStr)
|
||||
return false;
|
||||
|
||||
char szParam[5][32];
|
||||
|
||||
sscanf(pstStr->c_str(), "%s %s %s %s %s", szParam[0],szParam[1],szParam[2],szParam[3],szParam[4]);
|
||||
|
||||
str_to_number(*Param, szParam[index]);
|
||||
|
||||
sys_log(0, "GetParam %d", *Param);
|
||||
return true;
|
||||
}
|
||||
const char * CConfig::Get(const char* key)
|
||||
{
|
||||
std::string * pstStr = Search(key);
|
||||
|
||||
if (!pstStr)
|
||||
{
|
||||
static std::string stTemp = "";
|
||||
return stTemp.c_str();
|
||||
}
|
||||
|
||||
return pstStr->c_str();
|
||||
}
|
||||
|
||||
|
||||
bool CConfig::GetValue(const char * key, int* dest)
|
||||
{
|
||||
if (!Search(key))
|
||||
return false;
|
||||
|
||||
str_to_number(*dest, Get(key));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::GetValue(const char * key, float *dest)
|
||||
{
|
||||
if (!Search(key))
|
||||
return false;
|
||||
|
||||
str_to_number(*dest, Get(key));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::GetValue(const char * key, DWORD *dest)
|
||||
{
|
||||
if (!Search(key))
|
||||
return false;
|
||||
|
||||
str_to_number(*dest, Get(key));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::GetValue(const char * key, BYTE *dest)
|
||||
{
|
||||
if (!Search(key))
|
||||
return false;
|
||||
|
||||
*dest = *(BYTE *) Get(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::GetValue(const char * key, char *dest, size_t destSize)
|
||||
{
|
||||
if (!Search(key))
|
||||
return false;
|
||||
|
||||
strlcpy(dest, Get(key), destSize);
|
||||
|
||||
if (!*dest)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CConfig::GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2)
|
||||
{
|
||||
if (!GetParam(key, 0, dest1))
|
||||
return false;
|
||||
|
||||
if (!GetParam(key, 1, dest2))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef __INC_CONFIG_H__
|
||||
#define __INC_CONFIG_H__
|
||||
|
||||
typedef std::map<std::string, std::string> TValueMap;
|
||||
|
||||
class CConfig : public singleton<CConfig>
|
||||
{
|
||||
public:
|
||||
CConfig();
|
||||
~CConfig();
|
||||
|
||||
bool LoadFile(const char* filename);
|
||||
bool GetValue(const char* key, int* dest);
|
||||
bool GetValue(const char* key, float* dest);
|
||||
bool GetValue(const char* key, DWORD* dest);
|
||||
bool GetValue(const char* key, BYTE* dest);
|
||||
bool GetValue(const char* key, char* dest, size_t destSize);
|
||||
bool GetWord(FILE* fp, char* dest);
|
||||
bool GetLine(FILE* fp, char* dest);
|
||||
bool GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2);
|
||||
void NextLine(FILE* fp);
|
||||
|
||||
private:
|
||||
void Destroy();
|
||||
bool GetParam(const char*key,int index, DWORD *Param);
|
||||
|
||||
const char * Get(const char* key);
|
||||
std::string * Search(const char* key);
|
||||
|
||||
private:
|
||||
TValueMap m_valueMap;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,430 @@
|
|||
#include "stdafx.h"
|
||||
#include "CsvReader.h"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef Assert
|
||||
#include <assert.h>
|
||||
#define Assert assert
|
||||
#define LogToFile (void)(0);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/// 파싱용 state 열거값
|
||||
enum ParseState
|
||||
{
|
||||
STATE_NORMAL = 0, ///< 일반 상태
|
||||
STATE_QUOTE ///< 따옴표 뒤의 상태
|
||||
};
|
||||
|
||||
/// 문자열 좌우의 공백을 제거해서 반환한다.
|
||||
std::string Trim(std::string str)
|
||||
{
|
||||
str = str.erase(str.find_last_not_of(" \t\r\n") + 1);
|
||||
str = str.erase(0, str.find_first_not_of(" \t\r\n"));
|
||||
return str;
|
||||
}
|
||||
|
||||
/// \brief 주어진 문장에 있는 알파벳을 모두 소문자로 바꾼다.
|
||||
std::string Lower(std::string original)
|
||||
{
|
||||
std::transform(original.begin(), original.end(), original.begin(), tolower);
|
||||
return original;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
|
||||
/// \param name 셀 이름
|
||||
/// \param index 셀 인덱스
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void cCsvAlias::AddAlias(const char* name, size_t index)
|
||||
{
|
||||
std::string converted(Lower(name));
|
||||
|
||||
Assert(m_Name2Index.find(converted) == m_Name2Index.end());
|
||||
Assert(m_Index2Name.find(index) == m_Index2Name.end());
|
||||
|
||||
m_Name2Index.insert(NAME2INDEX_MAP::value_type(converted, index));
|
||||
m_Index2Name.insert(INDEX2NAME_MAP::value_type(index, name));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 모든 데이터를 삭제한다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void cCsvAlias::Destroy()
|
||||
{
|
||||
m_Name2Index.clear();
|
||||
m_Index2Name.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 숫자 인덱스를 이름으로 변환한다.
|
||||
/// \param index 숫자 인덱스
|
||||
/// \return const char* 이름
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const char* cCsvAlias::operator [] (size_t index) const
|
||||
{
|
||||
INDEX2NAME_MAP::const_iterator itr(m_Index2Name.find(index));
|
||||
if (itr == m_Index2Name.end())
|
||||
{
|
||||
LogToFile(NULL, "cannot find suitable conversion for %d", index);
|
||||
Assert(false && "cannot find suitable conversion");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return itr->second.c_str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 이름을 숫자 인덱스로 변환한다.
|
||||
/// \param name 이름
|
||||
/// \return size_t 숫자 인덱스
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
size_t cCsvAlias::operator [] (const char* name) const
|
||||
{
|
||||
NAME2INDEX_MAP::const_iterator itr(m_Name2Index.find(Lower(name)));
|
||||
if (itr == m_Name2Index.end())
|
||||
{
|
||||
LogToFile(NULL, "cannot find suitable conversion for %s", name);
|
||||
Assert(false && "cannot find suitable conversion");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 지정된 이름의 CSV 파일을 로드한다.
|
||||
/// \param fileName CSV 파일 이름
|
||||
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
|
||||
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
|
||||
/// \return bool 무사히 로드했다면 true, 아니라면 false
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool cCsvFile::Load(const char* fileName, const char seperator, const char quote)
|
||||
{
|
||||
Assert(seperator != quote);
|
||||
|
||||
std::ifstream file(fileName, std::ios::in);
|
||||
if (!file) return false;
|
||||
|
||||
Destroy(); // 기존의 데이터를 삭제
|
||||
|
||||
cCsvRow* row = NULL;
|
||||
ParseState state = STATE_NORMAL;
|
||||
std::string token = "";
|
||||
char buf[2048+1] = {0,};
|
||||
|
||||
while (file.good())
|
||||
{
|
||||
file.getline(buf, 2048);
|
||||
buf[sizeof(buf)-1] = 0;
|
||||
|
||||
std::string line(Trim(buf));
|
||||
if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue;
|
||||
|
||||
std::string text = std::string(line) + " "; // 파싱 lookahead 때문에 붙여준다.
|
||||
size_t cur = 0;
|
||||
|
||||
while (cur < text.size())
|
||||
{
|
||||
// 현재 모드가 QUOTE 모드일 때,
|
||||
if (state == STATE_QUOTE)
|
||||
{
|
||||
// '"' 문자의 종류는 두 가지이다.
|
||||
// 1. 셀 내부에 특수 문자가 있을 경우 이를 알리는 셀 좌우의 것
|
||||
// 2. 셀 내부의 '"' 문자가 '"' 2개로 치환된 것
|
||||
// 이 중 첫번째 경우의 좌측에 있는 것은 CSV 파일이 정상적이라면,
|
||||
// 무조건 STATE_NORMAL에 걸리게 되어있다.
|
||||
// 그러므로 여기서 걸리는 것은 1번의 우측 경우나, 2번 경우 뿐이다.
|
||||
// 2번의 경우에는 무조건 '"' 문자가 2개씩 나타난다. 하지만 1번의
|
||||
// 우측 경우에는 아니다. 이를 바탕으로 해서 코드를 짜면...
|
||||
if (text[cur] == quote)
|
||||
{
|
||||
// 다음 문자가 '"' 문자라면, 즉 연속된 '"' 문자라면
|
||||
// 이는 셀 내부의 '"' 문자가 치환된 것이다.
|
||||
if (text[cur+1] == quote)
|
||||
{
|
||||
token += quote;
|
||||
++cur;
|
||||
}
|
||||
// 다음 문자가 '"' 문자가 아니라면
|
||||
// 현재의 '"'문자는 셀의 끝을 알리는 문자라고 할 수 있다.
|
||||
else
|
||||
{
|
||||
state = STATE_NORMAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token += text[cur];
|
||||
}
|
||||
}
|
||||
// 현재 모드가 NORMAL 모드일 때,
|
||||
else if (state == STATE_NORMAL)
|
||||
{
|
||||
if (row == NULL)
|
||||
row = new cCsvRow();
|
||||
|
||||
// ',' 문자를 만났다면 셀의 끝의 의미한다.
|
||||
// 토큰으로서 셀 리스트에다가 집어넣고, 토큰을 초기화한다.
|
||||
if (text[cur] == seperator)
|
||||
{
|
||||
row->push_back(token);
|
||||
token.clear();
|
||||
}
|
||||
// '"' 문자를 만났다면, QUOTE 모드로 전환한다.
|
||||
else if (text[cur] == quote)
|
||||
{
|
||||
state = STATE_QUOTE;
|
||||
}
|
||||
// 다른 일반 문자라면 현재 토큰에다가 덧붙인다.
|
||||
else
|
||||
{
|
||||
token += text[cur];
|
||||
}
|
||||
}
|
||||
|
||||
++cur;
|
||||
}
|
||||
|
||||
// 마지막 셀은 끝에 ',' 문자가 없기 때문에 여기서 추가해줘야한다.
|
||||
// 단, 처음에 파싱 lookahead 때문에 붙인 스페이스 문자 두 개를 뗀다.
|
||||
if (state == STATE_NORMAL)
|
||||
{
|
||||
Assert(row != NULL);
|
||||
row->push_back(token.substr(0, token.size()-2));
|
||||
m_Rows.push_back(row);
|
||||
token.clear();
|
||||
row = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = token.substr(0, token.size()-2) + "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 가지고 있는 내용을 CSV 파일에다 저장한다.
|
||||
/// \param fileName CSV 파일 이름
|
||||
/// \param append true일 경우, 기존의 파일에다 덧붙인다. false인 경우에는
|
||||
/// 기존의 파일 내용을 삭제하고, 새로 쓴다.
|
||||
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
|
||||
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
|
||||
/// \return bool 무사히 저장했다면 true, 에러가 생긴 경우에는 false
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quote) const
|
||||
{
|
||||
Assert(seperator != quote);
|
||||
|
||||
// 출력 모드에 따라 파일을 적당한 플래그로 생성한다.
|
||||
std::ofstream file;
|
||||
if (append) { file.open(fileName, std::ios::out | std::ios::app); }
|
||||
else { file.open(fileName, std::ios::out | std::ios::trunc); }
|
||||
|
||||
// 파일을 열지 못했다면, false를 리턴한다.
|
||||
if (!file) return false;
|
||||
|
||||
char special_chars[5] = { seperator, quote, '\r', '\n', 0 };
|
||||
char quote_escape_string[3] = { quote, quote, 0 };
|
||||
|
||||
// 모든 행을 횡단하면서...
|
||||
for (size_t i=0; i<m_Rows.size(); i++)
|
||||
{
|
||||
const cCsvRow& row = *((*this)[i]);
|
||||
|
||||
std::string line;
|
||||
|
||||
// 행 안의 모든 토큰을 횡단하면서...
|
||||
for (size_t j=0; j<row.size(); j++)
|
||||
{
|
||||
const std::string& token = row[j];
|
||||
|
||||
// 일반적인('"' 또는 ','를 포함하지 않은)
|
||||
// 토큰이라면 그냥 저장하면 된다.
|
||||
if (token.find_first_of(special_chars) == std::string::npos)
|
||||
{
|
||||
line += token;
|
||||
}
|
||||
// 특수문자를 포함한 토큰이라면 문자열 좌우에 '"'를 붙여주고,
|
||||
// 문자열 내부의 '"'를 두 개로 만들어줘야한다.
|
||||
else
|
||||
{
|
||||
line += quote;
|
||||
|
||||
for (size_t k=0; k<token.size(); k++)
|
||||
{
|
||||
if (token[k] == quote) line += quote_escape_string;
|
||||
else line += token[k];
|
||||
}
|
||||
|
||||
line += quote;
|
||||
}
|
||||
|
||||
// 마지막 셀이 아니라면 ','를 토큰의 뒤에다 붙여줘야한다.
|
||||
if (j != row.size() - 1) { line += seperator; }
|
||||
}
|
||||
|
||||
// 라인을 출력한다.
|
||||
file << line << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 모든 데이터를 메모리에서 삭제한다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void cCsvFile::Destroy()
|
||||
{
|
||||
for (ROWS::iterator itr(m_Rows.begin()); itr != m_Rows.end(); ++itr)
|
||||
delete *itr;
|
||||
|
||||
m_Rows.clear();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 해당하는 인덱스의 행을 반환한다.
|
||||
/// \param index 인덱스
|
||||
/// \return cCsvRow* 해당 행
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
cCsvRow* cCsvFile::operator [] (size_t index)
|
||||
{
|
||||
Assert(index < m_Rows.size());
|
||||
return m_Rows[index];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 해당하는 인덱스의 행을 반환한다.
|
||||
/// \param index 인덱스
|
||||
/// \return const cCsvRow* 해당 행
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const cCsvRow* cCsvFile::operator [] (size_t index) const
|
||||
{
|
||||
Assert(index < m_Rows.size());
|
||||
return m_Rows[index];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 생성자
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
cCsvTable::cCsvTable()
|
||||
: m_CurRow(-1)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 소멸자
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
cCsvTable::~cCsvTable()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 지정된 이름의 CSV 파일을 로드한다.
|
||||
/// \param fileName CSV 파일 이름
|
||||
/// \param seperator 필드 분리자로 사용할 글자. 기본값은 ','이다.
|
||||
/// \param quote 따옴표로 사용할 글자. 기본값은 '"'이다.
|
||||
/// \return bool 무사히 로드했다면 true, 아니라면 false
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool cCsvTable::Load(const char* fileName, const char seperator, const char quote)
|
||||
{
|
||||
Destroy();
|
||||
return m_File.Load(fileName, seperator, quote);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 다음 행으로 넘어간다.
|
||||
/// \return bool 다음 행으로 무사히 넘어간 경우 true를 반환하고, 더 이상
|
||||
/// 넘어갈 행이 존재하지 않는 경우에는 false를 반환한다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool cCsvTable::Next()
|
||||
{
|
||||
// 20억번 정도 호출하면 오버플로가 일어날텐데...괜찮겠지?
|
||||
return ++m_CurRow < (int)m_File.GetRowCount() ? true : false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 현재 행의 셀 숫자를 반환한다.
|
||||
/// \return size_t 현재 행의 셀 숫자
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
size_t cCsvTable::ColCount() const
|
||||
{
|
||||
return CurRow()->size();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 인덱스를 이용해 int 형으로 셀 값을 반환한다.
|
||||
/// \param index 셀 인덱스
|
||||
/// \return int 셀 값
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int cCsvTable::AsInt(size_t index) const
|
||||
{
|
||||
const cCsvRow* const row = CurRow();
|
||||
Assert(row);
|
||||
Assert(index < row->size());
|
||||
return row->AsInt(index);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 인덱스를 이용해 double 형으로 셀 값을 반환한다.
|
||||
/// \param index 셀 인덱스
|
||||
/// \return double 셀 값
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
double cCsvTable::AsDouble(size_t index) const
|
||||
{
|
||||
const cCsvRow* const row = CurRow();
|
||||
Assert(row);
|
||||
Assert(index < row->size());
|
||||
return row->AsDouble(index);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 인덱스를 이용해 std::string 형으로 셀 값을 반환한다.
|
||||
/// \param index 셀 인덱스
|
||||
/// \return const char* 셀 값
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const char* cCsvTable::AsStringByIndex(size_t index) const
|
||||
{
|
||||
const cCsvRow* const row = CurRow();
|
||||
Assert(row);
|
||||
Assert(index < row->size());
|
||||
return row->AsString(index);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief alias를 포함해 모든 데이터를 삭제한다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void cCsvTable::Destroy()
|
||||
{
|
||||
m_File.Destroy();
|
||||
m_Alias.Destroy();
|
||||
m_CurRow = -1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief 현재 행을 반환한다.
|
||||
/// \return const cCsvRow* 액세스가 가능한 현재 행이 존재하는 경우에는 그 행의
|
||||
/// 포인터를 반환하고, 더 이상 액세스 가능한 행이 없는 경우에는 NULL을
|
||||
/// 반환한다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
const cCsvRow* const cCsvTable::CurRow() const
|
||||
{
|
||||
if (m_CurRow < 0)
|
||||
{
|
||||
Assert(false && "call Next() first!");
|
||||
return NULL;
|
||||
}
|
||||
else if (m_CurRow >= (int)m_File.GetRowCount())
|
||||
{
|
||||
Assert(false && "no more rows!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return m_File[m_CurRow];
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
#ifndef __CSVFILE_H__
|
||||
#define __CSVFILE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if _MSC_VER
|
||||
#include <hash_map>
|
||||
#else
|
||||
#include <map>
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \class cCsvAlias
|
||||
/// \brief CSV 파일을 수정했을 때 발생하는 인덱스 문제를 줄이기 위한
|
||||
/// 별명 객체.
|
||||
///
|
||||
/// 예를 들어 0번 컬럼이 A에 관한 내용을 포함하고, 1번 컬럼이 B에 관한 내용을
|
||||
/// 포함하고 있었는데...
|
||||
///
|
||||
/// <pre>
|
||||
/// int a = row.AsInt(0);
|
||||
/// int b = row.AsInt(1);
|
||||
/// </pre>
|
||||
///
|
||||
/// 그 사이에 C에 관한 내용을 포함하는 컬럼이 끼어든 경우, 하드코딩되어 있는
|
||||
/// 1번을 찾아서 고쳐야 하는데, 상당히 에러가 발생하기 쉬운 작업이다.
|
||||
///
|
||||
/// <pre>
|
||||
/// int a = row.AsInt(0);
|
||||
/// int c = row.AsInt(1);
|
||||
/// int b = row.AsInt(2); <-- 이 부분을 일일이 신경써야 한다.
|
||||
/// </pre>
|
||||
///
|
||||
/// 이 부분을 문자열로 처리하면 유지보수에 들어가는 수고를 약간이나마 줄일 수
|
||||
/// 있다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class cCsvAlias
|
||||
{
|
||||
private:
|
||||
#if _MSC_VER
|
||||
typedef stdext::hash_map<std::string, size_t> NAME2INDEX_MAP;
|
||||
typedef stdext::hash_map<size_t, std::string> INDEX2NAME_MAP;
|
||||
#else
|
||||
typedef std::map<std::string, size_t> NAME2INDEX_MAP;
|
||||
typedef std::map<size_t, std::string> INDEX2NAME_MAP;
|
||||
#endif
|
||||
|
||||
NAME2INDEX_MAP m_Name2Index; ///< 셀 인덱스 대신으로 사용하기 위한 이름들
|
||||
INDEX2NAME_MAP m_Index2Name; ///< 잘못된 alias를 검사하기 위한 추가적인 맵
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 생성자
|
||||
cCsvAlias() {}
|
||||
|
||||
/// \brief 소멸자
|
||||
virtual ~cCsvAlias() {}
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
|
||||
void AddAlias(const char* name, size_t index);
|
||||
|
||||
/// \brief 모든 데이터를 삭제한다.
|
||||
void Destroy();
|
||||
|
||||
/// \brief 숫자 인덱스를 이름으로 변환한다.
|
||||
const char* operator [] (size_t index) const;
|
||||
|
||||
/// \brief 이름을 숫자 인덱스로 변환한다.
|
||||
size_t operator [] (const char* name) const;
|
||||
|
||||
|
||||
private:
|
||||
/// \brief 복사 생성자 금지
|
||||
cCsvAlias(const cCsvAlias&) {}
|
||||
|
||||
/// \brief 대입 연산자 금지
|
||||
const cCsvAlias& operator = (const cCsvAlias&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \class cCsvRow
|
||||
/// \brief CSV 파일의 한 행을 캡슐화한 클래스
|
||||
///
|
||||
/// CSV의 기본 포맷은 엑셀에서 보이는 하나의 셀을 ',' 문자로 구분한 것이다.
|
||||
/// 하지만, 셀 안에 특수 문자로 쓰이는 ',' 문자나 '"' 문자가 들어갈 경우,
|
||||
/// 모양이 약간 이상하게 변한다. 다음은 그 변화의 예이다.
|
||||
///
|
||||
/// <pre>
|
||||
/// 엑셀에서 보이는 모양 | 실제 CSV 파일에 들어가있는 모양
|
||||
/// ---------------------+----------------------------------------------------
|
||||
/// ItemPrice | ItemPrice
|
||||
/// Item,Price | "Item,Price"
|
||||
/// Item"Price | "Item""Price"
|
||||
/// "ItemPrice" | """ItemPrice"""
|
||||
/// "Item,Price" | """Item,Price"""
|
||||
/// Item",Price | "Item"",Price"
|
||||
/// </pre>
|
||||
///
|
||||
/// 이 예로서 다음과 같은 사항을 알 수 있다.
|
||||
/// - 셀 내부에 ',' 또는 '"' 문자가 들어갈 경우, 셀 좌우에 '"' 문자가 생긴다.
|
||||
/// - 셀 내부의 '"' 문자는 2개로 치환된다.
|
||||
///
|
||||
/// \sa cCsvFile
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class cCsvRow : public std::vector<std::string>
|
||||
{
|
||||
public:
|
||||
/// \brief 기본 생성자
|
||||
cCsvRow() {}
|
||||
|
||||
/// \brief 소멸자
|
||||
~cCsvRow() {}
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 해당 셀의 데이터를 int 형으로 반환한다.
|
||||
int AsInt(size_t index) const { return atoi(at(index).c_str()); }
|
||||
|
||||
/// \brief 해당 셀의 데이터를 double 형으로 반환한다.
|
||||
double AsDouble(size_t index) const { return atof(at(index).c_str()); }
|
||||
|
||||
/// \brief 해당 셀의 데이터를 문자열로 반환한다.
|
||||
const char* AsString(size_t index) const { return at(index).c_str(); }
|
||||
|
||||
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
|
||||
int AsInt(const char* name, const cCsvAlias& alias) const {
|
||||
return atoi( at(alias[name]).c_str() );
|
||||
}
|
||||
|
||||
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
|
||||
double AsDouble(const char* name, const cCsvAlias& alias) const {
|
||||
return atof( at(alias[name]).c_str() );
|
||||
}
|
||||
|
||||
/// \brief 해당하는 이름의 셀 데이터를 문자열로 반환한다.
|
||||
const char* AsString(const char* name, const cCsvAlias& alias) const {
|
||||
return at(alias[name]).c_str();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
/// \brief 복사 생성자 금지
|
||||
cCsvRow(const cCsvRow&) {}
|
||||
|
||||
/// \brief 대입 연산자 금지
|
||||
const cCsvRow& operator = (const cCsvRow&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \class cCsvFile
|
||||
/// \brief CSV(Comma Seperated Values) 파일을 read/write하기 위한 클래스
|
||||
///
|
||||
/// <b>sample</b>
|
||||
/// <pre>
|
||||
/// cCsvFile file;
|
||||
///
|
||||
/// cCsvRow row1, row2, row3;
|
||||
/// row1.push_back("ItemPrice");
|
||||
/// row1.push_back("Item,Price");
|
||||
/// row1.push_back("Item\"Price");
|
||||
///
|
||||
/// row2.reserve(3);
|
||||
/// row2[0] = "\"ItemPrice\"";
|
||||
/// row2[1] = "\"Item,Price\"";
|
||||
/// row2[2] = "Item\",Price\"";
|
||||
///
|
||||
/// row3 = "\"ItemPrice\"\"Item,Price\"Item\",Price\"";
|
||||
///
|
||||
/// file.add(row1);
|
||||
/// file.add(row2);
|
||||
/// file.add(row3);
|
||||
/// file.save("test.csv", false);
|
||||
/// </pre>
|
||||
///
|
||||
/// \todo 파일에서만 읽어들일 것이 아니라, 메모리 소스로부터 읽는 함수도
|
||||
/// 있어야 할 듯 하다.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class cCsvFile
|
||||
{
|
||||
private:
|
||||
typedef std::vector<cCsvRow*> ROWS;
|
||||
|
||||
ROWS m_Rows; ///< 행 컬렉션
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 생성자
|
||||
cCsvFile() {}
|
||||
|
||||
/// \brief 소멸자
|
||||
virtual ~cCsvFile() { Destroy(); }
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 지정된 이름의 CSV 파일을 로드한다.
|
||||
bool Load(const char* fileName, const char seperator=',', const char quote='"');
|
||||
|
||||
/// \brief 가지고 있는 내용을 CSV 파일에다 저장한다.
|
||||
bool Save(const char* fileName, bool append=false, char seperator=',', char quote='"') const;
|
||||
|
||||
/// \brief 모든 데이터를 메모리에서 삭제한다.
|
||||
void Destroy();
|
||||
|
||||
/// \brief 해당하는 인덱스의 행을 반환한다.
|
||||
cCsvRow* operator [] (size_t index);
|
||||
|
||||
/// \brief 해당하는 인덱스의 행을 반환한다.
|
||||
const cCsvRow* operator [] (size_t index) const;
|
||||
|
||||
/// \brief 행의 갯수를 반환한다.
|
||||
size_t GetRowCount() const { return m_Rows.size(); }
|
||||
|
||||
|
||||
private:
|
||||
/// \brief 복사 생성자 금지
|
||||
cCsvFile(const cCsvFile&) {}
|
||||
|
||||
/// \brief 대입 연산자 금지
|
||||
const cCsvFile& operator = (const cCsvFile&) { return *this; }
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \class cCsvTable
|
||||
/// \brief CSV 파일을 이용해 테이블 데이터를 로드하는 경우가 많은데, 이 클래스는
|
||||
/// 그 작업을 좀 더 쉽게 하기 위해 만든 유틸리티 클래스다.
|
||||
///
|
||||
/// CSV 파일을 로드하는 경우, 숫자를 이용해 셀을 액세스해야 하는데, CSV
|
||||
/// 파일의 포맷이 바뀌는 경우, 이 숫자들을 변경해줘야한다. 이 작업이 꽤
|
||||
/// 신경 집중을 요구하는 데다가, 에러가 발생하기 쉽다. 그러므로 숫자로
|
||||
/// 액세스하기보다는 문자열로 액세스하는 것이 약간 느리지만 낫다고 할 수 있다.
|
||||
///
|
||||
/// <b>sample</b>
|
||||
/// <pre>
|
||||
/// cCsvTable table;
|
||||
///
|
||||
/// table.alias(0, "ItemClass");
|
||||
/// table.alias(1, "ItemType");
|
||||
///
|
||||
/// if (table.load("test.csv"))
|
||||
/// {
|
||||
/// while (table.next())
|
||||
/// {
|
||||
/// std::string item_class = table.AsString("ItemClass");
|
||||
/// int item_type = table.AsInt("ItemType");
|
||||
/// }
|
||||
/// }
|
||||
/// </pre>
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class cCsvTable
|
||||
{
|
||||
public :
|
||||
cCsvFile m_File; ///< CSV 파일 객체
|
||||
private:
|
||||
cCsvAlias m_Alias; ///< 문자열을 셀 인덱스로 변환하기 위한 객체
|
||||
int m_CurRow; ///< 현재 횡단 중인 행 번호
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 생성자
|
||||
cCsvTable();
|
||||
|
||||
/// \brief 소멸자
|
||||
virtual ~cCsvTable();
|
||||
|
||||
|
||||
public:
|
||||
/// \brief 지정된 이름의 CSV 파일을 로드한다.
|
||||
bool Load(const char* fileName, const char seperator=',', const char quote='"');
|
||||
|
||||
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
|
||||
void AddAlias(const char* name, size_t index) { m_Alias.AddAlias(name, index); }
|
||||
|
||||
/// \brief 다음 행으로 넘어간다.
|
||||
bool Next();
|
||||
|
||||
/// \brief 현재 행의 셀 숫자를 반환한다.
|
||||
size_t ColCount() const;
|
||||
|
||||
/// \brief 인덱스를 이용해 int 형으로 셀값을 반환한다.
|
||||
int AsInt(size_t index) const;
|
||||
|
||||
/// \brief 인덱스를 이용해 double 형으로 셀값을 반환한다.
|
||||
double AsDouble(size_t index) const;
|
||||
|
||||
/// \brief 인덱스를 이용해 std::string 형으로 셀값을 반환한다.
|
||||
const char* AsStringByIndex(size_t index) const;
|
||||
|
||||
/// \brief 셀 이름을 이용해 int 형으로 셀값을 반환한다.
|
||||
int AsInt(const char* name) const { return AsInt(m_Alias[name]); }
|
||||
|
||||
/// \brief 셀 이름을 이용해 double 형으로 셀값을 반환한다.
|
||||
double AsDouble(const char* name) const { return AsDouble(m_Alias[name]); }
|
||||
|
||||
/// \brief 셀 이름을 이용해 std::string 형으로 셀값을 반환한다.
|
||||
const char* AsString(const char* name) const { return AsStringByIndex(m_Alias[name]); }
|
||||
|
||||
/// \brief alias를 포함해 모든 데이터를 삭제한다.
|
||||
void Destroy();
|
||||
|
||||
|
||||
private:
|
||||
/// \brief 현재 행을 반환한다.
|
||||
const cCsvRow* const CurRow() const;
|
||||
|
||||
/// \brief 복사 생성자 금지
|
||||
cCsvTable(const cCsvTable&) {}
|
||||
|
||||
/// \brief 대입 연산자 금지
|
||||
const cCsvTable& operator = (const cCsvTable&) { return *this; }
|
||||
};
|
||||
|
||||
#endif //__CSVFILE_H__
|
|
@ -0,0 +1,188 @@
|
|||
#include "stdafx.h"
|
||||
#include "DBManager.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
extern std::string g_stLocale;
|
||||
|
||||
CDBManager::CDBManager()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
CDBManager::~CDBManager()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void CDBManager::Initialize()
|
||||
{
|
||||
for (int i = 0; i < SQL_MAX_NUM; ++i)
|
||||
{
|
||||
m_mainSQL[i] = NULL;
|
||||
m_directSQL[i] = NULL;
|
||||
m_asyncSQL[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CDBManager::Destroy()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CDBManager::Clear()
|
||||
{
|
||||
for (int i = 0; i < SQL_MAX_NUM; ++i)
|
||||
{
|
||||
if (m_mainSQL[i])
|
||||
{
|
||||
delete m_mainSQL[i];
|
||||
m_mainSQL[i] = NULL;
|
||||
}
|
||||
|
||||
if (m_directSQL[i])
|
||||
{
|
||||
delete m_directSQL[i];
|
||||
m_directSQL[i] = NULL;
|
||||
}
|
||||
|
||||
if (m_asyncSQL[i])
|
||||
{
|
||||
delete m_asyncSQL[i];
|
||||
m_asyncSQL[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void CDBManager::Quit()
|
||||
{
|
||||
for (int i = 0; i < SQL_MAX_NUM; ++i)
|
||||
{
|
||||
if (m_mainSQL[i])
|
||||
m_mainSQL[i]->Quit();
|
||||
|
||||
if (m_asyncSQL[i])
|
||||
m_asyncSQL[i]->Quit();
|
||||
|
||||
if (m_directSQL[i])
|
||||
m_directSQL[i]->Quit();
|
||||
}
|
||||
}
|
||||
|
||||
SQLMsg * CDBManager::PopResult()
|
||||
{
|
||||
SQLMsg * p;
|
||||
|
||||
for (int i = 0; i < SQL_MAX_NUM; ++i)
|
||||
if (m_mainSQL[i] && m_mainSQL[i]->PopResult(&p))
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SQLMsg * CDBManager::PopResult(eSQL_SLOT slot)
|
||||
{
|
||||
SQLMsg * p;
|
||||
|
||||
if (m_mainSQL[slot] && m_mainSQL[slot]->PopResult(&p))
|
||||
return p;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
int CDBManager::Connect(int iSlot, const char * db_address, const int db_port, const char * db_name, const char * user, const char * pwd)
|
||||
{
|
||||
if (db_address == NULL || db_name == NULL)
|
||||
return false;
|
||||
|
||||
if (iSlot < 0 || iSlot >= SQL_MAX_NUM)
|
||||
return false;
|
||||
|
||||
sys_log(0, "CREATING DIRECT_SQL");
|
||||
m_directSQL[iSlot] = new CAsyncSQL2;
|
||||
if (!m_directSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), true, db_port))
|
||||
{
|
||||
Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
sys_log(0, "CREATING MAIN_SQL");
|
||||
m_mainSQL[iSlot] = new CAsyncSQL2;
|
||||
if (!m_mainSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port))
|
||||
{
|
||||
Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_log(0, "CREATING ASYNC_SQL");
|
||||
m_asyncSQL[iSlot] = new CAsyncSQL2;
|
||||
if (!m_asyncSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port))
|
||||
{
|
||||
Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SQLMsg * CDBManager::DirectQuery(const char * c_pszQuery, int iSlot)
|
||||
{
|
||||
return m_directSQL[iSlot]->DirectQuery(c_pszQuery);
|
||||
}
|
||||
|
||||
extern CPacketInfo g_query_info;
|
||||
extern int g_query_count[2];
|
||||
|
||||
void CDBManager::ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * udata, int iSlot)
|
||||
{
|
||||
assert(iSlot < SQL_MAX_NUM);
|
||||
//sys_log(0, "ReturnQuery %s", c_pszQuery);
|
||||
CQueryInfo * p = new CQueryInfo;
|
||||
|
||||
p->iType = iType;
|
||||
p->dwIdent = dwIdent;
|
||||
p->pvData = udata;
|
||||
|
||||
m_mainSQL[iSlot]->ReturnQuery(c_pszQuery, p);
|
||||
|
||||
//g_query_info.Add(iType);
|
||||
++g_query_count[0];
|
||||
}
|
||||
|
||||
void CDBManager::AsyncQuery(const char * c_pszQuery, int iSlot)
|
||||
{
|
||||
assert(iSlot < SQL_MAX_NUM);
|
||||
m_asyncSQL[iSlot]->AsyncQuery(c_pszQuery);
|
||||
++g_query_count[1];
|
||||
}
|
||||
|
||||
unsigned long CDBManager::EscapeString(void *to, const void *from, unsigned long length, int iSlot)
|
||||
{
|
||||
assert(iSlot < SQL_MAX_NUM);
|
||||
return mysql_real_escape_string(m_directSQL[iSlot]->GetSQLHandle(), (char *) to, (const char *) from, length);
|
||||
}
|
||||
|
||||
void CDBManager::SetLocale(const char * szLocale)
|
||||
{
|
||||
const std::string stLocale(szLocale);
|
||||
sys_log(0, "SetLocale start" );
|
||||
for (int n = 0; n < SQL_MAX_NUM; ++n)
|
||||
{
|
||||
m_mainSQL[n]->SetLocale(stLocale);
|
||||
m_directSQL[n]->SetLocale(stLocale);
|
||||
m_asyncSQL[n]->SetLocale(stLocale);
|
||||
}
|
||||
sys_log(0, "End setlocale %s", szLocale);
|
||||
}
|
||||
|
||||
void CDBManager::QueryLocaleSet()
|
||||
{
|
||||
for (int n = 0; n < SQL_MAX_NUM; ++n)
|
||||
{
|
||||
m_mainSQL[n]->QueryLocaleSet();
|
||||
m_directSQL[n]->QueryLocaleSet();
|
||||
m_asyncSQL[n]->QueryLocaleSet();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_METIN2_DB_DBMANAGER_H__
|
||||
#define __INC_METIN2_DB_DBMANAGER_H__
|
||||
|
||||
// 디비 커넥션 클래스의 목적은... 디비에 접속해서 쿼리보내고 결과 받아오는
|
||||
// 모든 일들을 처리한다.
|
||||
// 코드 by 꼬붕 후로그래머 아노아~ = _=)b
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#include "../../libsql/AsyncSQL.h"
|
||||
|
||||
#define SQL_SAFE_LENGTH(size) (size * 2 + 1)
|
||||
#define QUERY_SAFE_LENGTH(size) (1024 + SQL_SAFE_LENGTH(size))
|
||||
|
||||
class CQueryInfo
|
||||
{
|
||||
public:
|
||||
int iType;
|
||||
DWORD dwIdent;
|
||||
void * pvData;
|
||||
};
|
||||
|
||||
enum eSQL_SLOT
|
||||
{
|
||||
SQL_PLAYER,
|
||||
SQL_ACCOUNT,
|
||||
SQL_COMMON,
|
||||
SQL_HOTBACKUP,
|
||||
SQL_MAX_NUM,
|
||||
};
|
||||
|
||||
class CDBManager : public singleton<CDBManager>
|
||||
{
|
||||
protected:
|
||||
void Initialize();
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
CDBManager();
|
||||
virtual ~CDBManager();
|
||||
|
||||
void Clear();
|
||||
void Quit();
|
||||
|
||||
int Connect(int iSlot, const char * host, int port, const char* dbname, const char* user, const char* pass);
|
||||
|
||||
void ReturnQuery(const char * c_pszQuery, int iType, DWORD dwIdent, void * pvData, int iSlot = SQL_PLAYER);
|
||||
void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
SQLMsg * DirectQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
|
||||
SQLMsg * PopResult();
|
||||
SQLMsg * PopResult(eSQL_SLOT slot );
|
||||
|
||||
unsigned long EscapeString(void * to, const void * from, unsigned long length, int iSlot = SQL_PLAYER);
|
||||
|
||||
DWORD CountReturnQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQuery() : 0; }
|
||||
DWORD CountReturnResult(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountResult() : 0; }
|
||||
DWORD CountReturnQueryFinished(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQueryFinished() : 0; }
|
||||
DWORD CountReturnCopiedQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->GetCopiedQueryCount() : 0; }
|
||||
|
||||
DWORD CountAsyncQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQuery() : 0; }
|
||||
DWORD CountAsyncResult(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountResult() : 0; }
|
||||
DWORD CountAsyncQueryFinished(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQueryFinished() : 0; }
|
||||
DWORD CountAsyncCopiedQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->GetCopiedQueryCount() : 0; }
|
||||
|
||||
void ResetCounter()
|
||||
{
|
||||
for (int i = 0; i < SQL_MAX_NUM; ++i)
|
||||
{
|
||||
if (m_mainSQL[i])
|
||||
{
|
||||
m_mainSQL[i]->ResetQueryFinished();
|
||||
m_mainSQL[i]->ResetCopiedQueryCount();
|
||||
}
|
||||
|
||||
if (m_asyncSQL[i])
|
||||
{
|
||||
m_asyncSQL[i]->ResetQueryFinished();
|
||||
m_asyncSQL[i]->ResetCopiedQueryCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CAsyncSQL2 * m_mainSQL[SQL_MAX_NUM];
|
||||
CAsyncSQL2 * m_directSQL[SQL_MAX_NUM];
|
||||
CAsyncSQL2 * m_asyncSQL[SQL_MAX_NUM];
|
||||
|
||||
int m_quit; // looping flag
|
||||
|
||||
//CHARSET
|
||||
public:
|
||||
void SetLocale(const char * szLocale );
|
||||
void QueryLocaleSet();
|
||||
private:
|
||||
|
||||
//END_CHARSET
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,260 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_GUILD_MANAGER_H
|
||||
#define __INC_GUILD_MANAGER_H
|
||||
|
||||
#include "Peer.h"
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include "../../libsql/libsql.h"
|
||||
#include "../../libpoly/Poly.h"
|
||||
|
||||
enum
|
||||
{
|
||||
GUILD_WARP_WAR_CHANNEL = 99
|
||||
};
|
||||
|
||||
class CGuildWarReserve;
|
||||
|
||||
struct TGuildDeclareInfo
|
||||
{
|
||||
BYTE bType;
|
||||
DWORD dwGuildID[2];
|
||||
|
||||
TGuildDeclareInfo(BYTE _bType, DWORD _dwGuildID1, DWORD _dwGuildID2)
|
||||
: bType(_bType)
|
||||
{
|
||||
dwGuildID[0] = _dwGuildID1;
|
||||
dwGuildID[1] = _dwGuildID2;
|
||||
}
|
||||
|
||||
bool operator < (const TGuildDeclareInfo& r) const
|
||||
{
|
||||
return dwGuildID[0] < r.dwGuildID[0] || dwGuildID[0] == r.dwGuildID[0] && dwGuildID[1] < r.dwGuildID[1];
|
||||
}
|
||||
|
||||
TGuildDeclareInfo& operator = (const TGuildDeclareInfo& r)
|
||||
{
|
||||
bType = r.bType;
|
||||
dwGuildID[0] = r.dwGuildID[0];
|
||||
dwGuildID[1] = r.dwGuildID[1];
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct TGuildWaitStartInfo
|
||||
{
|
||||
BYTE bType;
|
||||
DWORD GID[2];
|
||||
long lWarPrice;
|
||||
long lInitialScore;
|
||||
CGuildWarReserve * pkReserve;
|
||||
|
||||
TGuildWaitStartInfo(BYTE _bType,
|
||||
DWORD _g1,
|
||||
DWORD _g2,
|
||||
long _lWarPrice,
|
||||
long _lInitialScore,
|
||||
CGuildWarReserve * _pkReserve)
|
||||
: bType(_bType), lWarPrice(_lWarPrice), lInitialScore(_lInitialScore), pkReserve(_pkReserve)
|
||||
{
|
||||
GID[0] = _g1;
|
||||
GID[1] = _g2;
|
||||
}
|
||||
|
||||
bool operator < (const TGuildWaitStartInfo& r) const
|
||||
{
|
||||
return GID[0] < r.GID[0] || GID[0] == r.GID[0] && GID[1] < r.GID[1];
|
||||
}
|
||||
};
|
||||
|
||||
struct TGuildWarPQElement
|
||||
{
|
||||
bool bEnd;
|
||||
BYTE bType;
|
||||
DWORD GID[2];
|
||||
DWORD iScore[2];
|
||||
DWORD iBetScore[2];
|
||||
|
||||
TGuildWarPQElement(BYTE _bType, DWORD GID1, DWORD GID2) : bEnd(false), bType(_bType)
|
||||
{
|
||||
bType = _bType;
|
||||
GID[0] = GID1;
|
||||
GID[1] = GID2;
|
||||
iScore[0] = iScore[1] = 0;
|
||||
iBetScore[0] = iBetScore[1] = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct TGuildSkillUsed
|
||||
{
|
||||
DWORD GID;
|
||||
DWORD dwSkillVnum;
|
||||
|
||||
// GUILD_SKILL_COOLTIME_BUG_FIX
|
||||
TGuildSkillUsed(DWORD _GID, DWORD _dwSkillVnum) : GID(_GID), dwSkillVnum(_dwSkillVnum)
|
||||
{
|
||||
}
|
||||
// END_OF_GUILD_SKILL_COOLTIME_BUG_FIX
|
||||
};
|
||||
|
||||
inline bool operator < (const TGuildSkillUsed& a, const TGuildSkillUsed& b)
|
||||
{
|
||||
return a.GID < b.GID || a.GID == b.GID && a.dwSkillVnum < b.dwSkillVnum;
|
||||
}
|
||||
|
||||
typedef struct SGuild
|
||||
{
|
||||
SGuild() : ladder_point(0), win(0), draw(0), loss(0), gold(0), level(0)
|
||||
{
|
||||
memset(szName, 0, sizeof(szName));
|
||||
}
|
||||
|
||||
char szName[GUILD_NAME_MAX_LEN+1];
|
||||
int ladder_point;
|
||||
int win;
|
||||
int draw;
|
||||
int loss;
|
||||
int gold;
|
||||
int level;
|
||||
} TGuild;
|
||||
|
||||
typedef struct SGuildWarInfo
|
||||
{
|
||||
time_t tEndTime;
|
||||
TGuildWarPQElement * pElement;
|
||||
CGuildWarReserve * pkReserve;
|
||||
|
||||
SGuildWarInfo() : pElement(NULL)
|
||||
{
|
||||
}
|
||||
} TGuildWarInfo;
|
||||
|
||||
class CGuildWarReserve
|
||||
{
|
||||
public:
|
||||
CGuildWarReserve(const TGuildWarReserve& rTable);
|
||||
|
||||
void Initialize();
|
||||
|
||||
TGuildWarReserve & GetDataRef()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
void OnSetup(CPeer * peer);
|
||||
bool Bet(const char * pszLogin, DWORD dwGold, DWORD dwGuild);
|
||||
void Draw();
|
||||
void End(int iScoreFrom, int iScoreTo);
|
||||
|
||||
int GetLastNoticeMin() { return m_iLastNoticeMin; }
|
||||
void SetLastNoticeMin(int iMin) { m_iLastNoticeMin = iMin; }
|
||||
|
||||
private:
|
||||
CGuildWarReserve(); // 기본 생성자를 사용하지 못하도록 의도적으로 구현하지 않음
|
||||
|
||||
TGuildWarReserve m_data;
|
||||
// <login, <guild, gold>>
|
||||
std::map<std::string, std::pair<DWORD, DWORD> > mapBet;
|
||||
int m_iLastNoticeMin;
|
||||
};
|
||||
|
||||
class CGuildManager : public singleton<CGuildManager>
|
||||
{
|
||||
public:
|
||||
CGuildManager();
|
||||
virtual ~CGuildManager();
|
||||
|
||||
void Initialize();
|
||||
|
||||
void Load(DWORD dwGuildID);
|
||||
|
||||
TGuild & TouchGuild(DWORD GID);
|
||||
|
||||
void Update();
|
||||
|
||||
void OnSetup(CPeer * peer);
|
||||
void StartWar(BYTE bType, DWORD GID1, DWORD GID2, CGuildWarReserve * pkReserve = NULL);
|
||||
|
||||
void UpdateScore(DWORD guild_gain_point, DWORD guild_opponent, int iScore, int iBetScore);
|
||||
|
||||
void AddDeclare(BYTE bType, DWORD guild_from, DWORD guild_to);
|
||||
void RemoveDeclare(DWORD guild_from, DWORD guild_to);
|
||||
|
||||
bool TakeBetPrice(DWORD dwGuildTo, DWORD dwGuildFrom, long lWarPrice);
|
||||
|
||||
bool WaitStart(TPacketGuildWar * p);
|
||||
|
||||
void RecvWarEnd(DWORD GID1, DWORD GID2);
|
||||
void RecvWarOver(DWORD dwGuildWinner, DWORD dwGuildLoser, bool bDraw, long lWarPrice);
|
||||
|
||||
void ChangeLadderPoint(DWORD GID, int change);
|
||||
|
||||
void UseSkill(DWORD dwGuild, DWORD dwSkillVnum, DWORD dwCooltime);
|
||||
|
||||
INT GetGuildGold(DWORD dwGuild);
|
||||
void DepositMoney(DWORD dwGuild, INT lGold);
|
||||
void WithdrawMoney(CPeer* peer, DWORD dwGuild, INT lGold);
|
||||
void WithdrawMoneyReply(DWORD dwGuild, BYTE bGiveSuccess, INT lGold);
|
||||
|
||||
void MoneyChange(DWORD dwGuild, DWORD dwGold);
|
||||
|
||||
void QueryRanking();
|
||||
void ResultRanking(MYSQL_RES * pRes);
|
||||
int GetRanking(DWORD dwGID);
|
||||
|
||||
//
|
||||
// Reserve War
|
||||
//
|
||||
void BootReserveWar();
|
||||
bool ReserveWar(TPacketGuildWar * p);
|
||||
void ProcessReserveWar();
|
||||
bool Bet(DWORD dwID, const char * c_pszLogin, DWORD dwGold, DWORD dwGuild);
|
||||
|
||||
void CancelWar(DWORD GID1, DWORD GID2);
|
||||
|
||||
bool ChangeMaster(DWORD dwGID, DWORD dwFrom, DWORD dwTo);
|
||||
|
||||
private:
|
||||
void ParseResult(SQLResult * pRes);
|
||||
|
||||
void RemoveWar(DWORD GID1, DWORD GID2); // erase war from m_WarMap and set end on priority queue
|
||||
|
||||
void WarEnd(DWORD GID1, DWORD GID2, bool bDraw = false);
|
||||
|
||||
int GetLadderPoint(DWORD GID);
|
||||
|
||||
void GuildWarWin(DWORD GID);
|
||||
void GuildWarDraw(DWORD GID);
|
||||
void GuildWarLose(DWORD GID);
|
||||
|
||||
void ProcessDraw(DWORD dwGuildID1, DWORD dwGuildID2);
|
||||
void ProcessWinLose(DWORD dwGuildWinner, DWORD dwGuildLoser);
|
||||
|
||||
bool IsHalfWinLadderPoint(DWORD dwGuildWinner, DWORD dwGuildLoser);
|
||||
|
||||
std::map<DWORD, TGuild> m_map_kGuild;
|
||||
std::map<DWORD, std::map<DWORD, time_t> > m_mapGuildWarEndTime;
|
||||
|
||||
std::set<TGuildDeclareInfo> m_DeclareMap; // 선전 포고 상태를 저장
|
||||
std::map<DWORD, std::map<DWORD, TGuildWarInfo> > m_WarMap;
|
||||
|
||||
typedef std::pair<time_t, TGuildWarPQElement *> stPairGuildWar;
|
||||
typedef std::pair<time_t, TGuildSkillUsed> stPairSkillUsed;
|
||||
typedef std::pair<time_t, TGuildWaitStartInfo> stPairWaitStart;
|
||||
|
||||
std::priority_queue<stPairGuildWar, std::vector<stPairGuildWar>, std::greater<stPairGuildWar> >
|
||||
m_pqOnWar;
|
||||
std::priority_queue<stPairWaitStart, std::vector<stPairWaitStart>, std::greater<stPairWaitStart> >
|
||||
m_pqWaitStart;
|
||||
std::priority_queue<stPairSkillUsed, std::vector<stPairSkillUsed>, std::greater<stPairSkillUsed> >
|
||||
m_pqSkill;
|
||||
|
||||
std::map<DWORD, CGuildWarReserve *> m_map_kWarReserve;
|
||||
CPoly polyPower;
|
||||
CPoly polyHandicap;
|
||||
|
||||
// GID Ranking
|
||||
std::map<DWORD, int> map_kLadderPointRankingByGID;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,86 @@
|
|||
#include "stdafx.h"
|
||||
#include "HB.h"
|
||||
#include "Main.h"
|
||||
#include "DBManager.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
PlayerHB::PlayerHB()
|
||||
{
|
||||
m_iExpireTime = 3600; // 1 hour hotbackup default.
|
||||
}
|
||||
|
||||
PlayerHB::~PlayerHB()
|
||||
{
|
||||
}
|
||||
|
||||
bool PlayerHB::Initialize()
|
||||
{
|
||||
char szQuery[128];
|
||||
snprintf(szQuery, sizeof(szQuery), "SHOW CREATE TABLE player%s", GetTablePostfix());
|
||||
|
||||
std::auto_ptr<SQLMsg> pMsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
return false;
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
m_stCreateTableQuery = row[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// @version 05/07/05 Bang2ni - id 에 해당하는 data 가 없을 때 쿼리하고 data 를 insert 하는코드 추가.
|
||||
//
|
||||
void PlayerHB::Put(DWORD id)
|
||||
{
|
||||
itertype(m_map_data) it = m_map_data.find(id);
|
||||
|
||||
if (it == m_map_data.end())
|
||||
{
|
||||
Query(id);
|
||||
m_map_data.insert(std::pair< DWORD, time_t >(id, get_dword_time()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (time(0) - it->second > m_iExpireTime)
|
||||
Query(id);
|
||||
}
|
||||
|
||||
//
|
||||
// @version 05/07/05 Bang2ni - Query string 버퍼가 작아서 늘려줌.
|
||||
//
|
||||
bool PlayerHB::Query(DWORD id)
|
||||
{
|
||||
time_t ct = time(0);
|
||||
struct tm curr_tm = *localtime(&ct);
|
||||
char szTableName[64];
|
||||
snprintf(szTableName, sizeof(szTableName), "hb_%02d%02d%02d%02d_player%s",
|
||||
curr_tm.tm_year - 100, curr_tm.tm_mon + 1, curr_tm.tm_mday, curr_tm.tm_hour, GetTablePostfix());
|
||||
|
||||
char szQuery[4096];
|
||||
|
||||
if (m_stTableName.compare(szTableName))
|
||||
{
|
||||
char szFind[32];
|
||||
snprintf(szFind, sizeof(szFind), "CREATE TABLE `player%s`", GetTablePostfix());
|
||||
int pos = m_stCreateTableQuery.find(szFind);
|
||||
|
||||
if (pos < 0)
|
||||
{
|
||||
sys_err("cannot find %s ", szFind);
|
||||
// sys_err("cannot find %s in %s", szFind, m_stCreateTableQuery.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "CREATE TABLE IF NOT EXISTS %s%s", szTableName, m_stCreateTableQuery.c_str() + strlen(szFind));
|
||||
// sys_log(0, "%s", szQuery);
|
||||
std::auto_ptr<SQLMsg> pMsg(CDBManager::instance().DirectQuery(szQuery, SQL_HOTBACKUP));
|
||||
m_stTableName = szTableName;
|
||||
}
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "REPLACE INTO %s SELECT * FROM %splayer%s WHERE id=%u", m_stTableName.c_str(), GetPlayerDBName(), GetTablePostfix(), id);
|
||||
CDBManager::instance().AsyncQuery(szQuery, SQL_HOTBACKUP);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_METIN_II_PLAYERHB_H__
|
||||
#define __INC_METIN_II_PLAYERHB_H__
|
||||
|
||||
class PlayerHB : public singleton<PlayerHB>
|
||||
{
|
||||
public:
|
||||
PlayerHB();
|
||||
virtual ~PlayerHB();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
void Put(DWORD id);
|
||||
|
||||
private:
|
||||
bool Query(DWORD id);
|
||||
|
||||
std::map<DWORD, time_t> m_map_data;
|
||||
std::string m_stCreateTableQuery;
|
||||
std::string m_stTableName;
|
||||
int m_iExpireTime;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,129 @@
|
|||
#include "stdafx.h"
|
||||
#include "QID.h"
|
||||
#include "DBManager.h"
|
||||
#include "ItemAwardManager.h"
|
||||
#include "Peer.h"
|
||||
|
||||
#include "ClientManager.h"
|
||||
|
||||
|
||||
|
||||
DWORD g_dwLastCachedItemAwardID = 0;
|
||||
ItemAwardManager::ItemAwardManager()
|
||||
{
|
||||
}
|
||||
|
||||
ItemAwardManager::~ItemAwardManager()
|
||||
{
|
||||
}
|
||||
|
||||
void ItemAwardManager::RequestLoad()
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT id,login,vnum,count,socket0,socket1,socket2,mall,why FROM item_award WHERE taken_time IS NULL and id > %d", g_dwLastCachedItemAwardID);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_LOAD, 0, NULL);
|
||||
}
|
||||
|
||||
void ItemAwardManager::Load(SQLMsg * pMsg)
|
||||
{
|
||||
MYSQL_RES * pRes = pMsg->Get()->pSQLResult;
|
||||
|
||||
for (uint i = 0; i < pMsg->Get()->uiNumRows; ++i)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pRes);
|
||||
int col = 0;
|
||||
|
||||
DWORD dwID = 0;
|
||||
str_to_number(dwID, row[col++]);
|
||||
|
||||
if (m_map_award.find(dwID) != m_map_award.end())
|
||||
continue;
|
||||
|
||||
TItemAward * kData = new TItemAward;
|
||||
memset(kData, 0, sizeof(TItemAward));
|
||||
|
||||
kData->dwID = dwID;
|
||||
trim_and_lower(row[col++], kData->szLogin, sizeof(kData->szLogin));
|
||||
str_to_number(kData->dwVnum, row[col++]);
|
||||
str_to_number(kData->dwCount, row[col++]);
|
||||
str_to_number(kData->dwSocket0, row[col++]);
|
||||
str_to_number(kData->dwSocket1, row[col++]);
|
||||
str_to_number(kData->dwSocket2, row[col++]);
|
||||
str_to_number(kData->bMall, row[col++]);
|
||||
|
||||
if (row[col])
|
||||
{
|
||||
strlcpy(kData->szWhy, row[col], sizeof(kData->szWhy));
|
||||
//게임 중에 why콜룸에 변동이 생기면
|
||||
char* whyStr = kData->szWhy; //why 콜룸 읽기
|
||||
char cmdStr[100] = ""; //why콜룸에서 읽은 값을 임시 문자열에 복사해둠
|
||||
strcpy(cmdStr,whyStr); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
|
||||
char command[20] = "";
|
||||
strcpy(command,CClientManager::instance().GetCommand(cmdStr)); // command 얻기
|
||||
//sys_err("%d, %s",pItemAward->dwID,command);
|
||||
if( !(strcmp(command,"GIFT") )) // command 가 GIFT이면
|
||||
{
|
||||
TPacketItemAwardInfromer giftData;
|
||||
strcpy(giftData.login, kData->szLogin); //로그인 아이디 복사
|
||||
strcpy(giftData.command, command); //명령어 복사
|
||||
giftData.vnum = kData->dwVnum; //아이템 vnum도 복사
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_ITEMAWARD_INFORMER,&giftData,sizeof(TPacketItemAwardInfromer));
|
||||
}
|
||||
}
|
||||
|
||||
m_map_award.insert(std::make_pair(dwID, kData));
|
||||
|
||||
printf("ITEM_AWARD load id %u bMall %d \n", kData->dwID, kData->bMall);
|
||||
sys_log(0, "ITEM_AWARD: load id %lu login %s vnum %lu count %u socket %lu", kData->dwID, kData->szLogin, kData->dwVnum, kData->dwCount, kData->dwSocket0);
|
||||
std::set<TItemAward *> & kSet = m_map_kSetAwardByLogin[kData->szLogin];
|
||||
kSet.insert(kData);
|
||||
|
||||
if (dwID > g_dwLastCachedItemAwardID)
|
||||
g_dwLastCachedItemAwardID = dwID;
|
||||
}
|
||||
}
|
||||
|
||||
std::set<TItemAward *> * ItemAwardManager::GetByLogin(const char * c_pszLogin)
|
||||
{
|
||||
itertype(m_map_kSetAwardByLogin) it = m_map_kSetAwardByLogin.find(c_pszLogin);
|
||||
|
||||
if (it == m_map_kSetAwardByLogin.end())
|
||||
return NULL;
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
void ItemAwardManager::Taken(DWORD dwAwardID, DWORD dwItemID)
|
||||
{
|
||||
itertype(m_map_award) it = m_map_award.find(dwAwardID);
|
||||
|
||||
if (it == m_map_award.end())
|
||||
{
|
||||
sys_log(0, "ITEM_AWARD: Taken ID not exist %lu", dwAwardID);
|
||||
return;
|
||||
}
|
||||
|
||||
TItemAward * k = it->second;
|
||||
k->bTaken = true;
|
||||
|
||||
//
|
||||
// Update taken_time in database to prevent not to give him again.
|
||||
//
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"UPDATE item_award SET taken_time=NOW(),item_id=%u WHERE id=%u AND taken_time IS NULL",
|
||||
dwItemID, dwAwardID);
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_ITEM_AWARD_TAKEN, 0, NULL);
|
||||
}
|
||||
|
||||
std::map<DWORD, TItemAward *>& ItemAwardManager::GetMapAward()
|
||||
{
|
||||
return m_map_award;
|
||||
}
|
||||
|
||||
std::map<std::string, std::set<TItemAward *> >& ItemAwardManager::GetMapkSetAwardByLogin()
|
||||
{
|
||||
return m_map_kSetAwardByLogin;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_ITEM_AWARD_H
|
||||
#define __INC_ITEM_AWARD_H
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include "Peer.h"
|
||||
|
||||
typedef struct SItemAward
|
||||
{
|
||||
DWORD dwID;
|
||||
char szLogin[LOGIN_MAX_LEN+1];
|
||||
DWORD dwVnum;
|
||||
DWORD dwCount;
|
||||
DWORD dwSocket0;
|
||||
DWORD dwSocket1;
|
||||
DWORD dwSocket2;
|
||||
char szWhy[ITEM_AWARD_WHY_MAX_LEN+1];
|
||||
bool bTaken;
|
||||
bool bMall;
|
||||
} TItemAward;
|
||||
|
||||
class ItemAwardManager : public singleton<ItemAwardManager>
|
||||
{
|
||||
public:
|
||||
ItemAwardManager();
|
||||
virtual ~ItemAwardManager();
|
||||
|
||||
void RequestLoad();
|
||||
void Load(SQLMsg * pMsg);
|
||||
std::set<TItemAward *> * GetByLogin(const char * c_pszLogin);
|
||||
|
||||
void Taken(DWORD dwAwardID, DWORD dwItemID);
|
||||
// gift notify
|
||||
std::map<DWORD, TItemAward *>& GetMapAward();
|
||||
std::map<std::string, std::set<TItemAward *> >& GetMapkSetAwardByLogin();
|
||||
private:
|
||||
// ID, ItemAward pair
|
||||
std::map<DWORD, TItemAward *> m_map_award;
|
||||
// PID, ItemAward pair
|
||||
std::map<std::string, std::set<TItemAward *> > m_map_kSetAwardByLogin;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,165 @@
|
|||
#include "stdafx.h"
|
||||
#include "ItemIDRangeManager.h"
|
||||
#include "Main.h"
|
||||
#include "DBManager.h"
|
||||
#include "ClientManager.h"
|
||||
#include "Peer.h"
|
||||
|
||||
CItemIDRangeManager::CItemIDRangeManager()
|
||||
{
|
||||
m_listData.clear();
|
||||
}
|
||||
|
||||
void CItemIDRangeManager::Build()
|
||||
{
|
||||
DWORD dwMin = 0;
|
||||
DWORD dwMax = 0;
|
||||
TItemIDRangeTable range;
|
||||
|
||||
for (int i = 0; ; ++i)
|
||||
{
|
||||
dwMin = cs_dwMinimumRange * (i + 1) + 1;
|
||||
dwMax = cs_dwMinimumRange * (i + 2);
|
||||
|
||||
if (dwMax == cs_dwMaxItemID)
|
||||
break;
|
||||
|
||||
if (CClientManager::instance().GetItemRange().dwMin <= dwMin &&
|
||||
CClientManager::instance().GetItemRange().dwMax >= dwMax)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BuildRange(dwMin, dwMax, range) == true)
|
||||
{
|
||||
m_listData.push_back(range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FCheckCollision
|
||||
{
|
||||
bool hasCollision;
|
||||
TItemIDRangeTable range;
|
||||
|
||||
FCheckCollision(TItemIDRangeTable data)
|
||||
{
|
||||
hasCollision = false;
|
||||
range = data;
|
||||
}
|
||||
|
||||
void operator() (CPeer* peer)
|
||||
{
|
||||
if (hasCollision == false)
|
||||
{
|
||||
hasCollision = peer->CheckItemIDRangeCollision(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TItemIDRangeTable CItemIDRangeManager::GetRange()
|
||||
{
|
||||
TItemIDRangeTable ret;
|
||||
ret.dwMin = 0;
|
||||
ret.dwMax = 0;
|
||||
ret.dwUsableItemIDMin = 0;
|
||||
|
||||
if (m_listData.size() > 0)
|
||||
{
|
||||
while (m_listData.size() > 0)
|
||||
{
|
||||
ret = m_listData.front();
|
||||
m_listData.pop_front();
|
||||
|
||||
FCheckCollision f(ret);
|
||||
CClientManager::instance().for_each_peer(f);
|
||||
|
||||
if (f.hasCollision == false) return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
sys_err("ItemIDRange: NO MORE ITEM ID RANGE");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CItemIDRangeManager::BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range)
|
||||
{
|
||||
char szQuery[1024];
|
||||
DWORD dwItemMaxID = 0;
|
||||
SQLMsg* pMsg = NULL;
|
||||
MYSQL_ROW row;
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT MAX(id) FROM item%s WHERE id >= %u and id <= %u", GetTablePostfix(), dwMin, dwMax);
|
||||
|
||||
pMsg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->Get()->uiNumRows > 0)
|
||||
{
|
||||
row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
str_to_number(dwItemMaxID, row[0]);
|
||||
}
|
||||
delete pMsg;
|
||||
}
|
||||
|
||||
if (dwItemMaxID == 0)
|
||||
dwItemMaxID = dwMin;
|
||||
else
|
||||
dwItemMaxID++;
|
||||
|
||||
if ((dwMax < dwItemMaxID) || (dwMax - dwItemMaxID < cs_dwMinimumRemainCount))
|
||||
{
|
||||
sys_log(0, "ItemIDRange: Build %u ~ %u start: %u\tNOT USE remain count is below %u",
|
||||
dwMin, dwMax, dwItemMaxID, cs_dwMinimumRemainCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
range.dwMin = dwMin;
|
||||
range.dwMax = dwMax;
|
||||
range.dwUsableItemIDMin = dwItemMaxID;
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM item%s WHERE id >= %u AND id <= %u",
|
||||
GetTablePostfix(), range.dwUsableItemIDMin, range.dwMax);
|
||||
|
||||
pMsg = CDBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->Get()->uiNumRows > 0)
|
||||
{
|
||||
DWORD count = 0;
|
||||
row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
str_to_number(count, row[0]);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
sys_err("ItemIDRange: Build: %u ~ %u\thave a item", range.dwUsableItemIDMin, range.dwMax);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log(0, "ItemIDRange: Build: %u ~ %u start:%u", range.dwMin, range.dwMax, range.dwUsableItemIDMin);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
delete pMsg;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CItemIDRangeManager::UpdateRange(DWORD dwMin, DWORD dwMax)
|
||||
{
|
||||
TItemIDRangeTable range;
|
||||
|
||||
if (BuildRange(dwMin, dwMax, range) == true)
|
||||
{
|
||||
m_listData.push_back(range);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// vim:ts=4 sw=4
|
||||
#ifndef __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__
|
||||
#define __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__
|
||||
|
||||
class CItemIDRangeManager : public singleton<CItemIDRangeManager>
|
||||
{
|
||||
private :
|
||||
const static DWORD cs_dwMaxItemID = 4290000000UL;
|
||||
const static DWORD cs_dwMinimumRange = 10000000UL;
|
||||
const static DWORD cs_dwMinimumRemainCount = 10000UL;
|
||||
|
||||
std::list<TItemIDRangeTable> m_listData;
|
||||
|
||||
public :
|
||||
CItemIDRangeManager();
|
||||
|
||||
void Build();
|
||||
bool BuildRange(DWORD dwMin, DWORD dwMax, TItemIDRangeTable& range);
|
||||
void UpdateRange(DWORD dwMin, DWORD dwMax);
|
||||
|
||||
TItemIDRangeTable GetRange();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,60 @@
|
|||
#include "stdafx.h"
|
||||
#include "Lock.h"
|
||||
|
||||
CLock::CLock()
|
||||
{
|
||||
}
|
||||
|
||||
CLock::~CLock()
|
||||
{
|
||||
}
|
||||
|
||||
void CLock::Initialize()
|
||||
{
|
||||
m_bLocked = false;
|
||||
#ifndef __WIN32__
|
||||
pthread_mutex_init(&m_lock, NULL);
|
||||
#else
|
||||
::InitializeCriticalSection(&m_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CLock::Destroy()
|
||||
{
|
||||
assert(!m_bLocked && "lock didn't released");
|
||||
#ifndef __WIN32__
|
||||
pthread_mutex_destroy(&m_lock);
|
||||
#else
|
||||
::DeleteCriticalSection(&m_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
int CLock::Trylock()
|
||||
{
|
||||
#ifndef __WIN32__
|
||||
return pthread_mutex_trylock(&m_lock);
|
||||
#else
|
||||
return ::TryEnterCriticalSection(&m_lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CLock::Lock()
|
||||
{
|
||||
#ifndef __WIN32__
|
||||
pthread_mutex_lock(&m_lock);
|
||||
#else
|
||||
::EnterCriticalSection(&m_lock);
|
||||
#endif
|
||||
m_bLocked = true;
|
||||
}
|
||||
|
||||
void CLock::Unlock()
|
||||
{
|
||||
assert(m_bLocked && "lock didn't issued");
|
||||
m_bLocked = false;
|
||||
#ifndef __WIN32__
|
||||
pthread_mutex_unlock(&m_lock);
|
||||
#else
|
||||
::LeaveCriticalSection(&m_lock);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_LOCK_H__
|
||||
#define __INC_LOCK_H__
|
||||
|
||||
#ifdef __WIN32__
|
||||
typedef CRITICAL_SECTION lock_t;
|
||||
#else
|
||||
typedef pthread_mutex_t lock_t;
|
||||
#endif
|
||||
|
||||
class CLock
|
||||
{
|
||||
public:
|
||||
CLock();
|
||||
~CLock();
|
||||
|
||||
void Initialize();
|
||||
void Destroy();
|
||||
int Trylock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
|
||||
private:
|
||||
lock_t m_lock;
|
||||
bool m_bLocked;
|
||||
};
|
||||
#endif
|
|
@ -0,0 +1,123 @@
|
|||
#include "stdafx.h"
|
||||
#include "LoginData.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
CLoginData::CLoginData()
|
||||
{
|
||||
m_dwKey = 0;
|
||||
memset(m_adwClientKey, 0, sizeof(m_adwClientKey));
|
||||
m_dwConnectedPeerHandle = 0;
|
||||
m_dwLogonTime = 0;
|
||||
memset(m_szIP, 0, sizeof(m_szIP));
|
||||
m_bPlay = false;
|
||||
m_bDeleted = false;
|
||||
m_bBillType = 0;
|
||||
m_dwBillID = 0;
|
||||
m_lastPlayTime = 0;
|
||||
m_dwLastPlayerID = 0;
|
||||
|
||||
memset(&m_data, 0, sizeof(TAccountTable));
|
||||
}
|
||||
|
||||
TAccountTable & CLoginData::GetAccountRef()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
void CLoginData::SetClientKey(const DWORD * c_pdwClientKey)
|
||||
{
|
||||
thecore_memcpy(&m_adwClientKey, c_pdwClientKey, sizeof(DWORD) * 4);
|
||||
}
|
||||
|
||||
const DWORD * CLoginData::GetClientKey()
|
||||
{
|
||||
return &m_adwClientKey[0];
|
||||
}
|
||||
|
||||
void CLoginData::SetKey(DWORD dwKey)
|
||||
{
|
||||
m_dwKey = dwKey;
|
||||
}
|
||||
|
||||
DWORD CLoginData::GetKey()
|
||||
{
|
||||
return m_dwKey;
|
||||
}
|
||||
|
||||
void CLoginData::SetConnectedPeerHandle(DWORD dwHandle)
|
||||
{
|
||||
m_dwConnectedPeerHandle = dwHandle;
|
||||
}
|
||||
|
||||
DWORD CLoginData::GetConnectedPeerHandle()
|
||||
{
|
||||
return m_dwConnectedPeerHandle;
|
||||
}
|
||||
|
||||
void CLoginData::SetLogonTime()
|
||||
{
|
||||
m_dwLogonTime = get_dword_time();
|
||||
}
|
||||
|
||||
DWORD CLoginData::GetLogonTime()
|
||||
{
|
||||
return m_dwLogonTime;
|
||||
}
|
||||
|
||||
void CLoginData::SetIP(const char * c_pszIP)
|
||||
{
|
||||
strlcpy(m_szIP, c_pszIP, sizeof(m_szIP));
|
||||
}
|
||||
|
||||
const char * CLoginData::GetIP()
|
||||
{
|
||||
return m_szIP;
|
||||
}
|
||||
|
||||
void CLoginData::SetPlay(bool bOn)
|
||||
{
|
||||
if (bOn)
|
||||
{
|
||||
sys_log(0, "SetPlay on %lu %s", GetKey(), m_data.login);
|
||||
SetLogonTime();
|
||||
}
|
||||
else
|
||||
sys_log(0, "SetPlay off %lu %s", GetKey(), m_data.login);
|
||||
|
||||
m_bPlay = bOn;
|
||||
m_lastPlayTime = CClientManager::instance().GetCurrentTime();
|
||||
}
|
||||
|
||||
bool CLoginData::IsPlay()
|
||||
{
|
||||
return m_bPlay;
|
||||
}
|
||||
|
||||
void CLoginData::SetDeleted(bool bSet)
|
||||
{
|
||||
m_bDeleted = bSet;
|
||||
}
|
||||
|
||||
bool CLoginData::IsDeleted()
|
||||
{
|
||||
return m_bDeleted;
|
||||
}
|
||||
|
||||
void CLoginData::SetPremium(int * paiPremiumTimes)
|
||||
{
|
||||
thecore_memcpy(m_aiPremiumTimes, paiPremiumTimes, sizeof(m_aiPremiumTimes));
|
||||
}
|
||||
|
||||
int CLoginData::GetPremium(BYTE type)
|
||||
{
|
||||
if (type >= PREMIUM_MAX_NUM)
|
||||
return 0;
|
||||
|
||||
return m_aiPremiumTimes[type];
|
||||
}
|
||||
|
||||
int * CLoginData::GetPremiumPtr()
|
||||
{
|
||||
return &m_aiPremiumTimes[0];
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// vim:ts=8 sw=4
|
||||
#ifndef __INC_METIN_II_DB_LOGINDATA_H__
|
||||
#define __INC_METIN_II_DB_LOGINDATA_H__
|
||||
|
||||
class CLoginData
|
||||
{
|
||||
public:
|
||||
CLoginData();
|
||||
|
||||
TAccountTable & GetAccountRef();
|
||||
void SetClientKey(const DWORD * c_pdwClientKey);
|
||||
|
||||
const DWORD * GetClientKey();
|
||||
void SetKey(DWORD dwKey);
|
||||
DWORD GetKey();
|
||||
|
||||
void SetConnectedPeerHandle(DWORD dwHandle);
|
||||
DWORD GetConnectedPeerHandle();
|
||||
|
||||
void SetLogonTime();
|
||||
DWORD GetLogonTime();
|
||||
|
||||
void SetIP(const char * c_pszIP);
|
||||
const char * GetIP();
|
||||
|
||||
void SetPlay(bool bOn);
|
||||
bool IsPlay();
|
||||
|
||||
void SetDeleted(bool bSet);
|
||||
bool IsDeleted();
|
||||
|
||||
void SetBillID(DWORD id) { m_dwBillID = id; }
|
||||
DWORD GetBillID() { return m_dwBillID; }
|
||||
|
||||
void SetBillType(BYTE type) { m_bBillType = type; }
|
||||
BYTE GetBillType() { return m_bBillType; }
|
||||
|
||||
time_t GetLastPlayTime() { return m_lastPlayTime; }
|
||||
|
||||
void SetPremium(int * paiPremiumTimes);
|
||||
int GetPremium(BYTE type);
|
||||
int * GetPremiumPtr();
|
||||
|
||||
DWORD GetLastPlayerID() const { return m_dwLastPlayerID; }
|
||||
void SetLastPlayerID(DWORD id) { m_dwLastPlayerID = id; }
|
||||
|
||||
private:
|
||||
DWORD m_dwKey;
|
||||
DWORD m_adwClientKey[4];
|
||||
DWORD m_dwConnectedPeerHandle;
|
||||
DWORD m_dwLogonTime;
|
||||
char m_szIP[MAX_HOST_LENGTH+1];
|
||||
bool m_bPlay;
|
||||
bool m_bDeleted;
|
||||
|
||||
BYTE m_bBillType;
|
||||
DWORD m_dwBillID;
|
||||
time_t m_lastPlayTime;
|
||||
int m_aiPremiumTimes[PREMIUM_MAX_NUM];
|
||||
|
||||
DWORD m_dwLastPlayerID;
|
||||
|
||||
TAccountTable m_data;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,430 @@
|
|||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
#include "Peer.h"
|
||||
#include "DBManager.h"
|
||||
#include "ClientManager.h"
|
||||
#include "GuildManager.h"
|
||||
#include "ItemAwardManager.h"
|
||||
#include "HB.h"
|
||||
#include "PrivManager.h"
|
||||
#include "MoneyLog.h"
|
||||
#include "Marriage.h"
|
||||
#include "Monarch.h"
|
||||
#include "BlockCountry.h"
|
||||
#include "ItemIDRangeManager.h"
|
||||
#ifdef __AUCTION__
|
||||
#include "AuctionManager.h"
|
||||
#endif
|
||||
#include <signal.h>
|
||||
|
||||
void SetPlayerDBName(const char* c_pszPlayerDBName);
|
||||
void SetTablePostfix(const char* c_pszTablePostfix);
|
||||
int Start();
|
||||
|
||||
std::string g_stTablePostfix;
|
||||
std::string g_stLocaleNameColumn = "name";
|
||||
std::string g_stLocale = "euckr";
|
||||
std::string g_stPlayerDBName = "";
|
||||
|
||||
|
||||
bool g_bHotBackup = false;
|
||||
BOOL g_test_server = false;
|
||||
|
||||
//단위 초
|
||||
int g_iPlayerCacheFlushSeconds = 60*7;
|
||||
int g_iItemCacheFlushSeconds = 60*5;
|
||||
|
||||
//g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다.
|
||||
int g_iLogoutSeconds = 60*10;
|
||||
|
||||
int g_log = 1;
|
||||
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
int g_iItemPriceListTableCacheFlushSeconds = 540;
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
extern const char * _malloc_options;
|
||||
#endif
|
||||
|
||||
extern void WriteVersion();
|
||||
|
||||
void emergency_sig(int sig)
|
||||
{
|
||||
if (sig == SIGSEGV)
|
||||
sys_log(0, "SIGNAL: SIGSEGV");
|
||||
else if (sig == SIGUSR1)
|
||||
sys_log(0, "SIGNAL: SIGUSR1");
|
||||
|
||||
if (sig == SIGSEGV)
|
||||
abort();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
WriteVersion();
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
_malloc_options = "A";
|
||||
#endif
|
||||
|
||||
CConfig Config;
|
||||
CNetPoller poller;
|
||||
CDBManager DBManager;
|
||||
CClientManager ClientManager;
|
||||
PlayerHB player_hb;
|
||||
CGuildManager GuildManager;
|
||||
CPrivManager PrivManager;
|
||||
CMoneyLog MoneyLog;
|
||||
ItemAwardManager ItemAwardManager;
|
||||
marriage::CManager MarriageManager;
|
||||
CMonarch Monarch;
|
||||
CBlockCountry BlockCountry;
|
||||
CItemIDRangeManager ItemIDRangeManager;
|
||||
#ifdef __AUCTION__
|
||||
AuctionManager auctionManager;
|
||||
#endif
|
||||
if (!Start())
|
||||
return 1;
|
||||
|
||||
GuildManager.Initialize();
|
||||
MarriageManager.Initialize();
|
||||
BlockCountry.Load();
|
||||
ItemIDRangeManager.Build();
|
||||
#ifdef __AUCTION__
|
||||
AuctionManager::instance().Initialize();
|
||||
#endif
|
||||
sys_log(0, "Metin2DBCacheServer Start\n");
|
||||
|
||||
CClientManager::instance().MainLoop();
|
||||
|
||||
signal_timer_disable();
|
||||
|
||||
DBManager.Quit();
|
||||
int iCount;
|
||||
|
||||
while (1)
|
||||
{
|
||||
iCount = 0;
|
||||
|
||||
iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER);
|
||||
iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER);
|
||||
|
||||
if (iCount == 0)
|
||||
break;
|
||||
|
||||
usleep(1000);
|
||||
sys_log(0, "WAITING_QUERY_COUNT %d", iCount);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void emptybeat(LPHEART heart, int pulse)
|
||||
{
|
||||
if (!(pulse % heart->passes_per_sec)) // 1초에 한번
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @version 05/06/13 Bang2ni - 아이템 가격정보 캐시 flush timeout 설정 추가.
|
||||
//
|
||||
int Start()
|
||||
{
|
||||
if (!CConfig::instance().LoadFile("conf.txt"))
|
||||
{
|
||||
fprintf(stderr, "Loading conf.txt failed.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CConfig::instance().GetValue("TEST_SERVER", &g_test_server))
|
||||
{
|
||||
fprintf(stderr, "Real Server\n");
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "Test Server\n");
|
||||
|
||||
if (!CConfig::instance().GetValue("LOG", &g_log))
|
||||
{
|
||||
fprintf(stderr, "Log Off");
|
||||
g_log= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_log = 1;
|
||||
fprintf(stderr, "Log On");
|
||||
}
|
||||
|
||||
|
||||
int tmpValue;
|
||||
|
||||
int heart_beat = 50;
|
||||
if (!CConfig::instance().GetValue("CLIENT_HEART_FPS", &heart_beat))
|
||||
{
|
||||
fprintf(stderr, "Cannot find CLIENT_HEART_FPS configuration.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_set_expiration_days(3);
|
||||
|
||||
if (CConfig::instance().GetValue("LOG_KEEP_DAYS", &tmpValue))
|
||||
{
|
||||
tmpValue = MINMAX(3, tmpValue, 30);
|
||||
log_set_expiration_days(tmpValue);
|
||||
fprintf(stderr, "Setting log keeping days to %d\n", tmpValue);
|
||||
}
|
||||
|
||||
thecore_init(heart_beat, emptybeat);
|
||||
signal_timer_enable(60);
|
||||
|
||||
char szBuf[256+1];
|
||||
|
||||
if (CConfig::instance().GetValue("LOCALE", szBuf, 256))
|
||||
{
|
||||
g_stLocale = szBuf;
|
||||
sys_log(0, "LOCALE set to %s", g_stLocale.c_str());
|
||||
|
||||
// CHINA_DISABLE_HOTBACKUP
|
||||
if ("gb2312" == g_stLocale)
|
||||
{
|
||||
sys_log(0, "CIBN_LOCALE: DISABLE_HOTBACKUP");
|
||||
g_bHotBackup = false;
|
||||
}
|
||||
// END_OF_CHINA_DISABLE_HOTBACKUP
|
||||
}
|
||||
|
||||
int iDisableHotBackup;
|
||||
if (CConfig::instance().GetValue("DISABLE_HOTBACKUP", &iDisableHotBackup))
|
||||
{
|
||||
if (iDisableHotBackup)
|
||||
{
|
||||
sys_log(0, "CONFIG: DISABLE_HOTBACKUP");
|
||||
g_bHotBackup = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!CConfig::instance().GetValue("TABLE_POSTFIX", szBuf, 256))
|
||||
{
|
||||
sys_err("TABLE_POSTFIX not configured use default");
|
||||
szBuf[0] = '\0';
|
||||
}
|
||||
|
||||
SetTablePostfix(szBuf);
|
||||
|
||||
if (CConfig::instance().GetValue("PLAYER_CACHE_FLUSH_SECONDS", szBuf, 256))
|
||||
{
|
||||
str_to_number(g_iPlayerCacheFlushSeconds, szBuf);
|
||||
sys_log(0, "PLAYER_CACHE_FLUSH_SECONDS: %d", g_iPlayerCacheFlushSeconds);
|
||||
}
|
||||
|
||||
if (CConfig::instance().GetValue("ITEM_CACHE_FLUSH_SECONDS", szBuf, 256))
|
||||
{
|
||||
str_to_number(g_iItemCacheFlushSeconds, szBuf);
|
||||
sys_log(0, "ITEM_CACHE_FLUSH_SECONDS: %d", g_iItemCacheFlushSeconds);
|
||||
}
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
if (CConfig::instance().GetValue("ITEM_PRICELIST_CACHE_FLUSH_SECONDS", szBuf, 256))
|
||||
{
|
||||
str_to_number(g_iItemPriceListTableCacheFlushSeconds, szBuf);
|
||||
sys_log(0, "ITEM_PRICELIST_CACHE_FLUSH_SECONDS: %d", g_iItemPriceListTableCacheFlushSeconds);
|
||||
}
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
//
|
||||
if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256))
|
||||
{
|
||||
DWORD dwVal = 0; str_to_number(dwVal, szBuf);
|
||||
CClientManager::instance().SetCacheFlushCountLimit(dwVal);
|
||||
}
|
||||
|
||||
int iIDStart;
|
||||
if (!CConfig::instance().GetValue("PLAYER_ID_START", &iIDStart))
|
||||
{
|
||||
sys_err("PLAYER_ID_START not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
CClientManager::instance().SetPlayerIDStart(iIDStart);
|
||||
|
||||
if (CConfig::instance().GetValue("NAME_COLUMN", szBuf, 256))
|
||||
{
|
||||
fprintf(stderr, "%s %s", g_stLocaleNameColumn.c_str(), szBuf);
|
||||
g_stLocaleNameColumn = szBuf;
|
||||
}
|
||||
|
||||
char szAddr[64], szDB[64], szUser[64], szPassword[64];
|
||||
int iPort;
|
||||
char line[256+1];
|
||||
|
||||
if (CConfig::instance().GetValue("SQL_PLAYER", line, 256))
|
||||
{
|
||||
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
|
||||
sys_log(0, "connecting to MySQL server (player)");
|
||||
|
||||
int iRetry = 5;
|
||||
|
||||
do
|
||||
{
|
||||
if (CDBManager::instance().Connect(SQL_PLAYER, szAddr, iPort, szDB, szUser, szPassword))
|
||||
{
|
||||
sys_log(0, " OK");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_log(0, " failed, retrying in 5 seconds");
|
||||
fprintf(stderr, " failed, retrying in 5 seconds");
|
||||
sleep(5);
|
||||
} while (iRetry--);
|
||||
fprintf(stderr, "Success PLAYER\n");
|
||||
SetPlayerDBName(szDB);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_err("SQL_PLAYER not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CConfig::instance().GetValue("SQL_ACCOUNT", line, 256))
|
||||
{
|
||||
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
|
||||
sys_log(0, "connecting to MySQL server (account)");
|
||||
|
||||
int iRetry = 5;
|
||||
|
||||
do
|
||||
{
|
||||
if (CDBManager::instance().Connect(SQL_ACCOUNT, szAddr, iPort, szDB, szUser, szPassword))
|
||||
{
|
||||
sys_log(0, " OK");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_log(0, " failed, retrying in 5 seconds");
|
||||
fprintf(stderr, " failed, retrying in 5 seconds");
|
||||
sleep(5);
|
||||
} while (iRetry--);
|
||||
fprintf(stderr, "Success ACCOUNT\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_err("SQL_ACCOUNT not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CConfig::instance().GetValue("SQL_COMMON", line, 256))
|
||||
{
|
||||
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
|
||||
sys_log(0, "connecting to MySQL server (common)");
|
||||
|
||||
int iRetry = 5;
|
||||
|
||||
do
|
||||
{
|
||||
if (CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword))
|
||||
{
|
||||
sys_log(0, " OK");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_log(0, " failed, retrying in 5 seconds");
|
||||
fprintf(stderr, " failed, retrying in 5 seconds");
|
||||
sleep(5);
|
||||
} while (iRetry--);
|
||||
fprintf(stderr, "Success COMMON\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_err("SQL_COMMON not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CConfig::instance().GetValue("SQL_HOTBACKUP", line, 256))
|
||||
{
|
||||
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
|
||||
sys_log(0, "connecting to MySQL server (hotbackup)");
|
||||
|
||||
int iRetry = 5;
|
||||
|
||||
do
|
||||
{
|
||||
if (CDBManager::instance().Connect(SQL_HOTBACKUP, szAddr, iPort, szDB, szUser, szPassword))
|
||||
{
|
||||
sys_log(0, " OK");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_log(0, " failed, retrying in 5 seconds");
|
||||
fprintf(stderr, " failed, retrying in 5 seconds");
|
||||
sleep(5);
|
||||
}
|
||||
while (iRetry--);
|
||||
|
||||
fprintf(stderr, "Success HOTBACKUP\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_err("SQL_HOTBACKUP not configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CNetPoller::instance().Create())
|
||||
{
|
||||
sys_err("Cannot create network poller");
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_log(0, "ClientManager initialization.. ");
|
||||
|
||||
if (!CClientManager::instance().Initialize())
|
||||
{
|
||||
sys_log(0, " failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_log(0, " OK");
|
||||
|
||||
if (!PlayerHB::instance().Initialize())
|
||||
{
|
||||
sys_err("cannot initialize player hotbackup");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __WIN32__
|
||||
signal(SIGUSR1, emergency_sig);
|
||||
#endif
|
||||
signal(SIGSEGV, emergency_sig);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SetTablePostfix(const char* c_pszTablePostfix)
|
||||
{
|
||||
if (!c_pszTablePostfix || !*c_pszTablePostfix)
|
||||
g_stTablePostfix = "";
|
||||
else
|
||||
g_stTablePostfix = c_pszTablePostfix;
|
||||
}
|
||||
|
||||
const char * GetTablePostfix()
|
||||
{
|
||||
return g_stTablePostfix.c_str();
|
||||
}
|
||||
|
||||
void SetPlayerDBName(const char* c_pszPlayerDBName)
|
||||
{
|
||||
if (! c_pszPlayerDBName || ! *c_pszPlayerDBName)
|
||||
g_stPlayerDBName = "";
|
||||
else
|
||||
{
|
||||
g_stPlayerDBName = c_pszPlayerDBName;
|
||||
g_stPlayerDBName += ".";
|
||||
}
|
||||
}
|
||||
|
||||
const char * GetPlayerDBName()
|
||||
{
|
||||
return g_stPlayerDBName.c_str();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __INC_MAIN_H__
|
||||
#define __INC_MAIN_H__
|
||||
|
||||
int Start();
|
||||
void End();
|
||||
const char * GetTablePostfix();
|
||||
const char * GetPlayerDBName();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,382 @@
|
|||
#include "stdafx.h"
|
||||
#include "Marriage.h"
|
||||
#include "Main.h"
|
||||
#include "DBManager.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
namespace marriage
|
||||
{
|
||||
const DWORD WEDDING_LENGTH = 60 * 60; // sec
|
||||
bool operator < (const TWedding& lhs, const TWedding& rhs)
|
||||
{
|
||||
return lhs.dwTime < rhs.dwTime;
|
||||
}
|
||||
|
||||
bool operator > (const TWedding& lhs, const TWedding& rhs)
|
||||
{
|
||||
return lhs.dwTime > rhs.dwTime;
|
||||
}
|
||||
|
||||
bool operator > (const TWeddingInfo &lhs, const TWeddingInfo& rhs)
|
||||
{
|
||||
return lhs.dwTime > rhs.dwTime;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
CManager::CManager()
|
||||
{
|
||||
}
|
||||
|
||||
CManager::~CManager()
|
||||
{
|
||||
}
|
||||
|
||||
bool CManager::Initialize()
|
||||
{
|
||||
char szQuery[1024];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT pid1, pid2, love_point, time, is_married, p1.name, p2.name FROM marriage, player%s as p1, player%s as p2 WHERE p1.id = pid1 AND p2.id = pid2",
|
||||
GetTablePostfix(), GetTablePostfix());
|
||||
|
||||
auto_ptr<SQLMsg> pmsg_delete(CDBManager::instance().DirectQuery("DELETE FROM marriage WHERE is_married = 0"));
|
||||
auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult * pRes = pmsg->Get();
|
||||
sys_log(0, "MarriageList(size=%lu)", pRes->uiNumRows);
|
||||
|
||||
if (pRes->uiNumRows > 0)
|
||||
{
|
||||
for (uint uiRow = 0; uiRow != pRes->uiNumRows; ++uiRow)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult);
|
||||
|
||||
DWORD pid1 = 0; str_to_number(pid1, row[0]);
|
||||
DWORD pid2 = 0; str_to_number(pid2, row[1]);
|
||||
int love_point = 0; str_to_number(love_point, row[2]);
|
||||
DWORD time = 0; str_to_number(time, row[3]);
|
||||
BYTE is_married = 0; str_to_number(is_married, row[4]);
|
||||
const char* name1 = row[5];
|
||||
const char* name2 = row[6];
|
||||
|
||||
TMarriage* pMarriage = new TMarriage(pid1, pid2, love_point, time, is_married, name1, name2);
|
||||
m_Marriages.insert(pMarriage);
|
||||
m_MarriageByPID.insert(make_pair(pid1, pMarriage));
|
||||
m_MarriageByPID.insert(make_pair(pid2, pMarriage));
|
||||
|
||||
sys_log(0, "Marriage %lu: LP:%d TM:%u ST:%d %10lu:%16s %10lu:%16s ", uiRow, love_point, time, is_married, pid1, name1, pid2, name2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TMarriage* CManager::Get(DWORD dwPlayerID)
|
||||
{
|
||||
itertype(m_MarriageByPID) it = m_MarriageByPID.find(dwPlayerID);
|
||||
|
||||
if (it != m_MarriageByPID.end())
|
||||
return it->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Align(DWORD& rPID1, DWORD& rPID2)
|
||||
{
|
||||
if (rPID1 > rPID2)
|
||||
std::swap(rPID1, rPID2);
|
||||
}
|
||||
|
||||
void CManager::Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2)
|
||||
{
|
||||
DWORD now = CClientManager::instance().GetCurrentTime();
|
||||
if (IsMarried(dwPID1) || IsMarried(dwPID2))
|
||||
{
|
||||
sys_err("cannot marry already married character. %d - %d", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
Align(dwPID1, dwPID2);
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "INSERT INTO marriage(pid1, pid2, love_point, time) VALUES (%u, %u, 0, %u)", dwPID1, dwPID2, now);
|
||||
|
||||
auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult* res = pmsg->Get();
|
||||
if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1)
|
||||
{
|
||||
sys_err("cannot insert marriage");
|
||||
return;
|
||||
}
|
||||
|
||||
sys_log(0, "MARRIAGE ADD %u %u", dwPID1, dwPID2);
|
||||
|
||||
TMarriage* pMarriage = new TMarriage(dwPID1, dwPID2, 0, now, 0, szName1, szName2);
|
||||
m_Marriages.insert(pMarriage);
|
||||
m_MarriageByPID.insert(make_pair(dwPID1, pMarriage));
|
||||
m_MarriageByPID.insert(make_pair(dwPID2, pMarriage));
|
||||
|
||||
TPacketMarriageAdd p;
|
||||
p.dwPID1 = dwPID1;
|
||||
p.dwPID2 = dwPID2;
|
||||
p.tMarryTime = now;
|
||||
strlcpy(p.szName1, szName1, sizeof(p.szName1));
|
||||
strlcpy(p.szName2, szName2, sizeof(p.szName2));
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_ADD, &p, sizeof(p));
|
||||
}
|
||||
|
||||
void CManager::Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried)
|
||||
{
|
||||
TMarriage* pMarriage = Get(dwPID1);
|
||||
if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2)
|
||||
{
|
||||
sys_err("not under marriage : %u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
Align(dwPID1, dwPID2);
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET love_point = %d, is_married = %d WHERE pid1 = %u AND pid2 = %u",
|
||||
iLovePoint, byMarried, pMarriage->pid1, pMarriage->pid2);
|
||||
|
||||
auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult* res = pmsg->Get();
|
||||
if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1)
|
||||
{
|
||||
sys_err("cannot update marriage : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
sys_log(0, "MARRIAGE UPDATE PID:%u %u LP:%u ST:%d", dwPID1, dwPID2, iLovePoint, byMarried);
|
||||
pMarriage->love_point = iLovePoint;
|
||||
pMarriage->is_married = byMarried;
|
||||
|
||||
TPacketMarriageUpdate p;
|
||||
p.dwPID1 = dwPID1;
|
||||
p.dwPID2 = dwPID2;
|
||||
p.iLovePoint = pMarriage->love_point;
|
||||
p.byMarried = pMarriage->is_married;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p));
|
||||
}
|
||||
|
||||
void CManager::Remove(DWORD dwPID1, DWORD dwPID2)
|
||||
{
|
||||
TMarriage* pMarriage = Get(dwPID1);
|
||||
|
||||
if (pMarriage)
|
||||
{
|
||||
sys_log(0, "Break Marriage pid1 %d pid2 %d Other %d", dwPID1, dwPID2, pMarriage->GetOther(dwPID1));
|
||||
}
|
||||
if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2)
|
||||
{
|
||||
itertype(m_MarriageByPID) it = m_MarriageByPID.begin();
|
||||
|
||||
for (; it != m_MarriageByPID.end(); ++it)
|
||||
{
|
||||
sys_log(0, "Marriage List pid1 %d pid2 %d", it->second->pid1, it->second->pid2);
|
||||
}
|
||||
sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
Align(dwPID1, dwPID2);
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE FROM marriage WHERE pid1 = %u AND pid2 = %u", dwPID1, dwPID2);
|
||||
|
||||
auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult* res = pmsg->Get();
|
||||
if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1)
|
||||
{
|
||||
sys_err("cannot delete marriage : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
sys_log(0, "MARRIAGE REMOVE PID:%u %u", dwPID1, dwPID2);
|
||||
|
||||
m_Marriages.erase(pMarriage);
|
||||
m_MarriageByPID.erase(dwPID1);
|
||||
m_MarriageByPID.erase(dwPID2);
|
||||
|
||||
TPacketMarriageRemove p;
|
||||
p.dwPID1 = dwPID1;
|
||||
p.dwPID2 = dwPID2;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_REMOVE, &p, sizeof(p));
|
||||
|
||||
delete pMarriage;
|
||||
}
|
||||
|
||||
void CManager::EngageToMarriage(DWORD dwPID1, DWORD dwPID2)
|
||||
{
|
||||
TMarriage* pMarriage = Get(dwPID1);
|
||||
if (!pMarriage || pMarriage->GetOther(dwPID1) != dwPID2)
|
||||
{
|
||||
sys_err("not under marriage : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pMarriage->is_married)
|
||||
{
|
||||
sys_err("already married, cannot change engage to marry : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
Align(dwPID1, dwPID2);
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE marriage SET is_married = 1 WHERE pid1 = %u AND pid2 = %u",
|
||||
pMarriage->pid1, pMarriage->pid2);
|
||||
|
||||
auto_ptr<SQLMsg> pmsg(CDBManager::instance().DirectQuery(szQuery));
|
||||
|
||||
SQLResult* res = pmsg->Get();
|
||||
if (res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1)
|
||||
{
|
||||
sys_err("cannot change engage to marriage : PID:%u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
sys_log(0, "MARRIAGE ENGAGE->MARRIAGE PID:%u %u", dwPID1, dwPID2);
|
||||
pMarriage->is_married = 1;
|
||||
|
||||
TPacketMarriageUpdate p;
|
||||
p.dwPID1 = dwPID1;
|
||||
p.dwPID2 = dwPID2;
|
||||
p.iLovePoint = pMarriage->love_point;
|
||||
p.byMarried = pMarriage->is_married;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_MARRIAGE_UPDATE, &p, sizeof(p));
|
||||
}
|
||||
|
||||
void CManager::OnSetup(CPeer* peer)
|
||||
{
|
||||
// 결혼한 사람들 보내기
|
||||
for (itertype(m_Marriages) it = m_Marriages.begin(); it != m_Marriages.end(); ++it)
|
||||
{
|
||||
TMarriage* pMarriage = *it;
|
||||
|
||||
{
|
||||
TPacketMarriageAdd p;
|
||||
p.dwPID1 = pMarriage->pid1;
|
||||
p.dwPID2 = pMarriage->pid2;
|
||||
p.tMarryTime = pMarriage->time;
|
||||
strlcpy(p.szName1, pMarriage->name1.c_str(), sizeof(p.szName1));
|
||||
strlcpy(p.szName2, pMarriage->name2.c_str(), sizeof(p.szName2));
|
||||
peer->EncodeHeader(HEADER_DG_MARRIAGE_ADD, 0, sizeof(p));
|
||||
peer->Encode(&p, sizeof(p));
|
||||
}
|
||||
|
||||
{
|
||||
TPacketMarriageUpdate p;
|
||||
p.dwPID1 = pMarriage->pid1;
|
||||
p.dwPID2 = pMarriage->pid2;
|
||||
p.iLovePoint = pMarriage->love_point;
|
||||
p.byMarried = pMarriage->is_married;
|
||||
peer->EncodeHeader(HEADER_DG_MARRIAGE_UPDATE, 0, sizeof(p));
|
||||
peer->Encode(&p, sizeof(p));
|
||||
}
|
||||
}
|
||||
|
||||
// 결혼식 보내기
|
||||
for (itertype(m_mapRunningWedding) it = m_mapRunningWedding.begin(); it != m_mapRunningWedding.end(); ++it)
|
||||
{
|
||||
const TWedding& t = it->second;
|
||||
|
||||
TPacketWeddingReady p;
|
||||
p.dwPID1 = t.dwPID1;
|
||||
p.dwPID2 = t.dwPID2;
|
||||
p.dwMapIndex = t.dwMapIndex;
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_WEDDING_READY, 0, sizeof(p));
|
||||
peer->Encode(&p, sizeof(p));
|
||||
|
||||
TPacketWeddingStart p2;
|
||||
p2.dwPID1 = t.dwPID1;
|
||||
p2.dwPID2 = t.dwPID2;
|
||||
|
||||
peer->EncodeHeader(HEADER_DG_WEDDING_START, 0, sizeof(p2));
|
||||
peer->Encode(&p2, sizeof(p2));
|
||||
}
|
||||
}
|
||||
|
||||
void CManager::ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2)
|
||||
{
|
||||
DWORD dwStartTime = CClientManager::instance().GetCurrentTime();
|
||||
m_pqWeddingStart.push(TWedding(dwStartTime + 5, dwMapIndex, dwPID1, dwPID2));
|
||||
}
|
||||
|
||||
void CManager::EndWedding(DWORD dwPID1, DWORD dwPID2)
|
||||
{
|
||||
itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(dwPID1, dwPID2));
|
||||
if (it == m_mapRunningWedding.end())
|
||||
{
|
||||
sys_err("try to end wedding %u %u", dwPID1, dwPID2);
|
||||
return;
|
||||
}
|
||||
|
||||
TWedding& w = it->second;
|
||||
|
||||
TPacketWeddingEnd p;
|
||||
p.dwPID1 = w.dwPID1;
|
||||
p.dwPID2 = w.dwPID2;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p));
|
||||
m_mapRunningWedding.erase(it);
|
||||
}
|
||||
|
||||
void CManager::Update()
|
||||
{
|
||||
DWORD now = CClientManager::instance().GetCurrentTime();
|
||||
|
||||
if (!m_pqWeddingEnd.empty())
|
||||
{
|
||||
while (!m_pqWeddingEnd.empty() && m_pqWeddingEnd.top().dwTime <= now)
|
||||
{
|
||||
TWeddingInfo wi = m_pqWeddingEnd.top();
|
||||
m_pqWeddingEnd.pop();
|
||||
|
||||
itertype(m_mapRunningWedding) it = m_mapRunningWedding.find(make_pair(wi.dwPID1, wi.dwPID2));
|
||||
if (it == m_mapRunningWedding.end())
|
||||
continue;
|
||||
|
||||
TWedding& w = it->second;
|
||||
|
||||
TPacketWeddingEnd p;
|
||||
p.dwPID1 = w.dwPID1;
|
||||
p.dwPID2 = w.dwPID2;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_END, &p, sizeof(p));
|
||||
m_mapRunningWedding.erase(it);
|
||||
|
||||
itertype(m_MarriageByPID) it_marriage = m_MarriageByPID.find(w.dwPID1);
|
||||
|
||||
if (it_marriage != m_MarriageByPID.end())
|
||||
{
|
||||
TMarriage* pMarriage = it_marriage->second;
|
||||
if (!pMarriage->is_married)
|
||||
{
|
||||
Remove(pMarriage->pid1, pMarriage->pid2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!m_pqWeddingStart.empty())
|
||||
{
|
||||
while (!m_pqWeddingStart.empty() && m_pqWeddingStart.top().dwTime <= now)
|
||||
{
|
||||
TWedding w = m_pqWeddingStart.top();
|
||||
m_pqWeddingStart.pop();
|
||||
|
||||
TPacketWeddingStart p;
|
||||
p.dwPID1 = w.dwPID1;
|
||||
p.dwPID2 = w.dwPID2;
|
||||
CClientManager::instance().ForwardPacket(HEADER_DG_WEDDING_START, &p, sizeof(p));
|
||||
|
||||
w.dwTime += WEDDING_LENGTH;
|
||||
m_pqWeddingEnd.push(TWeddingInfo(w.dwTime, w.dwPID1, w.dwPID2));
|
||||
m_mapRunningWedding.insert(make_pair(make_pair(w.dwPID1, w.dwPID2), w));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
// vim: ts=4 sw=4
|
||||
#ifndef __MARRIAGE_H
|
||||
#define __MARRIAGE_H
|
||||
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
|
||||
#include "Peer.h"
|
||||
|
||||
namespace marriage
|
||||
{
|
||||
struct TWeddingInfo
|
||||
{
|
||||
DWORD dwTime;
|
||||
DWORD dwPID1;
|
||||
DWORD dwPID2;
|
||||
TWeddingInfo(DWORD time, DWORD pid1, DWORD pid2)
|
||||
: dwTime(time),
|
||||
dwPID1(pid1),
|
||||
dwPID2(pid2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct TWedding
|
||||
{
|
||||
DWORD dwTime;
|
||||
DWORD dwMapIndex;
|
||||
DWORD dwPID1;
|
||||
DWORD dwPID2;
|
||||
|
||||
TWedding(DWORD time, DWORD dwMapIndex, DWORD pid1, DWORD pid2)
|
||||
: dwTime(time),
|
||||
dwMapIndex(dwMapIndex),
|
||||
dwPID1(pid1),
|
||||
dwPID2(pid2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern bool operator < (const TWedding& lhs, const TWedding& rhs);
|
||||
extern bool operator > (const TWedding& lhs, const TWedding& rhs);
|
||||
extern bool operator > (const TWeddingInfo& lhs, const TWeddingInfo& rhs);
|
||||
|
||||
struct TMarriage
|
||||
{
|
||||
DWORD pid1;
|
||||
DWORD pid2;
|
||||
int love_point;
|
||||
DWORD time;
|
||||
BYTE is_married; // false : ¾àÈ¥ »óÅÂ, true : °áÈ¥ »óÅÂ
|
||||
std::string name1;
|
||||
std::string name2;
|
||||
|
||||
TMarriage(DWORD _pid1, DWORD _pid2, int _love_point, DWORD _time, BYTE _is_married, const char* name1, const char* name2)
|
||||
: pid1(_pid1), pid2(_pid2), love_point(_love_point), time(_time), is_married(_is_married), name1(name1), name2(name2)
|
||||
{
|
||||
}
|
||||
|
||||
DWORD GetOther(DWORD PID)
|
||||
{
|
||||
if (pid1 == PID)
|
||||
return pid2;
|
||||
|
||||
if (pid2 == PID)
|
||||
return pid1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class CManager : public singleton<CManager>
|
||||
{
|
||||
public:
|
||||
CManager();
|
||||
virtual ~CManager();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
TMarriage* Get(DWORD dwPlayerID);
|
||||
bool IsMarried(DWORD dwPlayerID)
|
||||
{
|
||||
return Get(dwPlayerID) != NULL;
|
||||
}
|
||||
|
||||
//void Reserve(DWORD dwPID1, DWORD dwPID2);
|
||||
void Add(DWORD dwPID1, DWORD dwPID2, const char* szName1, const char* szName2);
|
||||
void Remove(DWORD dwPID1, DWORD dwPID2);
|
||||
void Update(DWORD dwPID1, DWORD dwPID2, INT iLovePoint, BYTE byMarried);
|
||||
|
||||
void EngageToMarriage(DWORD dwPID1, DWORD dwPID2);
|
||||
|
||||
void ReadyWedding(DWORD dwMapIndex, DWORD dwPID1, DWORD dwPID2);
|
||||
void EndWedding(DWORD dwPID1, DWORD dwPID2);
|
||||
|
||||
void OnSetup(CPeer* peer);
|
||||
|
||||
void Update();
|
||||
|
||||
private:
|
||||
std::set<TMarriage *> m_Marriages;
|
||||
std::map<DWORD, TMarriage *> m_MarriageByPID;
|
||||
|
||||
std::priority_queue<TWedding, std::vector<TWedding>, std::greater<TWedding> > m_pqWeddingStart;
|
||||
|
||||
std::priority_queue<TWeddingInfo, std::vector<TWeddingInfo>, std::greater<TWeddingInfo> > m_pqWeddingEnd;
|
||||
|
||||
std::map<std::pair<DWORD, DWORD>, TWedding> m_mapRunningWedding;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,309 @@
|
|||
#include "Monarch.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "Main.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
extern int g_test_server;
|
||||
|
||||
CMonarch::CMonarch()
|
||||
{
|
||||
memset(&m_MonarchInfo, 0, sizeof(MonarchInfo));
|
||||
}
|
||||
|
||||
CMonarch::~CMonarch()
|
||||
{
|
||||
}
|
||||
|
||||
bool CMonarch::VoteMonarch(DWORD pid, DWORD selectdpid)
|
||||
{
|
||||
MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid);
|
||||
|
||||
if (it == m_map_MonarchElection.end())
|
||||
{
|
||||
MonarchElectionInfo * p = new MonarchElectionInfo;
|
||||
p->pid = pid;
|
||||
p->selectedpid= selectdpid;
|
||||
m_map_MonarchElection.insert(MAP_MONARCHELECTION::value_type(pid, p));
|
||||
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"INSERT INTO monarch_election(pid, selectedpid, electiondata) VALUES(%d, %d, now())", pid, selectdpid);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CMonarch::ElectMonarch()
|
||||
{
|
||||
int size = GetVecMonarchCandidacy().size();
|
||||
|
||||
int * s = new int[size];
|
||||
|
||||
itertype(m_map_MonarchElection) it = m_map_MonarchElection.begin();
|
||||
|
||||
int idx = 0;
|
||||
|
||||
for (; it != m_map_MonarchElection.end(); ++it)
|
||||
{
|
||||
if ((idx = GetCandidacyIndex(it->second->pid)) < 0)
|
||||
continue;
|
||||
|
||||
++s[idx];
|
||||
|
||||
if (g_test_server)
|
||||
sys_log (0, "[MONARCH_VOTE] pid(%d) come to vote candidacy pid(%d)", it->second->pid, m_vec_MonarchCandidacy[idx].pid);
|
||||
}
|
||||
|
||||
delete [] s;
|
||||
}
|
||||
|
||||
bool CMonarch::IsCandidacy(DWORD pid)
|
||||
{
|
||||
VEC_MONARCHCANDIDACY::iterator it = m_vec_MonarchCandidacy.begin();
|
||||
|
||||
for (; it != m_vec_MonarchCandidacy.end(); ++it)
|
||||
{
|
||||
if (it->pid == pid)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::AddCandidacy(DWORD pid, const char * name)
|
||||
{
|
||||
if (IsCandidacy(pid) == false)
|
||||
return false;
|
||||
|
||||
MonarchCandidacy info;
|
||||
|
||||
info.pid = pid;
|
||||
strlcpy(info.name, name, sizeof(info.name));
|
||||
m_vec_MonarchCandidacy.push_back(info);
|
||||
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"INSERT INTO monarch_candidacy(pid, date) VALUES(%d, now())", pid);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::DelCandidacy(const char * name)
|
||||
{
|
||||
itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin();
|
||||
for (; it != m_vec_MonarchCandidacy.end(); ++it)
|
||||
{
|
||||
if (0 == strncmp(it->name, name, sizeof(it->name)))
|
||||
{
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"DELETE FROM monarch_candidacy WHERE pid=%d ", it->pid);
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
m_vec_MonarchCandidacy.erase (it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool CMonarch::IsMonarch(int Empire, DWORD pid)
|
||||
{
|
||||
if (m_MonarchInfo.pid[Empire] != pid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::AddMoney(int Empire, int64_t Money)
|
||||
{
|
||||
if (m_MonarchInfo.money[Empire] + Money > 2000000000)
|
||||
return true;
|
||||
|
||||
m_MonarchInfo.money[Empire] += Money;
|
||||
|
||||
int64_t Money64 = m_MonarchInfo.money[Empire];
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%lld where empire=%d", Money64, Empire);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::DecMoney(int Empire, int64_t Money)
|
||||
{
|
||||
if (m_MonarchInfo.money[Empire] - Money < 0)
|
||||
return false;
|
||||
m_MonarchInfo.money[Empire] -= Money;
|
||||
|
||||
int64_t Money64 = m_MonarchInfo.money[Empire];
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%lld where empire=%d", Money64, Empire);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::TakeMoney(int Empire, DWORD pid, int64_t Money)
|
||||
{
|
||||
if (IsMonarch(Empire, pid) == false)
|
||||
return false;
|
||||
|
||||
if (m_MonarchInfo.money[Empire] < Money)
|
||||
return false;
|
||||
|
||||
m_MonarchInfo.money[Empire] -= Money;
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"UPDATE monarch set money=%lld; where empire=%d", m_MonarchInfo.money[Empire], Empire);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery);
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "[MONARCH] Take money empire(%d) money(%lld)", Empire, m_MonarchInfo.money[Empire]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::LoadMonarch()
|
||||
{
|
||||
MonarchInfo * p = &m_MonarchInfo;
|
||||
char szQuery[256];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name, money, windate FROM monarch a, player%s b where a.pid=b.id", GetTablePostfix());
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
delete pMsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n)
|
||||
{
|
||||
int idx = 0;
|
||||
int Empire = 0; str_to_number(Empire, row[idx++]);
|
||||
|
||||
str_to_number(p->pid[Empire], row[idx++]);
|
||||
strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire]));
|
||||
|
||||
str_to_number(p->money[Empire], row[idx++]);
|
||||
strlcpy(p->date[Empire], row[idx++], sizeof(p->date[Empire]));
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "[LOAD_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]);
|
||||
}
|
||||
|
||||
delete pMsg;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::SetMonarch(const char * name)
|
||||
{
|
||||
MonarchInfo * p = &m_MonarchInfo;
|
||||
char szQuery[256];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name FROM player a where a.name = '%s'", name);
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
delete pMsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
MYSQL_ROW row;
|
||||
int Empire = 0;
|
||||
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n)
|
||||
{
|
||||
int idx = 0;
|
||||
str_to_number(Empire, row[idx++]);
|
||||
|
||||
str_to_number(p->pid[Empire], row[idx++]);
|
||||
strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire]));
|
||||
p->money[Empire] = atoll(row[idx++]);
|
||||
|
||||
if (g_test_server)
|
||||
sys_log(0, "[Set_MONARCH] Empire %d pid %d money %lld windate %s", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]);
|
||||
}
|
||||
delete pMsg;
|
||||
|
||||
//db¿¡ ÀÔ·Â
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"REPLACE INTO monarch (empire, name, windate, money) VALUES(%d, %d, now(), %lld)", Empire, p->pid[Empire], p->money[Empire]);
|
||||
|
||||
CDBManager::instance().AsyncQuery(szQuery, SQL_PLAYER);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::DelMonarch(int Empire)
|
||||
{
|
||||
char szQuery[256];
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where empire=%d", Empire);
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
delete pMsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete pMsg;
|
||||
|
||||
memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire]));
|
||||
m_MonarchInfo.money[Empire] = 0;
|
||||
m_MonarchInfo.pid[Empire] = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CMonarch::DelMonarch(const char * name)
|
||||
{
|
||||
for (int n = 1; n < 4; ++n)
|
||||
{
|
||||
if (0 == strncmp(m_MonarchInfo.name[n], name, sizeof(m_MonarchInfo.name[n])))
|
||||
{
|
||||
char szQuery[256];
|
||||
|
||||
int Empire = n;
|
||||
snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where name=%d", Empire);
|
||||
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
sys_err(" DirectQuery failed(%s)", szQuery);
|
||||
delete pMsg;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete pMsg;
|
||||
|
||||
memset(m_MonarchInfo.name[Empire], 0, 32);
|
||||
m_MonarchInfo.money[Empire] = 0;
|
||||
m_MonarchInfo.pid[Empire] = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CMonarch::GetCandidacyIndex(DWORD pid)
|
||||
{
|
||||
itertype(m_vec_MonarchCandidacy) it = m_vec_MonarchCandidacy.begin();
|
||||
|
||||
for (int n = 0; it != m_vec_MonarchCandidacy.end(); ++it, ++n)
|
||||
{
|
||||
if (it->pid == pid)
|
||||
return n;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
// vim: ts=4 sw=4
|
||||
#ifndef METIN2_MONARCH_H
|
||||
#define METIN2_MONARCH_H
|
||||
|
||||
#include "../../libthecore/include/stdafx.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "../../common/singleton.h"
|
||||
#include "../../common/tables.h"
|
||||
|
||||
class CMonarch : public singleton<CMonarch>
|
||||
{
|
||||
public:
|
||||
typedef std::map<DWORD, MonarchElectionInfo*> MAP_MONARCHELECTION;
|
||||
typedef std::vector<MonarchCandidacy> VEC_MONARCHCANDIDACY;
|
||||
|
||||
CMonarch();
|
||||
virtual ~CMonarch();
|
||||
|
||||
bool VoteMonarch(DWORD pid, DWORD selectedpid);
|
||||
void ElectMonarch();
|
||||
|
||||
bool IsCandidacy(DWORD pid);
|
||||
bool AddCandidacy(DWORD pid, const char * name);
|
||||
bool DelCandidacy(const char * name);
|
||||
|
||||
bool LoadMonarch();
|
||||
bool SetMonarch(const char * name);
|
||||
bool DelMonarch(int Empire);
|
||||
bool DelMonarch(const char * name);
|
||||
|
||||
bool IsMonarch(int Empire, DWORD pid);
|
||||
bool AddMoney(int Empire, int64_t Money);
|
||||
bool DecMoney(int Empire, int64_t Money);
|
||||
bool TakeMoney(int Empire, DWORD pid, int64_t Money);
|
||||
|
||||
TMonarchInfo* GetMonarch()
|
||||
{
|
||||
return &m_MonarchInfo;
|
||||
}
|
||||
|
||||
VEC_MONARCHCANDIDACY& GetVecMonarchCandidacy()
|
||||
{
|
||||
return m_vec_MonarchCandidacy;
|
||||
}
|
||||
|
||||
size_t MonarchCandidacySize()
|
||||
{
|
||||
return m_vec_MonarchCandidacy.size();
|
||||
}
|
||||
|
||||
private:
|
||||
int GetCandidacyIndex(DWORD pid);
|
||||
|
||||
MAP_MONARCHELECTION m_map_MonarchElection;
|
||||
VEC_MONARCHCANDIDACY m_vec_MonarchCandidacy;
|
||||
TMonarchInfo m_MonarchInfo;
|
||||
|
||||
MonarchElectionInfo* GetMonarchElection(DWORD pid)
|
||||
{
|
||||
MAP_MONARCHELECTION::iterator it = m_map_MonarchElection.find(pid);
|
||||
|
||||
if (it != m_map_MonarchElection.end())
|
||||
return it->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,57 @@
|
|||
#include "stdafx.h"
|
||||
#include "MoneyLog.h"
|
||||
#include "ClientManager.h"
|
||||
#include "Peer.h"
|
||||
|
||||
CMoneyLog::CMoneyLog()
|
||||
{
|
||||
}
|
||||
|
||||
CMoneyLog::~CMoneyLog()
|
||||
{
|
||||
}
|
||||
|
||||
void CMoneyLog::Save()
|
||||
{
|
||||
CPeer* peer = CClientManager::instance().GetAnyPeer();
|
||||
if (!peer)
|
||||
return;
|
||||
for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType ++)
|
||||
{
|
||||
typeof(m_MoneyLogContainer[bType].begin()) it;
|
||||
for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it)
|
||||
{
|
||||
//bType;
|
||||
TPacketMoneyLog p;
|
||||
p.type = bType;
|
||||
p.vnum = it->first;
|
||||
p.gold = it->second;
|
||||
peer->EncodeHeader(HEADER_DG_MONEY_LOG, 0, sizeof(p));
|
||||
peer->Encode(&p, sizeof(p));
|
||||
}
|
||||
m_MoneyLogContainer[bType].clear();
|
||||
}
|
||||
/*
|
||||
CPeer* peer = GetPeer();
|
||||
|
||||
peer->
|
||||
|
||||
for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++)
|
||||
{
|
||||
//"INSERT INTO money_log%s VALUES('%s', %d, %d, %d)", CClientManager::instance().GetTablePostfix(),
|
||||
typeof(m_MoneyLogContainer[bType].begin()) it;
|
||||
for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it)
|
||||
{
|
||||
typeof(it->second.begin())
|
||||
}
|
||||
}
|
||||
|
||||
for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++)
|
||||
m_MoneyLogContainer[bType].clear()
|
||||
*/
|
||||
}
|
||||
|
||||
void CMoneyLog::AddLog(BYTE bType, DWORD dwVnum, int iGold)
|
||||
{
|
||||
m_MoneyLogContainer[bType][dwVnum] += iGold;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_MONEY_LOG
|
||||
#define __INC_MONEY_LOG
|
||||
|
||||
#include <map>
|
||||
|
||||
class CMoneyLog : public singleton<CMoneyLog>
|
||||
{
|
||||
public:
|
||||
CMoneyLog();
|
||||
virtual ~CMoneyLog();
|
||||
|
||||
void Save();
|
||||
void AddLog(BYTE bType, DWORD dwVnum, int iGold);
|
||||
|
||||
private:
|
||||
std::map<DWORD, int> m_MoneyLogContainer[MONEY_LOG_TYPE_MAX_NUM];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,54 @@
|
|||
#include "stdafx.h"
|
||||
#include "NetBase.h"
|
||||
#include "Config.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
LPFDWATCH CNetBase::m_fdWatcher = NULL;
|
||||
|
||||
CNetBase::CNetBase()
|
||||
{
|
||||
}
|
||||
|
||||
CNetBase::~CNetBase()
|
||||
{
|
||||
}
|
||||
|
||||
CNetPoller::CNetPoller()
|
||||
{
|
||||
}
|
||||
|
||||
CNetPoller::~CNetPoller()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
bool CNetPoller::Create()
|
||||
{
|
||||
sys_log(1, "NetPoller::Create()");
|
||||
|
||||
if (m_fdWatcher)
|
||||
return true;
|
||||
|
||||
m_fdWatcher = fdwatch_new(512);
|
||||
|
||||
if (!m_fdWatcher)
|
||||
{
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNetPoller::Destroy()
|
||||
{
|
||||
sys_log(1, "NetPoller::Destroy()");
|
||||
|
||||
if (m_fdWatcher)
|
||||
{
|
||||
fdwatch_delete(m_fdWatcher);
|
||||
m_fdWatcher = NULL;
|
||||
}
|
||||
|
||||
thecore_destroy();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_NETWORKBASE_H__
|
||||
#define __INC_NETWORKBASE_H__
|
||||
|
||||
class CNetBase
|
||||
{
|
||||
public:
|
||||
CNetBase();
|
||||
virtual ~CNetBase();
|
||||
|
||||
protected:
|
||||
static LPFDWATCH m_fdWatcher;
|
||||
};
|
||||
|
||||
class CNetPoller : public CNetBase, public singleton<CNetPoller>
|
||||
{
|
||||
public:
|
||||
CNetPoller();
|
||||
virtual ~CNetPoller();
|
||||
|
||||
bool Create();
|
||||
void Destroy();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,197 @@
|
|||
#include "stdafx.h"
|
||||
#include "Peer.h"
|
||||
#include "ItemIDRangeManager.h"
|
||||
|
||||
CPeer::CPeer()
|
||||
{
|
||||
m_state = 0;
|
||||
m_bChannel = 0;
|
||||
m_dwHandle = 0;
|
||||
m_dwUserCount = 0;
|
||||
m_wListenPort = 0;
|
||||
m_wP2PPort = 0;
|
||||
|
||||
memset(m_alMaps, 0, sizeof(m_alMaps));
|
||||
|
||||
m_itemRange.dwMin = m_itemRange.dwMax = m_itemRange.dwUsableItemIDMin = 0;
|
||||
m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0;
|
||||
}
|
||||
|
||||
CPeer::~CPeer()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void CPeer::OnAccept()
|
||||
{
|
||||
m_state = STATE_PLAYING;
|
||||
|
||||
static DWORD current_handle = 0;
|
||||
m_dwHandle = ++current_handle;
|
||||
|
||||
sys_log(0, "Connection accepted. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd);
|
||||
}
|
||||
|
||||
void CPeer::OnConnect()
|
||||
{
|
||||
sys_log(0, "Connection established. (host: %s handle: %u fd: %d)", m_host, m_dwHandle, m_fd);
|
||||
m_state = STATE_PLAYING;
|
||||
}
|
||||
|
||||
void CPeer::OnClose()
|
||||
{
|
||||
m_state = STATE_CLOSE;
|
||||
|
||||
sys_log(0, "Connection closed. (host: %s)", m_host);
|
||||
sys_log(0, "ItemIDRange: returned. %u ~ %u", m_itemRange.dwMin, m_itemRange.dwMax);
|
||||
|
||||
CItemIDRangeManager::instance().UpdateRange(m_itemRange.dwMin, m_itemRange.dwMax);
|
||||
|
||||
m_itemRange.dwMin = 0;
|
||||
m_itemRange.dwMax = 0;
|
||||
m_itemRange.dwUsableItemIDMin = 0;
|
||||
}
|
||||
|
||||
DWORD CPeer::GetHandle()
|
||||
{
|
||||
return m_dwHandle;
|
||||
}
|
||||
|
||||
DWORD CPeer::GetUserCount()
|
||||
{
|
||||
return m_dwUserCount;
|
||||
}
|
||||
|
||||
void CPeer::SetUserCount(DWORD dwCount)
|
||||
{
|
||||
m_dwUserCount = dwCount;
|
||||
}
|
||||
|
||||
bool CPeer::PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data)
|
||||
{
|
||||
if (GetRecvLength() < iBytesProceed + 9)
|
||||
return false;
|
||||
|
||||
const char * buf = (const char *) GetRecvBuffer();
|
||||
buf += iBytesProceed;
|
||||
|
||||
header = *(buf++);
|
||||
|
||||
dwHandle = *((DWORD *) buf);
|
||||
buf += sizeof(DWORD);
|
||||
|
||||
dwLength = *((DWORD *) buf);
|
||||
buf += sizeof(DWORD);
|
||||
|
||||
//sys_log(0, "%d header %d handle %u length %u", GetRecvLength(), header, dwHandle, dwLength);
|
||||
if (iBytesProceed + dwLength + 9 > (DWORD) GetRecvLength())
|
||||
{
|
||||
sys_log(0, "PeekPacket: not enough buffer size: len %u, recv %d",
|
||||
9+dwLength, GetRecvLength()-iBytesProceed);
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = buf;
|
||||
iBytesProceed += dwLength + 9;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPeer::EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize)
|
||||
{
|
||||
HEADER h;
|
||||
|
||||
sys_log(1, "EncodeHeader %u handle %u size %u", header, dwHandle, dwSize);
|
||||
|
||||
h.bHeader = header;
|
||||
h.dwHandle = dwHandle;
|
||||
h.dwSize = dwSize;
|
||||
|
||||
Encode(&h, sizeof(HEADER));
|
||||
}
|
||||
|
||||
void CPeer::EncodeReturn(BYTE header, DWORD dwHandle)
|
||||
{
|
||||
EncodeHeader(header, dwHandle, 0);
|
||||
}
|
||||
|
||||
int CPeer::Send()
|
||||
{
|
||||
if (m_state == STATE_CLOSE)
|
||||
return -1;
|
||||
|
||||
return (CPeerBase::Send());
|
||||
}
|
||||
|
||||
void CPeer::SetP2PPort(WORD wPort)
|
||||
{
|
||||
m_wP2PPort = wPort;
|
||||
}
|
||||
|
||||
void CPeer::SetMaps(long * pl)
|
||||
{
|
||||
thecore_memcpy(m_alMaps, pl, sizeof(m_alMaps));
|
||||
}
|
||||
|
||||
void CPeer::SendSpareItemIDRange()
|
||||
{
|
||||
if (m_itemSpareRange.dwMin == 0 || m_itemSpareRange.dwMax == 0 || m_itemSpareRange.dwUsableItemIDMin == 0)
|
||||
{
|
||||
EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable));
|
||||
Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetItemIDRange(m_itemSpareRange);
|
||||
|
||||
if (SetSpareItemIDRange(CItemIDRangeManager::instance().GetRange()) == false)
|
||||
{
|
||||
sys_log(0, "ItemIDRange: spare range set error");
|
||||
m_itemSpareRange.dwMin = m_itemSpareRange.dwMax = m_itemSpareRange.dwUsableItemIDMin = 0;
|
||||
}
|
||||
|
||||
EncodeHeader(HEADER_DG_ACK_SPARE_ITEM_ID_RANGE, 0, sizeof(TItemIDRangeTable));
|
||||
Encode(&m_itemSpareRange, sizeof(TItemIDRangeTable));
|
||||
}
|
||||
}
|
||||
|
||||
bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange)
|
||||
{
|
||||
if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false;
|
||||
|
||||
m_itemRange = itemRange;
|
||||
sys_log(0, "ItemIDRange: SET %s %u ~ %u start: %u", GetPublicIP(), m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPeer::SetSpareItemIDRange(TItemIDRangeTable itemRange)
|
||||
{
|
||||
if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false;
|
||||
|
||||
m_itemSpareRange = itemRange;
|
||||
sys_log(0, "ItemIDRange: SPARE SET %s %u ~ %u start: %u", GetPublicIP(), m_itemSpareRange.dwMin, m_itemSpareRange.dwMax,
|
||||
m_itemSpareRange.dwUsableItemIDMin);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPeer::CheckItemIDRangeCollision(TItemIDRangeTable itemRange)
|
||||
{
|
||||
if (m_itemRange.dwMin < itemRange.dwMax && m_itemRange.dwMax > itemRange.dwMin)
|
||||
{
|
||||
sys_err("ItemIDRange: Collision!! this %u ~ %u check %u ~ %u",
|
||||
m_itemRange.dwMin, m_itemRange.dwMax, itemRange.dwMin, itemRange.dwMax);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_itemSpareRange.dwMin < itemRange.dwMax && m_itemSpareRange.dwMax > itemRange.dwMin)
|
||||
{
|
||||
sys_err("ItemIDRange: Collision with spare range this %u ~ %u check %u ~ %u",
|
||||
m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, itemRange.dwMin, itemRange.dwMax);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_PEER_H__
|
||||
#define __INC_PEER_H__
|
||||
|
||||
#include "PeerBase.h"
|
||||
|
||||
class CPeer : public CPeerBase
|
||||
{
|
||||
protected:
|
||||
virtual void OnAccept();
|
||||
virtual void OnClose();
|
||||
virtual void OnConnect();
|
||||
|
||||
public:
|
||||
#pragma pack(1)
|
||||
typedef struct _header
|
||||
{
|
||||
BYTE bHeader;
|
||||
DWORD dwHandle;
|
||||
DWORD dwSize;
|
||||
} HEADER;
|
||||
#pragma pack()
|
||||
enum EState
|
||||
{
|
||||
STATE_CLOSE = 0,
|
||||
STATE_PLAYING = 1
|
||||
};
|
||||
|
||||
CPeer();
|
||||
virtual ~CPeer();
|
||||
|
||||
void EncodeHeader(BYTE header, DWORD dwHandle, DWORD dwSize);
|
||||
bool PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWORD & dwLength, const char ** data);
|
||||
void EncodeReturn(BYTE header, DWORD dwHandle);
|
||||
|
||||
void ProcessInput();
|
||||
int Send();
|
||||
|
||||
DWORD GetHandle();
|
||||
DWORD GetUserCount();
|
||||
void SetUserCount(DWORD dwCount);
|
||||
|
||||
void SetPublicIP(const char * ip) { m_stPublicIP = ip; }
|
||||
const char * GetPublicIP() { return m_stPublicIP.c_str(); }
|
||||
|
||||
void SetChannel(BYTE bChannel) { m_bChannel = bChannel; }
|
||||
BYTE GetChannel() { return m_bChannel; }
|
||||
|
||||
void SetListenPort(WORD wPort) { m_wListenPort = wPort; }
|
||||
WORD GetListenPort() { return m_wListenPort; }
|
||||
|
||||
void SetP2PPort(WORD wPort);
|
||||
WORD GetP2PPort() { return m_wP2PPort; }
|
||||
|
||||
void SetMaps(long* pl);
|
||||
long * GetMaps() { return &m_alMaps[0]; }
|
||||
|
||||
bool SetItemIDRange(TItemIDRangeTable itemRange);
|
||||
bool SetSpareItemIDRange(TItemIDRangeTable itemRange);
|
||||
bool CheckItemIDRangeCollision(TItemIDRangeTable itemRange);
|
||||
void SendSpareItemIDRange();
|
||||
|
||||
private:
|
||||
int m_state;
|
||||
|
||||
BYTE m_bChannel;
|
||||
DWORD m_dwHandle;
|
||||
DWORD m_dwUserCount;
|
||||
WORD m_wListenPort; // 게임서버가 클라이언트를 위해 listen 하는 포트
|
||||
WORD m_wP2PPort; // 게임서버가 게임서버 P2P 접속을 위해 listen 하는 포트
|
||||
long m_alMaps[32]; // 어떤 맵을 관장하고 있는가?
|
||||
|
||||
TItemIDRangeTable m_itemRange;
|
||||
TItemIDRangeTable m_itemSpareRange;
|
||||
|
||||
std::string m_stPublicIP;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,214 @@
|
|||
#include "stdafx.h"
|
||||
#include "PeerBase.h"
|
||||
|
||||
CPeerBase::CPeerBase() : m_fd(INVALID_SOCKET), m_BytesRemain(0), m_outBuffer(NULL), m_inBuffer(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
CPeerBase::~CPeerBase()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void CPeerBase::Disconnect()
|
||||
{
|
||||
if (m_fd != INVALID_SOCKET)
|
||||
{
|
||||
fdwatch_del_fd(m_fdWatcher, m_fd);
|
||||
|
||||
socket_close(m_fd);
|
||||
m_fd = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
void CPeerBase::Destroy()
|
||||
{
|
||||
Disconnect();
|
||||
|
||||
if (m_outBuffer)
|
||||
{
|
||||
buffer_delete(m_outBuffer);
|
||||
m_outBuffer = NULL;
|
||||
}
|
||||
|
||||
if (m_inBuffer)
|
||||
{
|
||||
buffer_delete(m_inBuffer);
|
||||
m_inBuffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPeerBase::Accept(socket_t fd_accept)
|
||||
{
|
||||
struct sockaddr_in peer;
|
||||
|
||||
if ((m_fd = socket_accept(fd_accept, &peer)) == INVALID_SOCKET)
|
||||
{
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
//socket_block(m_fd);
|
||||
socket_sndbuf(m_fd, 233016);
|
||||
socket_rcvbuf(m_fd, 233016);
|
||||
|
||||
strlcpy(m_host, inet_ntoa(peer.sin_addr), sizeof(m_host));
|
||||
m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE);
|
||||
m_inBuffer = buffer_new(MAX_INPUT_LEN);
|
||||
|
||||
if (!m_outBuffer || !m_inBuffer)
|
||||
{
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false);
|
||||
|
||||
OnAccept();
|
||||
sys_log(0, "ACCEPT FROM %s", inet_ntoa(peer.sin_addr));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPeerBase::Connect(const char* host, WORD port)
|
||||
{
|
||||
strlcpy(m_host, host, sizeof(m_host));
|
||||
|
||||
if ((m_fd = socket_connect(host, port)) == INVALID_SOCKET)
|
||||
return false;
|
||||
|
||||
m_outBuffer = buffer_new(DEFAULT_PACKET_BUFFER_SIZE);
|
||||
|
||||
if (!m_outBuffer)
|
||||
{
|
||||
Destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_READ, false);
|
||||
|
||||
OnConnect();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPeerBase::Close()
|
||||
{
|
||||
OnClose();
|
||||
}
|
||||
|
||||
void CPeerBase::EncodeBYTE(BYTE b)
|
||||
{
|
||||
if (!m_outBuffer)
|
||||
{
|
||||
sys_err("Not ready to write");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_write(m_outBuffer, &b, 1);
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true);
|
||||
}
|
||||
|
||||
void CPeerBase::EncodeWORD(WORD w)
|
||||
{
|
||||
if (!m_outBuffer)
|
||||
{
|
||||
sys_err("Not ready to write");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_write(m_outBuffer, &w, 2);
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true);
|
||||
}
|
||||
|
||||
void CPeerBase::EncodeDWORD(DWORD dw)
|
||||
{
|
||||
if (!m_outBuffer)
|
||||
{
|
||||
sys_err("Not ready to write");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_write(m_outBuffer, &dw, 4);
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true);
|
||||
}
|
||||
|
||||
void CPeerBase::Encode(const void* data, DWORD size)
|
||||
{
|
||||
if (!m_outBuffer)
|
||||
{
|
||||
sys_err("Not ready to write");
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_write(m_outBuffer, data, size);
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true);
|
||||
}
|
||||
|
||||
int CPeerBase::Recv()
|
||||
{
|
||||
if (!m_inBuffer)
|
||||
{
|
||||
sys_err("input buffer nil");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_adjust_size(m_inBuffer, MAX_INPUT_LEN >> 2);
|
||||
int bytes_to_read = buffer_has_space(m_inBuffer);
|
||||
ssize_t bytes_read = socket_read(m_fd, (char *) buffer_write_peek(m_inBuffer), bytes_to_read);
|
||||
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
sys_err("socket_read failed %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
return 0;
|
||||
|
||||
buffer_write_proceed(m_inBuffer, bytes_read);
|
||||
m_BytesRemain = buffer_size(m_inBuffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CPeerBase::RecvEnd(int proceed_bytes)
|
||||
{
|
||||
buffer_read_proceed(m_inBuffer, proceed_bytes);
|
||||
m_BytesRemain = buffer_size(m_inBuffer);
|
||||
}
|
||||
|
||||
int CPeerBase::GetRecvLength()
|
||||
{
|
||||
return m_BytesRemain;
|
||||
}
|
||||
|
||||
const void * CPeerBase::GetRecvBuffer()
|
||||
{
|
||||
return buffer_read_peek(m_inBuffer);
|
||||
}
|
||||
|
||||
int CPeerBase::GetSendLength()
|
||||
{
|
||||
return buffer_size(m_outBuffer);
|
||||
}
|
||||
|
||||
int CPeerBase::Send()
|
||||
{
|
||||
if (buffer_size(m_outBuffer) <= 0)
|
||||
return 0;
|
||||
|
||||
int iBufferLeft = fdwatch_get_buffer_size(m_fdWatcher, m_fd);
|
||||
int iBytesToWrite = MIN(iBufferLeft, buffer_size(m_outBuffer));
|
||||
|
||||
if (iBytesToWrite == 0)
|
||||
return 0;
|
||||
|
||||
int result = socket_write(m_fd, (const char *) buffer_read_peek(m_outBuffer), iBytesToWrite);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
buffer_read_proceed(m_outBuffer, iBytesToWrite);
|
||||
|
||||
if (buffer_size(m_outBuffer) != 0)
|
||||
fdwatch_add_fd(m_fdWatcher, m_fd, this, FDW_WRITE, true);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_PEERBASE_H__
|
||||
#define __INC_PEERBASE_H__
|
||||
|
||||
#include "NetBase.h"
|
||||
|
||||
class CPeerBase : public CNetBase
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_HOST_LENGTH = 30,
|
||||
MAX_INPUT_LEN = 1024 * 1024 * 2,
|
||||
DEFAULT_PACKET_BUFFER_SIZE = 1024 * 1024 * 2
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual void OnAccept() = 0;
|
||||
virtual void OnConnect() = 0;
|
||||
virtual void OnClose() = 0;
|
||||
|
||||
public:
|
||||
bool Accept(socket_t accept_fd);
|
||||
bool Connect(const char* host, WORD port);
|
||||
void Close();
|
||||
|
||||
public:
|
||||
CPeerBase();
|
||||
virtual ~CPeerBase();
|
||||
|
||||
void Disconnect();
|
||||
void Destroy();
|
||||
|
||||
socket_t GetFd() { return m_fd; }
|
||||
|
||||
void EncodeBYTE(BYTE b);
|
||||
void EncodeWORD(WORD w);
|
||||
void EncodeDWORD(DWORD dw);
|
||||
void Encode(const void* data, DWORD size);
|
||||
int Send();
|
||||
|
||||
int Recv();
|
||||
void RecvEnd(int proceed_bytes);
|
||||
int GetRecvLength();
|
||||
const void * GetRecvBuffer();
|
||||
|
||||
int GetSendLength();
|
||||
|
||||
const char * GetHost() { return m_host; }
|
||||
|
||||
protected:
|
||||
char m_host[MAX_HOST_LENGTH + 1];
|
||||
socket_t m_fd;
|
||||
|
||||
private:
|
||||
int m_BytesRemain;
|
||||
LPBUFFER m_outBuffer;
|
||||
LPBUFFER m_inBuffer;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,291 @@
|
|||
#include "stdafx.h"
|
||||
#include "PrivManager.h"
|
||||
#include "ClientManager.h"
|
||||
|
||||
const int PRIV_DURATION = 60*60*12;
|
||||
const int CHARACTER_GOOD_PRIV_DURATION = 2*60*60;
|
||||
const int CHARACTER_BAD_PRIV_DURATION = 60*60;
|
||||
|
||||
CPrivManager::CPrivManager()
|
||||
{
|
||||
for (int type = 0; type < MAX_PRIV_NUM; ++type)
|
||||
{
|
||||
for (int empire = 0; empire < EMPIRE_MAX_NUM; ++empire)
|
||||
m_aaPrivEmpire[type][empire] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
CPrivManager::~CPrivManager()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// @version 05/06/07 Bang2ni - 중복적으로 보너스가 적용 된 길드에 대한 처리
|
||||
//
|
||||
void CPrivManager::Update()
|
||||
{
|
||||
time_t now = CClientManager::instance().GetCurrentTime();
|
||||
|
||||
while (!m_pqPrivGuild.empty() && m_pqPrivGuild.top().first <= now)
|
||||
{
|
||||
TPrivGuildData* p = m_pqPrivGuild.top().second;
|
||||
m_pqPrivGuild.pop();
|
||||
|
||||
if (p->value != 0 && !p->bRemoved)
|
||||
{
|
||||
|
||||
typeof(m_aPrivGuild[p->type].begin()) it = m_aPrivGuild[p->type].find(p->guild_id);
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
// 길드에 중복적으로 보너스가 설정되었을 경우 map 의 value 가 갱신(수정) 되었으므로
|
||||
// TPrivGuildData 의 포인터가 같을때 실제로 삭제해 주고 게임서버들에게 cast 해 준다.
|
||||
if (it != m_aPrivGuild[p->type].end() && it->second == p) {
|
||||
m_aPrivGuild[p->type].erase(it);
|
||||
SendChangeGuildPriv(p->guild_id, p->type, 0, 0);
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
}
|
||||
}
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
while (!m_pqPrivEmpire.empty() && m_pqPrivEmpire.top().first <= now)
|
||||
{
|
||||
TPrivEmpireData* p = (m_pqPrivEmpire.top().second);
|
||||
m_pqPrivEmpire.pop();
|
||||
|
||||
if (p->value != 0 && !p->bRemoved)
|
||||
{
|
||||
SendChangeEmpirePriv(p->empire, p->type, 0, 0);
|
||||
m_aaPrivEmpire[p->type][p->empire] = 0;
|
||||
}
|
||||
|
||||
delete p;
|
||||
}
|
||||
|
||||
while (!m_pqPrivChar.empty() && m_pqPrivChar.top().first <= now)
|
||||
{
|
||||
TPrivCharData* p = (m_pqPrivChar.top().second);
|
||||
m_pqPrivChar.pop();
|
||||
|
||||
if (!p->bRemoved)
|
||||
{
|
||||
// TODO send packet
|
||||
SendChangeCharPriv(p->pid, p->type, 0);
|
||||
typeof(m_aPrivChar[p->type].begin()) it = m_aPrivChar[p->type].find(p->pid);
|
||||
if (it != m_aPrivChar[p->type].end())
|
||||
m_aPrivChar[p->type].erase(it);
|
||||
}
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
|
||||
void CPrivManager::AddCharPriv(DWORD pid, BYTE type, int value)
|
||||
{
|
||||
if (MAX_PRIV_NUM <= type)
|
||||
{
|
||||
sys_err("PRIV_MANAGER: AddCharPriv: wrong char priv type(%u) recved", type);
|
||||
return;
|
||||
}
|
||||
|
||||
typeof(m_aPrivChar[type].begin()) it = m_aPrivChar[type].find(pid);
|
||||
|
||||
if (it != m_aPrivChar[type].end())
|
||||
return;
|
||||
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
time_t now = CClientManager::instance().GetCurrentTime();
|
||||
TPrivCharData* p = new TPrivCharData(type, value, pid);
|
||||
|
||||
int iDuration = CHARACTER_BAD_PRIV_DURATION;
|
||||
|
||||
if (value > 0)
|
||||
iDuration = CHARACTER_GOOD_PRIV_DURATION;
|
||||
|
||||
m_pqPrivChar.push(std::make_pair(now+iDuration, p));
|
||||
m_aPrivChar[type].insert(std::make_pair(pid, p));
|
||||
|
||||
// TODO send packet
|
||||
sys_log(0, "AddCharPriv %d %d %d", pid, type, value);
|
||||
SendChangeCharPriv(pid, type, value);
|
||||
}
|
||||
|
||||
//
|
||||
// @version 05/06/07 Bang2ni - 이미 보너스가 적용 된 길드에 보너스 설정
|
||||
//
|
||||
void CPrivManager::AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t duration_sec)
|
||||
{
|
||||
if (MAX_PRIV_NUM <= type)
|
||||
{
|
||||
sys_err("PRIV_MANAGER: AddGuildPriv: wrong guild priv type(%u) recved", type);
|
||||
return;
|
||||
}
|
||||
|
||||
typeof(m_aPrivGuild[type].begin()) it = m_aPrivGuild[type].find(guild_id);
|
||||
|
||||
time_t now = CClientManager::instance().GetCurrentTime();
|
||||
time_t end = now + duration_sec;
|
||||
TPrivGuildData * p = new TPrivGuildData(type, value, guild_id, end);
|
||||
m_pqPrivGuild.push(std::make_pair(end, p));
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
// 이미 보너스가 설정되 있다면 map 의 value 를 갱신해 준다.
|
||||
// 이전 value 의 포인터는 priority queue 에서 삭제될 때 해제된다.
|
||||
if (it != m_aPrivGuild[type].end())
|
||||
it->second = p;
|
||||
else
|
||||
m_aPrivGuild[type].insert(std::make_pair(guild_id, p));
|
||||
|
||||
SendChangeGuildPriv(guild_id, type, value, end);
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
|
||||
sys_log(0, "Guild Priv guild(%d) type(%d) value(%d) duration_sec(%d)", guild_id, type, value, duration_sec);
|
||||
}
|
||||
|
||||
void CPrivManager::AddEmpirePriv(BYTE empire, BYTE type, int value, time_t duration_sec)
|
||||
{
|
||||
if (MAX_PRIV_NUM <= type)
|
||||
{
|
||||
sys_err("PRIV_MANAGER: AddEmpirePriv: wrong empire priv type(%u) recved", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (duration_sec < 0)
|
||||
duration_sec = 0;
|
||||
|
||||
time_t now = CClientManager::instance().GetCurrentTime();
|
||||
time_t end = now+duration_sec;
|
||||
|
||||
// 이전 설정값 무효화
|
||||
// priority_queue에 들어있는 pointer == m_aaPrivEmpire[type][empire]
|
||||
{
|
||||
if (m_aaPrivEmpire[type][empire])
|
||||
m_aaPrivEmpire[type][empire]->bRemoved = true;
|
||||
}
|
||||
|
||||
TPrivEmpireData * p = new TPrivEmpireData(type, value, empire, end);
|
||||
m_pqPrivEmpire.push(std::make_pair(end, p));
|
||||
m_aaPrivEmpire[type][empire] = p;
|
||||
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
SendChangeEmpirePriv(empire, type, value, end);
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
|
||||
sys_log(0, "Empire Priv empire(%d) type(%d) value(%d) duration_sec(%d)", empire, type, value, duration_sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @version 05/06/08 Bang2ni - 지속시간 추가
|
||||
*/
|
||||
struct FSendChangeGuildPriv
|
||||
{
|
||||
FSendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec)
|
||||
{
|
||||
p.guild_id = guild_id;
|
||||
p.type = type;
|
||||
p.value = value;
|
||||
p.bLog = 1;
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
p.end_time_sec = end_time_sec;
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
}
|
||||
|
||||
void operator() (CPeer* peer)
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_CHANGE_GUILD_PRIV, 0, sizeof(TPacketDGChangeGuildPriv));
|
||||
peer->Encode(&p, sizeof(TPacketDGChangeGuildPriv));
|
||||
p.bLog = 0;
|
||||
}
|
||||
|
||||
TPacketDGChangeGuildPriv p;
|
||||
};
|
||||
|
||||
struct FSendChangeEmpirePriv
|
||||
{
|
||||
FSendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec)
|
||||
{
|
||||
p.empire = empire;
|
||||
p.type = type;
|
||||
p.value = value;
|
||||
p.bLog = 1;
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
p.end_time_sec = end_time_sec;
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
}
|
||||
|
||||
void operator ()(CPeer* peer)
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_CHANGE_EMPIRE_PRIV, 0, sizeof(TPacketDGChangeEmpirePriv));
|
||||
peer->Encode(&p, sizeof(TPacketDGChangeEmpirePriv));
|
||||
p.bLog = 0;
|
||||
}
|
||||
|
||||
TPacketDGChangeEmpirePriv p;
|
||||
};
|
||||
|
||||
struct FSendChangeCharPriv
|
||||
{
|
||||
FSendChangeCharPriv(DWORD pid, BYTE type, int value)
|
||||
{
|
||||
p.pid = pid;
|
||||
p.type = type;
|
||||
p.value = value;
|
||||
p.bLog = 1;
|
||||
}
|
||||
void operator()(CPeer* peer)
|
||||
{
|
||||
peer->EncodeHeader(HEADER_DG_CHANGE_CHARACTER_PRIV, 0, sizeof(TPacketDGChangeCharacterPriv));
|
||||
peer->Encode(&p, sizeof(TPacketDGChangeCharacterPriv));
|
||||
p.bLog = 0;
|
||||
}
|
||||
TPacketDGChangeCharacterPriv p;
|
||||
};
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
void CPrivManager::SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec)
|
||||
{
|
||||
CClientManager::instance().for_each_peer(FSendChangeGuildPriv(guild_id, type, value, end_time_sec));
|
||||
}
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
void CPrivManager::SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec)
|
||||
{
|
||||
CClientManager::instance().for_each_peer(FSendChangeEmpirePriv(empire, type, value, end_time_sec));
|
||||
}
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
|
||||
void CPrivManager::SendChangeCharPriv(DWORD pid, BYTE type, int value)
|
||||
{
|
||||
CClientManager::instance().for_each_peer(FSendChangeCharPriv(pid, type, value));
|
||||
}
|
||||
|
||||
void CPrivManager::SendPrivOnSetup(CPeer* peer)
|
||||
{
|
||||
for (int i = 1; i < MAX_PRIV_NUM; ++i)
|
||||
{
|
||||
for (int e = 0; e < EMPIRE_MAX_NUM; ++e)
|
||||
{
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
TPrivEmpireData* pPrivEmpireData = m_aaPrivEmpire[i][e];
|
||||
if (pPrivEmpireData)
|
||||
{
|
||||
FSendChangeEmpirePriv(e, i, pPrivEmpireData->value, pPrivEmpireData->end_time_sec)(peer);
|
||||
}
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
}
|
||||
|
||||
for (typeof(m_aPrivGuild[i].begin()) it = m_aPrivGuild[i].begin(); it != m_aPrivGuild[i].end();++it)
|
||||
{
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
FSendChangeGuildPriv(it->first, i, it->second->value, it->second->end_time_sec)(peer);
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
}
|
||||
for (typeof(m_aPrivChar[i].begin()) it = m_aPrivChar[i].begin(); it != m_aPrivChar[i].end(); ++it)
|
||||
{
|
||||
FSendChangeCharPriv(it->first, i, it->second->value)(peer);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_PRIV_MANAGER_H
|
||||
#define __INC_PRIV_MANAGER_H
|
||||
|
||||
#include "Peer.h"
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
struct TPrivEmpireData
|
||||
{
|
||||
BYTE type;
|
||||
int value;
|
||||
bool bRemoved;
|
||||
BYTE empire;
|
||||
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
time_t end_time_sec;
|
||||
|
||||
TPrivEmpireData(BYTE type, int value, BYTE empire, time_t end_time_sec)
|
||||
: type(type), value(value), bRemoved(false), empire(empire), end_time_sec(end_time_sec)
|
||||
{}
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
};
|
||||
|
||||
/**
|
||||
* @version 05/06/08 Bang2ni - 지속시간 추가
|
||||
*/
|
||||
struct TPrivGuildData
|
||||
{
|
||||
BYTE type;
|
||||
int value;
|
||||
bool bRemoved;
|
||||
DWORD guild_id;
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
time_t end_time_sec; ///< 지속시간
|
||||
|
||||
TPrivGuildData(BYTE type, int value, DWORD guild_id, time_t _end_time_sec)
|
||||
: type(type), value(value), bRemoved(false), guild_id(guild_id), end_time_sec(_end_time_sec )
|
||||
{}
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
};
|
||||
|
||||
struct TPrivCharData
|
||||
{
|
||||
BYTE type;
|
||||
int value;
|
||||
bool bRemoved;
|
||||
DWORD pid;
|
||||
TPrivCharData(BYTE type, int value, DWORD pid)
|
||||
: type(type), value(value), bRemoved(false), pid(pid)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @version 05/06/08 Bang2ni - Guild privilege 관련 함수 지속 시간 추가
|
||||
*/
|
||||
class CPrivManager : public singleton<CPrivManager>
|
||||
{
|
||||
public:
|
||||
CPrivManager();
|
||||
virtual ~CPrivManager();
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
void AddGuildPriv(DWORD guild_id, BYTE type, int value, time_t time_sec);
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
void AddEmpirePriv(BYTE empire, BYTE type, int value, time_t time_sec);
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
|
||||
void AddCharPriv(DWORD pid, BYTE type, int value);
|
||||
|
||||
void Update();
|
||||
|
||||
void SendPrivOnSetup(CPeer* peer);
|
||||
|
||||
private:
|
||||
|
||||
// ADD_GUILD_PRIV_TIME
|
||||
void SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec);
|
||||
// END_OF_ADD_GUILD_PRIV_TIME
|
||||
|
||||
// ADD_EMPIRE_PRIV_TIME
|
||||
void SendChangeEmpirePriv(BYTE empire, BYTE type, int value, time_t end_time_sec);
|
||||
// END_OF_ADD_EMPIRE_PRIV_TIME
|
||||
|
||||
void SendChangeCharPriv(DWORD pid, BYTE type, int value);
|
||||
|
||||
typedef std::pair<time_t, TPrivCharData *> stPairChar;
|
||||
typedef std::pair<time_t, TPrivGuildData*> stPairGuild;
|
||||
typedef std::pair<time_t, TPrivEmpireData*> stPairEmpire;
|
||||
|
||||
std::priority_queue<stPairChar, std::vector<stPairChar>, std::greater<stPairChar> >
|
||||
m_pqPrivChar;
|
||||
std::priority_queue<stPairGuild, std::vector<stPairGuild>, std::greater<stPairGuild> >
|
||||
m_pqPrivGuild;
|
||||
std::priority_queue<stPairEmpire, std::vector<stPairEmpire>, std::greater<stPairEmpire> >
|
||||
m_pqPrivEmpire;
|
||||
|
||||
TPrivEmpireData* m_aaPrivEmpire[MAX_PRIV_NUM][EMPIRE_MAX_NUM];
|
||||
std::map<DWORD, TPrivGuildData*> m_aPrivGuild[MAX_PRIV_NUM];
|
||||
std::map<DWORD, TPrivCharData*> m_aPrivChar[MAX_PRIV_NUM];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,838 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include <math.h>
|
||||
#include "ProtoReader.h"
|
||||
|
||||
#include "CsvReader.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
inline string trim_left(const string& str)
|
||||
{
|
||||
string::size_type n = str.find_first_not_of(" \t\v\n\r");
|
||||
return n == string::npos ? str : str.substr(n, str.length());
|
||||
}
|
||||
|
||||
inline string trim_right(const string& str)
|
||||
{
|
||||
string::size_type n = str.find_last_not_of(" \t\v\n\r");
|
||||
return n == string::npos ? str : str.substr(0, n + 1);
|
||||
}
|
||||
|
||||
string trim(const string& str){return trim_left(trim_right(str));}
|
||||
|
||||
static string* StringSplit(string strOrigin, string strTok)
|
||||
{
|
||||
int cutAt; //자르는위치
|
||||
int index = 0; //문자열인덱스
|
||||
string* strResult = new string[30]; //결과return 할변수
|
||||
|
||||
//strTok을찾을때까지반복
|
||||
while ((cutAt = strOrigin.find_first_of(strTok)) != strOrigin.npos)
|
||||
{
|
||||
if (cutAt > 0) //자르는위치가0보다크면(성공시)
|
||||
{
|
||||
strResult[index++] = strOrigin.substr(0, cutAt); //결과배열에추가
|
||||
}
|
||||
strOrigin = strOrigin.substr(cutAt+1); //원본은자른부분제외한나머지
|
||||
}
|
||||
|
||||
if(strOrigin.length() > 0) //원본이아직남았으면
|
||||
{
|
||||
strResult[index++] = strOrigin.substr(0, cutAt); //나머지를결과배열에추가
|
||||
}
|
||||
|
||||
for( int i=0;i<index;i++)
|
||||
{
|
||||
strResult[i] = trim(strResult[i]);
|
||||
}
|
||||
|
||||
return strResult; //결과return
|
||||
}
|
||||
|
||||
|
||||
|
||||
int get_Item_Type_Value(string inputString)
|
||||
{
|
||||
string arType[] = {"ITEM_NONE", "ITEM_WEAPON",
|
||||
"ITEM_ARMOR", "ITEM_USE",
|
||||
"ITEM_AUTOUSE", "ITEM_MATERIAL",
|
||||
"ITEM_SPECIAL", "ITEM_TOOL",
|
||||
"ITEM_LOTTERY", "ITEM_ELK", //10개
|
||||
|
||||
"ITEM_METIN", "ITEM_CONTAINER",
|
||||
"ITEM_FISH", "ITEM_ROD",
|
||||
"ITEM_RESOURCE", "ITEM_CAMPFIRE",
|
||||
"ITEM_UNIQUE", "ITEM_SKILLBOOK",
|
||||
"ITEM_QUEST", "ITEM_POLYMORPH", //20개
|
||||
|
||||
"ITEM_TREASURE_BOX", "ITEM_TREASURE_KEY",
|
||||
"ITEM_SKILLFORGET", "ITEM_GIFTBOX",
|
||||
"ITEM_PICK", "ITEM_HAIR",
|
||||
"ITEM_TOTEM", "ITEM_BLEND",
|
||||
"ITEM_COSTUME", "ITEM_DS", //30개
|
||||
|
||||
"ITEM_SPECIAL_DS", "ITEM_EXTRACT",
|
||||
"ITEM_SECONDARY_COIN", //33개
|
||||
|
||||
"ITEM_RING",
|
||||
"ITEM_BELT", //35개 (EItemTypes 값으로 치면 34)
|
||||
};
|
||||
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "Type : " << typeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arType)/sizeof(arType[0]);j++) {
|
||||
string tempString = arType[j];
|
||||
if (inputString.find(tempString)!=string::npos && tempString.find(inputString)!=string::npos) {
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
|
||||
}
|
||||
|
||||
int get_Item_SubType_Value(int type_value, string inputString)
|
||||
{
|
||||
static string arSub1[] = { "WEAPON_SWORD", "WEAPON_DAGGER", "WEAPON_BOW", "WEAPON_TWO_HANDED",
|
||||
"WEAPON_BELL", "WEAPON_FAN", "WEAPON_ARROW", "WEAPON_MOUNT_SPEAR"};
|
||||
static string arSub2[] = { "ARMOR_BODY", "ARMOR_HEAD", "ARMOR_SHIELD", "ARMOR_WRIST", "ARMOR_FOOTS",
|
||||
"ARMOR_NECK", "ARMOR_EAR", "ARMOR_NUM_TYPES"};
|
||||
static string arSub3[] = { "USE_POTION", "USE_TALISMAN", "USE_TUNING", "USE_MOVE", "USE_TREASURE_BOX", "USE_MONEYBAG", "USE_BAIT",
|
||||
"USE_ABILITY_UP", "USE_AFFECT", "USE_CREATE_STONE", "USE_SPECIAL", "USE_POTION_NODELAY", "USE_CLEAR",
|
||||
"USE_INVISIBILITY", "USE_DETACHMENT", "USE_BUCKET", "USE_POTION_CONTINUE", "USE_CLEAN_SOCKET",
|
||||
"USE_CHANGE_ATTRIBUTE", "USE_ADD_ATTRIBUTE", "USE_ADD_ACCESSORY_SOCKET", "USE_PUT_INTO_ACCESSORY_SOCKET",
|
||||
"USE_ADD_ATTRIBUTE2", "USE_RECIPE", "USE_CHANGE_ATTRIBUTE2", "USE_BIND", "USE_UNBIND", "USE_TIME_CHARGE_PER", "USE_TIME_CHARGE_FIX", "USE_PUT_INTO_BELT_SOCKET", "USE_PUT_INTO_RING_SOCKET"};
|
||||
static string arSub4[] = { "AUTOUSE_POTION", "AUTOUSE_ABILITY_UP", "AUTOUSE_BOMB", "AUTOUSE_GOLD", "AUTOUSE_MONEYBAG", "AUTOUSE_TREASURE_BOX"};
|
||||
static string arSub5[] = { "MATERIAL_LEATHER", "MATERIAL_BLOOD", "MATERIAL_ROOT", "MATERIAL_NEEDLE", "MATERIAL_JEWEL",
|
||||
"MATERIAL_DS_REFINE_NORMAL", "MATERIAL_DS_REFINE_BLESSED", "MATERIAL_DS_REFINE_HOLLY"};
|
||||
static string arSub6[] = { "SPECIAL_MAP", "SPECIAL_KEY", "SPECIAL_DOC", "SPECIAL_SPIRIT"};
|
||||
static string arSub7[] = { "TOOL_FISHING_ROD" };
|
||||
static string arSub8[] = { "LOTTERY_TICKET", "LOTTERY_INSTANT" };
|
||||
static string arSub10[] = { "METIN_NORMAL", "METIN_GOLD" };
|
||||
static string arSub12[] = { "FISH_ALIVE", "FISH_DEAD"};
|
||||
static string arSub14[] = { "RESOURCE_FISHBONE", "RESOURCE_WATERSTONEPIECE", "RESOURCE_WATERSTONE", "RESOURCE_BLOOD_PEARL",
|
||||
"RESOURCE_BLUE_PEARL", "RESOURCE_WHITE_PEARL", "RESOURCE_BUCKET", "RESOURCE_CRYSTAL", "RESOURCE_GEM",
|
||||
"RESOURCE_STONE", "RESOURCE_METIN", "RESOURCE_ORE" };
|
||||
static string arSub16[] = { "UNIQUE_NONE", "UNIQUE_BOOK", "UNIQUE_SPECIAL_RIDE", "UNIQUE_3", "UNIQUE_4", "UNIQUE_5",
|
||||
"UNIQUE_6", "UNIQUE_7", "UNIQUE_8", "UNIQUE_9", "USE_SPECIAL"};
|
||||
static string arSub28[] = { "COSTUME_BODY", "COSTUME_HAIR" };
|
||||
static string arSub29[] = { "DS_SLOT1", "DS_SLOT2", "DS_SLOT3", "DS_SLOT4", "DS_SLOT5", "DS_SLOT6" };
|
||||
static string arSub31[] = { "EXTRACT_DRAGON_SOUL", "EXTRACT_DRAGON_HEART" };
|
||||
|
||||
static string* arSubType[] = {0, //0
|
||||
arSub1, //1
|
||||
arSub2, //2
|
||||
arSub3, //3
|
||||
arSub4, //4
|
||||
arSub5, //5
|
||||
arSub6, //6
|
||||
arSub7, //7
|
||||
arSub8, //8
|
||||
0, //9
|
||||
arSub10, //10
|
||||
0, //11
|
||||
arSub12, //12
|
||||
0, //13
|
||||
arSub14, //14
|
||||
0, //15
|
||||
arSub16, //16
|
||||
0, //17
|
||||
0, //18
|
||||
0, //19
|
||||
0, //20
|
||||
0, //21
|
||||
0, //22
|
||||
0, //23
|
||||
0, //24
|
||||
0, //25
|
||||
0, //26
|
||||
0, //27
|
||||
arSub28, //28
|
||||
arSub29, //29
|
||||
arSub29, //30
|
||||
arSub31, //31
|
||||
0, //32
|
||||
0, //33 반지
|
||||
0, //34 벨트
|
||||
};
|
||||
static int arNumberOfSubtype[_countof(arSubType)] = {
|
||||
0,
|
||||
sizeof(arSub1)/sizeof(arSub1[0]),
|
||||
sizeof(arSub2)/sizeof(arSub2[0]),
|
||||
sizeof(arSub3)/sizeof(arSub3[0]),
|
||||
sizeof(arSub4)/sizeof(arSub4[0]),
|
||||
sizeof(arSub5)/sizeof(arSub5[0]),
|
||||
sizeof(arSub6)/sizeof(arSub6[0]),
|
||||
sizeof(arSub7)/sizeof(arSub7[0]),
|
||||
sizeof(arSub8)/sizeof(arSub8[0]),
|
||||
0,
|
||||
sizeof(arSub10)/sizeof(arSub10[0]),
|
||||
0,
|
||||
sizeof(arSub12)/sizeof(arSub12[0]),
|
||||
0,
|
||||
sizeof(arSub14)/sizeof(arSub14[0]),
|
||||
0,
|
||||
sizeof(arSub16)/sizeof(arSub16[0]),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
sizeof(arSub28)/sizeof(arSub28[0]),
|
||||
sizeof(arSub29)/sizeof(arSub29[0]),
|
||||
sizeof(arSub29)/sizeof(arSub29[0]),
|
||||
sizeof(arSub31)/sizeof(arSub31[0]),
|
||||
0, // 32
|
||||
0, // 33 반지
|
||||
0, // 34 벨트
|
||||
};
|
||||
|
||||
|
||||
assert(_countof(arSubType) > type_value && "Subtype rule: Out of range!!");
|
||||
|
||||
// assert 안 먹히는 듯..
|
||||
if (_countof(arSubType) <= type_value)
|
||||
{
|
||||
sys_err("SubType : Out of range!! (type_value: %d, count of registered subtype: %d", type_value, _countof(arSubType));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//아이템 타입의 서브타입 어레이가 존재하는지 알아보고, 없으면 0 리턴
|
||||
if (arSubType[type_value]==0) {
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "SubType : " << subTypeStr << " -> ";
|
||||
for (int j=0;j<arNumberOfSubtype[type_value];j++) {
|
||||
string tempString = arSubType[type_value][j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int get_Item_AntiFlag_Value(string inputString)
|
||||
{
|
||||
|
||||
string arAntiFlag[] = {"ANTI_FEMALE", "ANTI_MALE", "ANTI_MUSA", "ANTI_ASSASSIN", "ANTI_SURA", "ANTI_MUDANG",
|
||||
"ANTI_GET", "ANTI_DROP", "ANTI_SELL", "ANTI_EMPIRE_A", "ANTI_EMPIRE_B", "ANTI_EMPIRE_C",
|
||||
"ANTI_SAVE", "ANTI_GIVE", "ANTI_PKDROP", "ANTI_STACK", "ANTI_MYSHOP", "ANTI_SAFEBOX"};
|
||||
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arAntiFlag)/sizeof(arAntiFlag[0]);i++) {
|
||||
string tempString = arAntiFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "AntiFlag : " << antiFlagStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int get_Item_Flag_Value(string inputString)
|
||||
{
|
||||
|
||||
string arFlag[] = {"ITEM_TUNABLE", "ITEM_SAVE", "ITEM_STACKABLE", "COUNT_PER_1GOLD", "ITEM_SLOW_QUERY", "ITEM_UNIQUE",
|
||||
"ITEM_MAKECOUNT", "ITEM_IRREMOVABLE", "CONFIRM_WHEN_USE", "QUEST_USE", "QUEST_USE_MULTIPLE",
|
||||
"QUEST_GIVE", "ITEM_QUEST", "LOG", "STACKABLE", "SLOW_QUERY", "REFINEABLE", "IRREMOVABLE", "ITEM_APPLICABLE"};
|
||||
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arFlag)/sizeof(arFlag[0]);i++) {
|
||||
string tempString = arFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "Flag : " << flagStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int get_Item_WearFlag_Value(string inputString)
|
||||
{
|
||||
|
||||
string arWearrFlag[] = {"WEAR_BODY", "WEAR_HEAD", "WEAR_FOOTS", "WEAR_WRIST", "WEAR_WEAPON", "WEAR_NECK", "WEAR_EAR", "WEAR_SHIELD", "WEAR_UNIQUE",
|
||||
"WEAR_ARROW", "WEAR_HAIR", "WEAR_ABILITY"};
|
||||
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arWearrFlag)/sizeof(arWearrFlag[0]);i++) {
|
||||
string tempString = arWearrFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "WearFlag : " << wearFlagStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int get_Item_Immune_Value(string inputString)
|
||||
{
|
||||
|
||||
string arImmune[] = {"PARA","CURSE","STUN","SLEEP","SLOW","POISON","TERROR"};
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, "|"); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arImmune)/sizeof(arImmune[0]);i++) {
|
||||
string tempString = arImmune[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "Immune : " << immuneStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int get_Item_LimitType_Value(string inputString)
|
||||
{
|
||||
string arLimitType[] = {"LIMIT_NONE", "LEVEL", "STR", "DEX", "INT", "CON", "PC_BANG", "REAL_TIME", "REAL_TIME_FIRST_USE", "TIMER_BASED_ON_WEAR"};
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "LimitType : " << limitTypeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arLimitType)/sizeof(arLimitType[0]);j++) {
|
||||
string tempString = arLimitType[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
|
||||
int get_Item_ApplyType_Value(string inputString)
|
||||
{
|
||||
string arApplyType[] = {"APPLY_NONE", "APPLY_MAX_HP", "APPLY_MAX_SP", "APPLY_CON", "APPLY_INT", "APPLY_STR", "APPLY_DEX", "APPLY_ATT_SPEED",
|
||||
"APPLY_MOV_SPEED", "APPLY_CAST_SPEED", "APPLY_HP_REGEN", "APPLY_SP_REGEN", "APPLY_POISON_PCT", "APPLY_STUN_PCT",
|
||||
"APPLY_SLOW_PCT", "APPLY_CRITICAL_PCT", "APPLY_PENETRATE_PCT", "APPLY_ATTBONUS_HUMAN", "APPLY_ATTBONUS_ANIMAL",
|
||||
"APPLY_ATTBONUS_ORC", "APPLY_ATTBONUS_MILGYO", "APPLY_ATTBONUS_UNDEAD", "APPLY_ATTBONUS_DEVIL", "APPLY_STEAL_HP",
|
||||
"APPLY_STEAL_SP", "APPLY_MANA_BURN_PCT", "APPLY_DAMAGE_SP_RECOVER", "APPLY_BLOCK", "APPLY_DODGE", "APPLY_RESIST_SWORD",
|
||||
"APPLY_RESIST_TWOHAND", "APPLY_RESIST_DAGGER", "APPLY_RESIST_BELL", "APPLY_RESIST_FAN", "APPLY_RESIST_BOW", "APPLY_RESIST_FIRE",
|
||||
"APPLY_RESIST_ELEC", "APPLY_RESIST_MAGIC", "APPLY_RESIST_WIND", "APPLY_REFLECT_MELEE", "APPLY_REFLECT_CURSE", "APPLY_POISON_REDUCE",
|
||||
"APPLY_KILL_SP_RECOVER", "APPLY_EXP_DOUBLE_BONUS", "APPLY_GOLD_DOUBLE_BONUS", "APPLY_ITEM_DROP_BONUS", "APPLY_POTION_BONUS",
|
||||
"APPLY_KILL_HP_RECOVER", "APPLY_IMMUNE_STUN", "APPLY_IMMUNE_SLOW", "APPLY_IMMUNE_FALL", "APPLY_SKILL", "APPLY_BOW_DISTANCE",
|
||||
"APPLY_ATT_GRADE_BONUS", "APPLY_DEF_GRADE_BONUS", "APPLY_MAGIC_ATT_GRADE", "APPLY_MAGIC_DEF_GRADE", "APPLY_CURSE_PCT",
|
||||
"APPLY_MAX_STAMINA", "APPLY_ATTBONUS_WARRIOR", "APPLY_ATTBONUS_ASSASSIN", "APPLY_ATTBONUS_SURA", "APPLY_ATTBONUS_SHAMAN",
|
||||
"APPLY_ATTBONUS_MONSTER", "APPLY_MALL_ATTBONUS", "APPLY_MALL_DEFBONUS", "APPLY_MALL_EXPBONUS", "APPLY_MALL_ITEMBONUS",
|
||||
"APPLY_MALL_GOLDBONUS", "APPLY_MAX_HP_PCT", "APPLY_MAX_SP_PCT", "APPLY_SKILL_DAMAGE_BONUS", "APPLY_NORMAL_HIT_DAMAGE_BONUS",
|
||||
"APPLY_SKILL_DEFEND_BONUS", "APPLY_NORMAL_HIT_DEFEND_BONUS", "APPLY_PC_BANG_EXP_BONUS", "APPLY_PC_BANG_DROP_BONUS",
|
||||
"APPLY_EXTRACT_HP_PCT", "APPLY_RESIST_WARRIOR", "APPLY_RESIST_ASSASSIN", "APPLY_RESIST_SURA", "APPLY_RESIST_SHAMAN",
|
||||
"APPLY_ENERGY", "APPLY_DEF_GRADE", "APPLY_COSTUME_ATTR_BONUS", "APPLY_MAGIC_ATTBONUS_PER", "APPLY_MELEE_MAGIC_ATTBONUS_PER",
|
||||
"APPLY_RESIST_ICE", "APPLY_RESIST_EARTH", "APPLY_RESIST_DARK", "APPLY_ANTI_CRITICAL_PCT", "APPLY_ANTI_PENETRATE_PCT",
|
||||
};
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "ApplyType : " << applyTypeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arApplyType)/sizeof(arApplyType[0]);j++) {
|
||||
string tempString = arApplyType[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//몬스터 프로토도 읽는다.
|
||||
|
||||
|
||||
int get_Mob_Rank_Value(string inputString)
|
||||
{
|
||||
string arRank[] = {"PAWN", "S_PAWN", "KNIGHT", "S_KNIGHT", "BOSS", "KING"};
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "Rank : " << rankStr << " -> ";
|
||||
for (int j=0;j<sizeof(arRank)/sizeof(arRank[0]);j++) {
|
||||
string tempString = arRank[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
|
||||
int get_Mob_Type_Value(string inputString)
|
||||
{
|
||||
string arType[] = { "MONSTER", "NPC", "STONE", "WARP", "DOOR", "BUILDING", "PC", "POLYMORPH_PC", "HORSE", "GOTO"};
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "Type : " << typeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arType)/sizeof(arType[0]);j++) {
|
||||
string tempString = arType[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
int get_Mob_BattleType_Value(string inputString)
|
||||
{
|
||||
string arBattleType[] = { "MELEE", "RANGE", "MAGIC", "SPECIAL", "POWER", "TANKER", "SUPER_POWER", "SUPER_TANKER"};
|
||||
|
||||
int retInt = -1;
|
||||
//cout << "Battle Type : " << battleTypeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arBattleType)/sizeof(arBattleType[0]);j++) {
|
||||
string tempString = arBattleType[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
int get_Mob_Size_Value(string inputString)
|
||||
{
|
||||
string arSize[] = { "SAMLL", "MEDIUM", "BIG"};
|
||||
|
||||
int retInt = 0;
|
||||
//cout << "Size : " << sizeStr << " -> ";
|
||||
for (int j=0;j<sizeof(arSize)/sizeof(arSize[0]);j++) {
|
||||
string tempString = arSize[j];
|
||||
string tempInputString = trim(inputString);
|
||||
if (tempInputString.compare(tempString)==0)
|
||||
{
|
||||
//cout << j << " ";
|
||||
retInt = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//cout << endl;
|
||||
|
||||
return retInt;
|
||||
}
|
||||
|
||||
int get_Mob_AIFlag_Value(string inputString)
|
||||
{
|
||||
string arAIFlag[] = {"AGGR","NOMOVE","COWARD","NOATTSHINSU","NOATTCHUNJO","NOATTJINNO","ATTMOB","BERSERK","STONESKIN","GODSPEED","DEATHBLOW","REVIVE"};
|
||||
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arAIFlag)/sizeof(arAIFlag[0]);i++) {
|
||||
string tempString = arAIFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "AIFlag : " << aiFlagStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
int get_Mob_RaceFlag_Value(string inputString)
|
||||
{
|
||||
string arRaceFlag[] = {"ANIMAL","UNDEAD","DEVIL","HUMAN","ORC","MILGYO","INSECT","FIRE","ICE","DESERT","TREE",
|
||||
"ATT_ELEC","ATT_FIRE","ATT_ICE","ATT_WIND","ATT_EARTH","ATT_DARK"};
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arRaceFlag)/sizeof(arRaceFlag[0]);i++) {
|
||||
string tempString = arRaceFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "Race Flag : " << raceFlagStr << " -> " << retValue << endl;
|
||||
|
||||
return retValue;
|
||||
}
|
||||
int get_Mob_ImmuneFlag_Value(string inputString)
|
||||
{
|
||||
string arImmuneFlag[] = {"STUN","SLOW","FALL","CURSE","POISON","TERROR", "REFLECT"};
|
||||
|
||||
int retValue = 0;
|
||||
string* arInputString = StringSplit(inputString, ","); //프로토 정보 내용을 단어별로 쪼갠 배열.
|
||||
for(int i =0;i<sizeof(arImmuneFlag)/sizeof(arImmuneFlag[0]);i++) {
|
||||
string tempString = arImmuneFlag[i];
|
||||
for (int j=0; j<30 ; j++) //최대 30개 단어까지. (하드코딩)
|
||||
{
|
||||
string tempString2 = arInputString[j];
|
||||
if (tempString2.compare(tempString)==0) { //일치하는지 확인.
|
||||
retValue = retValue + pow((float)2,(float)i);
|
||||
}
|
||||
|
||||
if(tempString2.compare("") == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete []arInputString;
|
||||
//cout << "Immune Flag : " << immuneFlagStr << " -> " << retValue << endl;
|
||||
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
|
||||
#ifndef __DUMP_PROTO__
|
||||
|
||||
//몹 테이블을 셋팅해준다.
|
||||
bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable,std::map<int,const char*> &nameMap)
|
||||
{
|
||||
int col = 0;
|
||||
str_to_number(mobTable->dwVnum, csvTable.AsStringByIndex(col++));
|
||||
strlcpy(mobTable->szName, csvTable.AsStringByIndex(col++), sizeof(mobTable->szName));
|
||||
|
||||
//3. 지역별 이름 넣어주기.
|
||||
map<int,const char*>::iterator it;
|
||||
it = nameMap.find(mobTable->dwVnum);
|
||||
if (it != nameMap.end()) {
|
||||
const char * localeName = it->second;
|
||||
strlcpy(mobTable->szLocaleName, localeName, sizeof (mobTable->szLocaleName));
|
||||
} else {
|
||||
strlcpy(mobTable->szLocaleName, mobTable->szName, sizeof (mobTable->szLocaleName));
|
||||
}
|
||||
|
||||
//RANK
|
||||
int rankValue = get_Mob_Rank_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->bRank = rankValue;
|
||||
//TYPE
|
||||
int typeValue = get_Mob_Type_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->bType = typeValue;
|
||||
//BATTLE_TYPE
|
||||
int battleTypeValue = get_Mob_BattleType_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->bBattleType = battleTypeValue;
|
||||
|
||||
str_to_number(mobTable->bLevel, csvTable.AsStringByIndex(col++));
|
||||
//SIZE
|
||||
int sizeValue = get_Mob_Size_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->bSize = sizeValue;
|
||||
//AI_FLAG
|
||||
int aiFlagValue = get_Mob_AIFlag_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->dwAIFlag = aiFlagValue;
|
||||
//mount_capacity;
|
||||
col++;
|
||||
//RACE_FLAG
|
||||
int raceFlagValue = get_Mob_RaceFlag_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->dwRaceFlag = raceFlagValue;
|
||||
//IMMUNE_FLAG
|
||||
int immuneFlagValue = get_Mob_ImmuneFlag_Value(csvTable.AsStringByIndex(col++));
|
||||
mobTable->dwImmuneFlag = immuneFlagValue;
|
||||
|
||||
str_to_number(mobTable->bEmpire, csvTable.AsStringByIndex(col++)); //col = 11
|
||||
|
||||
strlcpy(mobTable->szFolder, csvTable.AsStringByIndex(col++), sizeof(mobTable->szFolder));
|
||||
|
||||
str_to_number(mobTable->bOnClickType, csvTable.AsStringByIndex(col++));
|
||||
|
||||
str_to_number(mobTable->bStr, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bDex, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bCon, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bInt, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwDamageRange[0], csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwDamageRange[1], csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwMaxHP, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bRegenCycle, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bRegenPercent, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwGoldMin, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwGoldMax, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwExp, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->wDef, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->sAttackSpeed, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->sMovingSpeed, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bAggresiveHPPct, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->wAggressiveSight, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->wAttackRange, csvTable.AsStringByIndex(col++));
|
||||
|
||||
str_to_number(mobTable->dwDropItemVnum, csvTable.AsStringByIndex(col++)); //32
|
||||
str_to_number(mobTable->dwResurrectionVnum, csvTable.AsStringByIndex(col++));
|
||||
for (int i = 0; i < MOB_ENCHANTS_MAX_NUM; ++i)
|
||||
str_to_number(mobTable->cEnchants[i], csvTable.AsStringByIndex(col++));
|
||||
|
||||
for (int i = 0; i < MOB_RESISTS_MAX_NUM; ++i)
|
||||
str_to_number(mobTable->cResists[i], csvTable.AsStringByIndex(col++));
|
||||
|
||||
str_to_number(mobTable->fDamMultiply, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwSummonVnum, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->dwDrainSP, csvTable.AsStringByIndex(col++));
|
||||
|
||||
//Mob_Color
|
||||
++col;
|
||||
|
||||
str_to_number(mobTable->dwPolymorphItemVnum, csvTable.AsStringByIndex(col++));
|
||||
|
||||
str_to_number(mobTable->Skills[0].bLevel, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[0].dwVnum, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[1].bLevel, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[1].dwVnum, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[2].bLevel, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[2].dwVnum, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[3].bLevel, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[3].dwVnum, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[4].bLevel, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->Skills[4].dwVnum, csvTable.AsStringByIndex(col++));
|
||||
|
||||
str_to_number(mobTable->bBerserkPoint, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bStoneSkinPoint, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bGodSpeedPoint, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bDeathBlowPoint, csvTable.AsStringByIndex(col++));
|
||||
str_to_number(mobTable->bRevivePoint, csvTable.AsStringByIndex(col++));
|
||||
|
||||
sys_log(0, "MOB #%-5d %-24s level: %-3u rank: %u empire: %d", mobTable->dwVnum, mobTable->szLocaleName, mobTable->bLevel, mobTable->bRank, mobTable->bEmpire);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<int,const char*> &nameMap)
|
||||
{
|
||||
int col = 0;
|
||||
|
||||
int dataArray[33];
|
||||
for (int i=0; i<sizeof(dataArray)/sizeof(dataArray[0]);i++) {
|
||||
int validCheck = 0;
|
||||
if (i==2) {
|
||||
dataArray[i] = get_Item_Type_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==3) {
|
||||
dataArray[i] = get_Item_SubType_Value(dataArray[i-1], csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==5) {
|
||||
dataArray[i] = get_Item_AntiFlag_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==6) {
|
||||
dataArray[i] = get_Item_Flag_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==7) {
|
||||
dataArray[i] = get_Item_WearFlag_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==8) {
|
||||
dataArray[i] = get_Item_Immune_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==14) {
|
||||
dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==16) {
|
||||
dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==18) {
|
||||
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==20) {
|
||||
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else if (i==22) {
|
||||
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
|
||||
validCheck = dataArray[i];
|
||||
} else {
|
||||
str_to_number(dataArray[i], csvTable.AsStringByIndex(col));
|
||||
}
|
||||
|
||||
if (validCheck == -1)
|
||||
{
|
||||
std::ostringstream dataStream;
|
||||
|
||||
for (int j = 0; j < i; ++j)
|
||||
dataStream << dataArray[j] << ",";
|
||||
|
||||
//fprintf(stderr, "ItemProto Reading Failed : Invalid value.\n");
|
||||
sys_err("ItemProto Reading Failed : Invalid value. (index: %d, col: %d, value: %s)", i, col, csvTable.AsStringByIndex(col));
|
||||
sys_err("\t%d ~ %d Values: %s", 0, i, dataStream.str().c_str());
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
col = col + 1;
|
||||
}
|
||||
|
||||
// vnum 및 vnum range 읽기.
|
||||
{
|
||||
std::string s(csvTable.AsStringByIndex(0));
|
||||
int pos = s.find("~");
|
||||
// vnum 필드에 '~'가 없다면 패스
|
||||
if (std::string::npos == pos)
|
||||
{
|
||||
itemTable->dwVnum = dataArray[0];
|
||||
itemTable->dwVnumRange = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string s_start_vnum (s.substr(0, pos));
|
||||
std::string s_end_vnum (s.substr(pos +1 ));
|
||||
|
||||
int start_vnum = atoi(s_start_vnum.c_str());
|
||||
int end_vnum = atoi(s_end_vnum.c_str());
|
||||
if (0 == start_vnum || (0 != end_vnum && end_vnum < start_vnum))
|
||||
{
|
||||
sys_err ("INVALID VNUM %s", s.c_str());
|
||||
return false;
|
||||
}
|
||||
itemTable->dwVnum = start_vnum;
|
||||
itemTable->dwVnumRange = end_vnum - start_vnum;
|
||||
}
|
||||
}
|
||||
|
||||
strlcpy(itemTable->szName, csvTable.AsStringByIndex(1), sizeof(itemTable->szName));
|
||||
//지역별 이름 넣어주기.
|
||||
map<int,const char*>::iterator it;
|
||||
it = nameMap.find(itemTable->dwVnum);
|
||||
if (it != nameMap.end()) {
|
||||
const char * localeName = it->second;
|
||||
strlcpy(itemTable->szLocaleName, localeName, sizeof (itemTable->szLocaleName));
|
||||
} else {
|
||||
strlcpy(itemTable->szLocaleName, itemTable->szName, sizeof (itemTable->szLocaleName));
|
||||
}
|
||||
itemTable->bType = dataArray[2];
|
||||
itemTable->bSubType = dataArray[3];
|
||||
itemTable->bSize = dataArray[4];
|
||||
itemTable->dwAntiFlags = dataArray[5];
|
||||
itemTable->dwFlags = dataArray[6];
|
||||
itemTable->dwWearFlags = dataArray[7];
|
||||
itemTable->dwImmuneFlag = dataArray[8];
|
||||
itemTable->dwGold = dataArray[9];
|
||||
itemTable->dwShopBuyPrice = dataArray[10];
|
||||
itemTable->dwRefinedVnum = dataArray[11];
|
||||
itemTable->wRefineSet = dataArray[12];
|
||||
itemTable->bAlterToMagicItemPct = dataArray[13];
|
||||
itemTable->cLimitRealTimeFirstUseIndex = -1;
|
||||
itemTable->cLimitTimerBasedOnWearIndex = -1;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
|
||||
{
|
||||
itemTable->aLimits[i].bType = dataArray[14+i*2];
|
||||
itemTable->aLimits[i].lValue = dataArray[15+i*2];
|
||||
|
||||
if (LIMIT_REAL_TIME_START_FIRST_USE == itemTable->aLimits[i].bType)
|
||||
itemTable->cLimitRealTimeFirstUseIndex = (char)i;
|
||||
|
||||
if (LIMIT_TIMER_BASED_ON_WEAR == itemTable->aLimits[i].bType)
|
||||
itemTable->cLimitTimerBasedOnWearIndex = (char)i;
|
||||
|
||||
}
|
||||
|
||||
for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
|
||||
{
|
||||
itemTable->aApplies[i].bType = dataArray[18+i*2];
|
||||
itemTable->aApplies[i].lValue = dataArray[19+i*2];
|
||||
}
|
||||
|
||||
for (i = 0; i < ITEM_VALUES_MAX_NUM; ++i)
|
||||
itemTable->alValues[i] = dataArray[24+i];
|
||||
|
||||
//column for 'Specular'
|
||||
itemTable->bGainSocketPct = dataArray[31];
|
||||
itemTable->sAddonType = dataArray[32];
|
||||
|
||||
//test
|
||||
str_to_number(itemTable->bWeight, "0");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef __Item_CSV_READER_H__
|
||||
#define __Item_CSV_READER_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include "CsvReader.h"
|
||||
|
||||
//csv 파일을 읽어와서 아이템 테이블에 넣어준다.
|
||||
void putItemIntoTable(); //(테이블, 테스트여부)
|
||||
|
||||
int get_Item_Type_Value(std::string inputString);
|
||||
int get_Item_SubType_Value(int type_value, std::string inputString);
|
||||
int get_Item_AntiFlag_Value(std::string inputString);
|
||||
int get_Item_Flag_Value(std::string inputString);
|
||||
int get_Item_WearFlag_Value(std::string inputString);
|
||||
int get_Item_Immune_Value(std::string inputString);
|
||||
int get_Item_LimitType_Value(std::string inputString);
|
||||
int get_Item_ApplyType_Value(std::string inputString);
|
||||
|
||||
|
||||
//몬스터 프로토도 읽을 수 있다.
|
||||
int get_Mob_Rank_Value(std::string inputString);
|
||||
int get_Mob_Type_Value(std::string inputString);
|
||||
int get_Mob_BattleType_Value(std::string inputString);
|
||||
|
||||
int get_Mob_Size_Value(std::string inputString);
|
||||
int get_Mob_AIFlag_Value(std::string inputString);
|
||||
int get_Mob_RaceFlag_Value(std::string inputString);
|
||||
int get_Mob_ImmuneFlag_Value(std::string inputString);
|
||||
|
||||
//
|
||||
bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable, std::map<int,const char*> &nameMap);
|
||||
bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<int,const char*> &nameMap);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef __INC_METIN_II_DB_QID_H__
|
||||
#define __INC_METIN_II_DB_QID_H__
|
||||
|
||||
/**
|
||||
* @version 05/06/10 Bang2ni - 아이템 가격정보 쿼리 추가(QID_ITEMPRICE_XXX)
|
||||
*/
|
||||
enum QID
|
||||
{
|
||||
QID_PLAYER, // 0
|
||||
QID_ITEM, // 1
|
||||
QID_QUEST, // 2
|
||||
QID_AFFECT, // 3
|
||||
QID_LOGIN, // 4
|
||||
QID_SAFEBOX_LOAD, // 5
|
||||
QID_SAFEBOX_CHANGE_SIZE, // 6
|
||||
QID_SAFEBOX_CHANGE_PASSWORD, // 7
|
||||
QID_SAFEBOX_CHANGE_PASSWORD_SECOND, // 8
|
||||
QID_SAFEBOX_SAVE, // 9
|
||||
QID_ITEM_SAVE, // 10
|
||||
QID_ITEM_DESTROY, // 11
|
||||
QID_QUEST_SAVE, // 12
|
||||
QID_PLAYER_SAVE, // 13
|
||||
QID_HIGHSCORE_REGISTER, // 14
|
||||
QID_PLAYER_DELETE, // 15
|
||||
QID_LOGIN_BY_KEY, // 16
|
||||
QID_PLAYER_INDEX_CREATE, // 17
|
||||
QID_ITEM_AWARD_LOAD, // 18
|
||||
QID_ITEM_AWARD_TAKEN, // 19
|
||||
QID_GUILD_RANKING, // 20
|
||||
|
||||
// MYSHOP_PRICE_LIST
|
||||
QID_ITEMPRICE_SAVE, ///< 21, 아이템 가격정보 저장 쿼리
|
||||
QID_ITEMPRICE_DESTROY, ///< 22, 아이템 가격정보 삭제 쿼리
|
||||
QID_ITEMPRICE_LOAD_FOR_UPDATE, ///< 23, 가격정보 업데이트를 위한 아이템 가격정보 로드 쿼리
|
||||
QID_ITEMPRICE_LOAD, ///< 24, 아이템 가격정보 로드 쿼리
|
||||
// END_OF_MYSHOP_PRICE_LIST
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,130 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "../../libthecore/include/memcpy.h"
|
||||
#include "../../common/stl.h"
|
||||
#include "grid.h"
|
||||
|
||||
CGrid::CGrid(int w, int h) : m_iWidth(w), m_iHeight(h)
|
||||
{
|
||||
m_pGrid = new char[m_iWidth * m_iHeight];
|
||||
memset(m_pGrid, 0, sizeof(char) * m_iWidth * m_iHeight);
|
||||
}
|
||||
|
||||
CGrid::CGrid(CGrid * pkGrid, int w, int h) : m_iWidth(w), m_iHeight(h)
|
||||
{
|
||||
m_pGrid = new char[m_iWidth * m_iHeight];
|
||||
int iSize = std::MIN(w * h, pkGrid->m_iWidth * pkGrid->m_iHeight);
|
||||
thecore_memcpy(m_pGrid, pkGrid->m_pGrid, sizeof(char) * iSize);
|
||||
}
|
||||
|
||||
CGrid::~CGrid()
|
||||
{
|
||||
delete [] m_pGrid;
|
||||
}
|
||||
|
||||
void CGrid::Clear()
|
||||
{
|
||||
memset(m_pGrid, 0, sizeof(char) * m_iWidth * m_iHeight);
|
||||
}
|
||||
|
||||
int CGrid::FindBlank(int w, int h)
|
||||
{
|
||||
// 크기가 더 크다면 확인할 필요 없이 그냥 리턴
|
||||
if (w > m_iWidth || h > m_iHeight)
|
||||
return -1;
|
||||
|
||||
int iRow;
|
||||
|
||||
for (iRow = 0; iRow < m_iHeight; ++iRow)
|
||||
{
|
||||
for (int iCol = 0; iCol < m_iWidth; ++iCol)
|
||||
{
|
||||
int iIndex = iRow * m_iWidth + iCol;
|
||||
|
||||
if (IsEmpty(iIndex, w, h))
|
||||
return iIndex;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool CGrid::Put(int iPos, int w, int h)
|
||||
{
|
||||
if (!IsEmpty(iPos, w, h))
|
||||
return false;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
int iStart = iPos + (y * m_iWidth);
|
||||
m_pGrid[iStart] = true;
|
||||
|
||||
int x = 1;
|
||||
while (x < w)
|
||||
m_pGrid[iStart + x++] = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGrid::Get(int iPos, int w, int h)
|
||||
{
|
||||
if (iPos >= m_iWidth * m_iHeight)
|
||||
return;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
int iStart = iPos + (y * m_iWidth);
|
||||
m_pGrid[iStart] = false;
|
||||
|
||||
int x = 1;
|
||||
while (x < w)
|
||||
m_pGrid[iStart + x++] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CGrid::IsEmpty(int iPos, int w, int h)
|
||||
{
|
||||
int iRow = iPos / m_iWidth;
|
||||
|
||||
// Grid 안쪽인가를 먼저 검사
|
||||
if (iRow + h > m_iHeight)
|
||||
return false;
|
||||
|
||||
if (iPos + w > iRow * m_iWidth + m_iWidth)
|
||||
return false;
|
||||
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
int iStart = iPos + (y * m_iWidth);
|
||||
|
||||
if (m_pGrid[iStart])
|
||||
return false;
|
||||
|
||||
int x = 1;
|
||||
while (x < w)
|
||||
if (m_pGrid[iStart + x++])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGrid::Print()
|
||||
{
|
||||
printf("Grid %p\n", this);
|
||||
|
||||
for (int y = 0; y < m_iHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < m_iWidth; ++x)
|
||||
printf("%d", m_pGrid[y * m_iWidth + x]);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CGrid::GetSize()
|
||||
{
|
||||
return m_iWidth * m_iHeight;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// vim: ts=8 sw=4
|
||||
#ifndef __INC_METIN_II_GRID_H__
|
||||
#define __INC_METIN_II_GRID_H__
|
||||
|
||||
class CGrid
|
||||
{
|
||||
public:
|
||||
CGrid(int w, int h);
|
||||
CGrid(CGrid * pkGrid, int w, int h);
|
||||
~CGrid();
|
||||
|
||||
void Clear();
|
||||
int FindBlank(int w, int h);
|
||||
bool IsEmpty(int iPos, int w, int h);
|
||||
bool Put(int iPos, int w, int h);
|
||||
void Get(int iPos, int w, int h);
|
||||
void Print();
|
||||
unsigned int GetSize();
|
||||
|
||||
protected:
|
||||
int m_iWidth;
|
||||
int m_iHeight;
|
||||
|
||||
char * m_pGrid;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef __INC_METiN_II_DBSERV_STDAFX_H__
|
||||
#define __INC_METiN_II_DBSERV_STDAFX_H__
|
||||
|
||||
#include "../../libthecore/include/stdafx.h"
|
||||
|
||||
#ifndef __WIN32__
|
||||
#include <semaphore.h>
|
||||
#else
|
||||
#define isdigit iswdigit
|
||||
#define isspace iswspace
|
||||
#endif
|
||||
|
||||
#include "../../common/length.h"
|
||||
#include "../../common/tables.h"
|
||||
#include "../../common/singleton.h"
|
||||
#include "../../common/utils.h"
|
||||
#include "../../common/stl.h"
|
||||
#include "../../common/service.h"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void WriteVersion()
|
||||
{
|
||||
#ifndef __WIN32__
|
||||
FILE* fp(fopen("VERSION.txt", "w"));
|
||||
|
||||
if (NULL != fp)
|
||||
{
|
||||
fprintf(fp, "game perforce revision: %s\n", __P4_VERSION__);
|
||||
fprintf(fp, "%s@%s:%s\n", __USER__, __HOSTNAME__, __PWD__);
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "cannot open VERSION.txt\n");
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(game CXX)
|
||||
|
||||
file(GLOB_RECURSE sources
|
||||
src/*.cpp src/*.h
|
||||
)
|
||||
|
||||
include_directories(${PROJECT_BINARY_DIR}/src/system/)
|
||||
|
||||
include_directories(src/)
|
||||
|
||||
# Find dependencies
|
||||
find_package(libmysql REQUIRED)
|
||||
find_package(Boost COMPONENTS system REQUIRED)
|
||||
find_package(DevIL REQUIRED)
|
||||
find_package(LZO REQUIRED)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${sources})
|
||||
|
||||
# Link dependencies if found
|
||||
if (libmysql_FOUND)
|
||||
target_link_libraries (${PROJECT_NAME} ${MYSQL_LIBRARIES})
|
||||
endif (libmysql_FOUND)
|
||||
|
||||
if (Boost_FOUND)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries (${PROJECT_NAME} ${Boost_LIBRARIES} ${Boost_SYSTEM_LIBRARY})
|
||||
endif (Boost_FOUND)
|
||||
|
||||
if (IL_FOUND)
|
||||
include_directories(${IL_INCLUDE_DIR})
|
||||
target_link_libraries (${PROJECT_NAME} ${IL_LIBRARIES})
|
||||
endif (IL_FOUND)
|
||||
|
||||
if (LZO_FOUND)
|
||||
include_directories(${LZO_INCLUDE_DIR})
|
||||
target_link_libraries (${PROJECT_NAME} ${LZO_LIBRARIES})
|
||||
endif (LZO_FOUND)
|
||||
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} md)
|
||||
|
||||
# Pthreads
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package (Threads REQUIRED)
|
||||
target_link_libraries (${PROJECT_NAME} Threads::Threads)
|
||||
|
||||
find_package(GTest REQUIRED)
|
||||
if (GTEST_FOUND)
|
||||
include_directories(${GTEST_INCLUDE_DIRS})
|
||||
target_link_libraries (${PROJECT_NAME} ${GTEST_BOTH_LIBRARIES})
|
||||
endif (GTEST_FOUND)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} libgame libpoly libsql libthecore liblua)
|
||||
|
|
@ -0,0 +1,335 @@
|
|||
#include "stdafx.h"
|
||||
#include "constants.h"
|
||||
#include "BattleArena.h"
|
||||
#include "start_position.h"
|
||||
#include "char_manager.h"
|
||||
#include "char.h"
|
||||
#include "sectree_manager.h"
|
||||
#include "regen.h"
|
||||
#include "questmanager.h"
|
||||
|
||||
extern int passes_per_sec;
|
||||
extern int test_server;
|
||||
|
||||
CBattleArena::CBattleArena()
|
||||
: m_pEvent(NULL),
|
||||
m_status(STATUS_CLOSE),
|
||||
m_nMapIndex(0),
|
||||
m_nEmpire(0),
|
||||
m_bForceEnd(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool CBattleArena::IsRunning()
|
||||
{
|
||||
return m_status == STATUS_CLOSE ? false : true;
|
||||
}
|
||||
|
||||
bool CBattleArena::IsBattleArenaMap(int nMapIndex)
|
||||
{
|
||||
if ( nMapIndex == nBATTLE_ARENA_MAP[1] ||
|
||||
nMapIndex == nBATTLE_ARENA_MAP[2] ||
|
||||
nMapIndex == nBATTLE_ARENA_MAP[3] )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct FWarpToHome
|
||||
{
|
||||
void operator() (LPENTITY ent)
|
||||
{
|
||||
if ( ent->IsType(ENTITY_CHARACTER) == true )
|
||||
{
|
||||
LPCHARACTER lpChar = (LPCHARACTER)ent;
|
||||
|
||||
if ( lpChar->IsPC() == true )
|
||||
{
|
||||
if ( !test_server )
|
||||
{
|
||||
if ( lpChar->GetGMLevel() != GM_PLAYER ) return;
|
||||
}
|
||||
|
||||
int nEmpire, nMapIndex, x, y;
|
||||
|
||||
nEmpire = lpChar->GetEmpire();
|
||||
nMapIndex = EMPIRE_START_MAP(nEmpire);
|
||||
x = EMPIRE_START_X(nEmpire);
|
||||
y = EMPIRE_START_Y(nEmpire);
|
||||
|
||||
lpChar->WarpSet(x, y, nMapIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void CBattleArena::SetStatus(BATTLEARENA_STATUS status)
|
||||
{
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
EVENTINFO(SBattleArenaInfo)
|
||||
{
|
||||
int nEmpire;
|
||||
int nMapIndex;
|
||||
int state;
|
||||
int wait_count;
|
||||
|
||||
SBattleArenaInfo()
|
||||
: nEmpire( 0 )
|
||||
, nMapIndex( 0 )
|
||||
, state( 0 )
|
||||
, wait_count( 0 )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
EVENTFUNC(battle_arena_event)
|
||||
{
|
||||
SBattleArenaInfo * pInfo = dynamic_cast<SBattleArenaInfo *>(event->info );
|
||||
|
||||
if ( pInfo == NULL )
|
||||
{
|
||||
return 0; // cancel immediately
|
||||
}
|
||||
|
||||
if (pInfo != NULL)
|
||||
{
|
||||
switch (pInfo->state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
++pInfo->state;
|
||||
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 5분 남았습니다!!!"));
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*4);
|
||||
|
||||
case 1:
|
||||
{
|
||||
++pInfo->state;
|
||||
BroadcastNotice(LC_TEXT("몬스터들의 공격까지 1분 남았습니다!!!"));
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(10) : PASSES_PER_SEC(60);
|
||||
|
||||
case 2:
|
||||
{
|
||||
++pInfo->state;
|
||||
pInfo->wait_count = 0;
|
||||
|
||||
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0);
|
||||
BroadcastNotice(LC_TEXT("몬스터들이 성을 공격하기 시작했습니다."));
|
||||
|
||||
LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex);
|
||||
|
||||
if ( sectree != NULL )
|
||||
{
|
||||
std::string strMap = LocaleService_GetMapPath();
|
||||
if ( pInfo->nEmpire > 0 )
|
||||
{
|
||||
strMap += strRegen[pInfo->nEmpire];
|
||||
|
||||
regen_do(strMap.c_str(), pInfo->nMapIndex, sectree->m_setting.iBaseX, sectree->m_setting.iBaseY, NULL, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(60*1) : PASSES_PER_SEC(60*5);
|
||||
|
||||
case 3 :
|
||||
{
|
||||
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
|
||||
{
|
||||
pInfo->state = 6;
|
||||
SendNoticeMap(LC_TEXT("중앙 제단에 악의 기운이 모여듭니다."), pInfo->nMapIndex, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
pInfo->wait_count++;
|
||||
|
||||
if ( pInfo->wait_count >= 5 )
|
||||
{
|
||||
pInfo->state++;
|
||||
SendNoticeMap(LC_TEXT("몬스터들이 물러갈 조짐을 보입니다."), pInfo->nMapIndex, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
CBattleArena::instance().SpawnRandomStone();
|
||||
}
|
||||
}
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
|
||||
|
||||
case 4 :
|
||||
{
|
||||
pInfo->state++;
|
||||
SendNoticeMap(LC_TEXT("몬스터들이 물러가기 시작했습니다."), pInfo->nMapIndex, false);
|
||||
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
|
||||
|
||||
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
|
||||
}
|
||||
return PASSES_PER_SEC(30);
|
||||
|
||||
case 5 :
|
||||
{
|
||||
LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(pInfo->nMapIndex);
|
||||
|
||||
if ( sectree != NULL )
|
||||
{
|
||||
struct FWarpToHome f;
|
||||
sectree->for_each( f );
|
||||
}
|
||||
|
||||
CBattleArena::instance().End();
|
||||
}
|
||||
return 0;
|
||||
|
||||
case 6 :
|
||||
{
|
||||
pInfo->state++;
|
||||
pInfo->wait_count = 0;
|
||||
|
||||
SendNoticeMap(LC_TEXT("몬스터들의 대장이 나타났습니다."), pInfo->nMapIndex, false);
|
||||
SendNoticeMap(LC_TEXT("30분 내로 귀목령주를 물리쳐주세요."), pInfo->nMapIndex, false);
|
||||
|
||||
CBattleArena::instance().SpawnLastBoss();
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
|
||||
|
||||
case 7 :
|
||||
{
|
||||
if ( SECTREE_MANAGER::instance().GetMonsterCountInMap(pInfo->nMapIndex) <= 0 )
|
||||
{
|
||||
SendNoticeMap(LC_TEXT("귀목령주와 그의 부하들을 모두 물리쳤습니다."), pInfo->nMapIndex, false);
|
||||
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
|
||||
|
||||
pInfo->state = 5;
|
||||
|
||||
return PASSES_PER_SEC(60);
|
||||
}
|
||||
|
||||
pInfo->wait_count++;
|
||||
|
||||
if ( pInfo->wait_count >= 6 )
|
||||
{
|
||||
SendNoticeMap(LC_TEXT("귀목령주가 퇴각하였습니다."), pInfo->nMapIndex, false);
|
||||
SendNoticeMap(LC_TEXT("잠시 후 마을로 돌아갑니다."), pInfo->nMapIndex, false);
|
||||
|
||||
SECTREE_MANAGER::instance().PurgeMonstersInMap(pInfo->nMapIndex);
|
||||
SECTREE_MANAGER::instance().PurgeStonesInMap(pInfo->nMapIndex);
|
||||
|
||||
pInfo->state = 5;
|
||||
|
||||
return PASSES_PER_SEC(60);
|
||||
}
|
||||
else
|
||||
{
|
||||
CBattleArena::instance().SpawnRandomStone();
|
||||
}
|
||||
}
|
||||
return test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(60*5);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CBattleArena::Start(int nEmpire)
|
||||
{
|
||||
if ( m_status != STATUS_CLOSE ) return false;
|
||||
if ( nEmpire < 1 || nEmpire > 3 ) return false;
|
||||
|
||||
m_nMapIndex = nBATTLE_ARENA_MAP[nEmpire];
|
||||
m_nEmpire = nEmpire;
|
||||
|
||||
char szBuf[1024];
|
||||
snprintf(szBuf, sizeof(szBuf), LC_TEXT("%s의 성으로 몬스터들이 진군하고 있습니다."), EMPIRE_NAME(m_nEmpire));
|
||||
BroadcastNotice(szBuf);
|
||||
BroadcastNotice(LC_TEXT("10분 뒤 성을 공격할 예정입니다."));
|
||||
|
||||
if (m_pEvent != NULL) {
|
||||
event_cancel(&m_pEvent);
|
||||
}
|
||||
|
||||
SBattleArenaInfo* info = AllocEventInfo<SBattleArenaInfo>();
|
||||
|
||||
info->nMapIndex = m_nMapIndex;
|
||||
info->nEmpire = m_nEmpire;
|
||||
info->state = 0;
|
||||
info->wait_count = 0;
|
||||
|
||||
m_pEvent = event_create(battle_arena_event, info, test_server ? PASSES_PER_SEC(60) : PASSES_PER_SEC(5*60));
|
||||
|
||||
SetStatus(STATUS_BATTLE);
|
||||
|
||||
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", m_nMapIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBattleArena::End()
|
||||
{
|
||||
if (m_pEvent != NULL) {
|
||||
event_cancel(&m_pEvent);
|
||||
}
|
||||
m_bForceEnd = false;
|
||||
m_nMapIndex = 0;
|
||||
m_nEmpire = 0;
|
||||
m_status = STATUS_CLOSE;
|
||||
|
||||
quest::CQuestManager::instance().RequestSetEventFlag("battle_arena", 0);
|
||||
}
|
||||
|
||||
void CBattleArena::ForceEnd()
|
||||
{
|
||||
if ( m_bForceEnd == true ) return;
|
||||
|
||||
m_bForceEnd = true;
|
||||
|
||||
if (m_pEvent != NULL) {
|
||||
event_cancel(&m_pEvent);
|
||||
}
|
||||
|
||||
SBattleArenaInfo* info = AllocEventInfo<SBattleArenaInfo>();
|
||||
|
||||
info->nMapIndex = m_nMapIndex;
|
||||
info->nEmpire = m_nEmpire;
|
||||
info->state = 3;
|
||||
|
||||
event_create(battle_arena_event, info, PASSES_PER_SEC(5));
|
||||
}
|
||||
|
||||
void CBattleArena::SpawnRandomStone()
|
||||
{
|
||||
static const DWORD vnum[7] = { 8012, 8013, 8014, 8024, 8025, 8026, 8027 };
|
||||
|
||||
static const int region_info[3][4] = {
|
||||
{688900, 247600, 692700, 250000},
|
||||
{740200, 251000, 744200, 247700},
|
||||
{791400, 250900, 795900, 250400} };
|
||||
|
||||
int idx = m_nMapIndex - 190;
|
||||
if ( idx < 0 || idx >= 3 ) return;
|
||||
|
||||
CHARACTER_MANAGER::instance().SpawnMobRange(
|
||||
vnum[number(0, 6)],
|
||||
m_nMapIndex,
|
||||
region_info[idx][0], region_info[idx][1], region_info[idx][2], region_info[idx][3],
|
||||
false, true);
|
||||
}
|
||||
|
||||
void CBattleArena::SpawnLastBoss()
|
||||
{
|
||||
static const DWORD vnum = 8616;
|
||||
|
||||
static const int position[3][2] = {
|
||||
{ 691000, 248900 },
|
||||
{ 742300, 249000 },
|
||||
{ 793600, 249300 } };
|
||||
|
||||
int idx = m_nMapIndex - 190;
|
||||
if ( idx < 0 || idx >= 3 ) return;
|
||||
|
||||
CHARACTER_MANAGER::instance().SpawnMob(vnum, m_nMapIndex, position[idx][0], position[idx][1], 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
const static int nBATTLE_ARENA_MAP[] = { 0, 190, 191, 192 };
|
||||
const static std::string strRegen[] =
|
||||
{
|
||||
"",
|
||||
"/metin2_map_battlearena01/regen00.txt",
|
||||
"/metin2_map_battlearena02/regen00.txt",
|
||||
"/metin2_map_battlearena03/regen00.txt",
|
||||
};
|
||||
|
||||
enum BATTLEARENA_STATUS
|
||||
{
|
||||
STATUS_CLOSE = 0,
|
||||
STATUS_BATTLE,
|
||||
STATUS_END,
|
||||
};
|
||||
|
||||
class CBattleArena : public singleton<CBattleArena>
|
||||
{
|
||||
private :
|
||||
LPEVENT m_pEvent;
|
||||
BATTLEARENA_STATUS m_status;
|
||||
int m_nMapIndex;
|
||||
int m_nEmpire;
|
||||
bool m_bForceEnd;
|
||||
|
||||
public :
|
||||
CBattleArena();
|
||||
|
||||
static bool IsBattleArenaMap(int nMapIndex);
|
||||
|
||||
bool IsRunning();
|
||||
void SetStatus(BATTLEARENA_STATUS status);
|
||||
|
||||
bool Start(int nEmpire);
|
||||
void ForceEnd();
|
||||
void End();
|
||||
|
||||
void SpawnLastBoss();
|
||||
void SpawnRandomStone();
|
||||
};
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "BlueDragon.h"
|
||||
|
||||
extern int test_server;
|
||||
extern int passes_per_sec;
|
||||
|
||||
#include "vector.h"
|
||||
#include "utils.h"
|
||||
#include "char.h"
|
||||
#include "mob_manager.h"
|
||||
#include "sectree_manager.h"
|
||||
#include "battle.h"
|
||||
#include "affect.h"
|
||||
#include "BlueDragon_Binder.h"
|
||||
#include "BlueDragon_Skill.h"
|
||||
#include "packet.h"
|
||||
#include "motion.h"
|
||||
|
||||
time_t UseBlueDragonSkill(LPCHARACTER pChar, unsigned int idx)
|
||||
{
|
||||
LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap( pChar->GetMapIndex() );
|
||||
|
||||
if (NULL == pSecMap)
|
||||
return 0;
|
||||
|
||||
int nextUsingTime = 0;
|
||||
|
||||
switch (idx)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
sys_log(0, "BlueDragon: Using Skill Breath");
|
||||
|
||||
FSkillBreath f(pChar);
|
||||
|
||||
pSecMap->for_each( f );
|
||||
|
||||
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill0", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "period", "max"));
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
sys_log(0, "BlueDragon: Using Skill Weak Breath");
|
||||
|
||||
FSkillWeakBreath f(pChar);
|
||||
|
||||
pSecMap->for_each( f );
|
||||
|
||||
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill1", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "period", "max"));
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
sys_log(0, "BlueDragon: Using Skill EarthQuake");
|
||||
|
||||
FSkillEarthQuake f(pChar);
|
||||
|
||||
pSecMap->for_each( f );
|
||||
|
||||
nextUsingTime = number(BlueDragon_GetSkillFactor(3, "Skill2", "period", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "period", "max"));
|
||||
|
||||
if (NULL != f.pFarthestChar)
|
||||
{
|
||||
pChar->BeginFight( f.pFarthestChar );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sys_err("BlueDragon: Wrong Skill Index: %d", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int addPct = BlueDragon_GetRangeFactor("hp_period", pChar->GetHPPct());
|
||||
|
||||
nextUsingTime += (nextUsingTime * addPct) / 100;
|
||||
|
||||
return nextUsingTime;
|
||||
}
|
||||
|
||||
int BlueDragon_StateBattle(LPCHARACTER pChar)
|
||||
{
|
||||
if (pChar->GetHPPct() > 98)
|
||||
return PASSES_PER_SEC(1);
|
||||
|
||||
const int SkillCount = 3;
|
||||
int SkillPriority[SkillCount];
|
||||
static time_t timeSkillCanUseTime[SkillCount];
|
||||
|
||||
if (pChar->GetHPPct() > 76)
|
||||
{
|
||||
SkillPriority[0] = 1;
|
||||
SkillPriority[1] = 0;
|
||||
SkillPriority[2] = 2;
|
||||
}
|
||||
else if (pChar->GetHPPct() > 31)
|
||||
{
|
||||
SkillPriority[0] = 0;
|
||||
SkillPriority[1] = 1;
|
||||
SkillPriority[2] = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
SkillPriority[0] = 0;
|
||||
SkillPriority[1] = 2;
|
||||
SkillPriority[2] = 1;
|
||||
}
|
||||
|
||||
time_t timeNow = static_cast<time_t>(get_dword_time());
|
||||
|
||||
for (int i=0 ; i < SkillCount ; ++i)
|
||||
{
|
||||
const int SkillIndex = SkillPriority[i];
|
||||
|
||||
if (timeSkillCanUseTime[SkillIndex] < timeNow)
|
||||
{
|
||||
int SkillUsingDuration =
|
||||
static_cast<int>(CMotionManager::instance().GetMotionDuration( pChar->GetRaceNum(), MAKE_MOTION_KEY(MOTION_MODE_GENERAL, MOTION_SPECIAL_1 + SkillIndex) ));
|
||||
|
||||
timeSkillCanUseTime[SkillIndex] = timeNow + (UseBlueDragonSkill( pChar, SkillIndex ) * 1000) + SkillUsingDuration + 3000;
|
||||
|
||||
pChar->SendMovePacket(FUNC_MOB_SKILL, SkillIndex, pChar->GetX(), pChar->GetY(), 0, timeNow);
|
||||
|
||||
return 0 == SkillUsingDuration ? PASSES_PER_SEC(1) : PASSES_PER_SEC(SkillUsingDuration);
|
||||
}
|
||||
}
|
||||
|
||||
return PASSES_PER_SEC(1);
|
||||
}
|
||||
|
||||
int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER pAttacker, int dam)
|
||||
{
|
||||
if (NULL == me || NULL == pAttacker)
|
||||
return dam;
|
||||
|
||||
if (true == pAttacker->IsMonster() && 2493 == pAttacker->GetMobTable().dwVnum)
|
||||
{
|
||||
for (int i=1 ; i <= 4 ; ++i)
|
||||
{
|
||||
if (ATK_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
|
||||
{
|
||||
DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
|
||||
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
|
||||
size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( pAttacker->GetMapIndex(), dwDragonStoneID );
|
||||
|
||||
dam += (dam * (val*cnt))/100;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (true == me->IsMonster() && 2493 == me->GetMobTable().dwVnum)
|
||||
{
|
||||
for (int i=1 ; i <= 4 ; ++i)
|
||||
{
|
||||
if (DEF_BONUS == BlueDragon_GetIndexFactor("DragonStone", i, "effect_type"))
|
||||
{
|
||||
DWORD dwDragonStoneID = BlueDragon_GetIndexFactor("DragonStone", i, "vnum");
|
||||
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "val");
|
||||
size_t cnt = SECTREE_MANAGER::instance().GetMonsterCountInMap( me->GetMapIndex(), dwDragonStoneID );
|
||||
|
||||
dam -= (dam * (val*cnt))/100;
|
||||
|
||||
if (dam <= 0)
|
||||
dam = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (true == me->IsStone() && 0 != pAttacker->GetMountVnum())
|
||||
{
|
||||
for (int i=1 ; i <= 4 ; ++i)
|
||||
{
|
||||
if (me->GetMobTable().dwVnum == BlueDragon_GetIndexFactor("DragonStone", i, "vnum"))
|
||||
{
|
||||
if (pAttacker->GetMountVnum() == BlueDragon_GetIndexFactor("DragonStone", i, "enemy"))
|
||||
{
|
||||
size_t val = BlueDragon_GetIndexFactor("DragonStone", i, "enemy_val");
|
||||
|
||||
dam *= val;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dam;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
extern int BlueDragon_StateBattle (LPCHARACTER);
|
||||
extern time_t UseBlueDragonSkill (LPCHARACTER, unsigned int);
|
||||
extern int BlueDragon_Damage (LPCHARACTER me, LPCHARACTER attacker, int dam);
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "BlueDragon_Binder.h"
|
||||
|
||||
#include "questmanager.h"
|
||||
|
||||
unsigned int BlueDragon_GetSkillFactor(const size_t cnt, ...)
|
||||
{
|
||||
lua_State* L = quest::CQuestManager::instance().GetLuaState();
|
||||
|
||||
const int stack_top = lua_gettop(L);
|
||||
|
||||
lua_getglobal( L, "BlueDragonSetting" );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, cnt);
|
||||
|
||||
for( size_t i=0 ; i < cnt ; ++i )
|
||||
{
|
||||
const char* key = va_arg(vl, const char*);
|
||||
|
||||
if (NULL == key)
|
||||
{
|
||||
va_end(vl);
|
||||
lua_settop( L, stack_top );
|
||||
sys_err("BlueDragon: wrong key list");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pushstring( L, key );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_istable(L, -1) && i != cnt-1)
|
||||
{
|
||||
va_end(vl);
|
||||
lua_settop( L, stack_top );
|
||||
sys_err("BlueDragon: wrong key table %s", key);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(vl);
|
||||
|
||||
if (false == lua_isnumber(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
sys_err("BlueDragon: Last key is not a number");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int val = static_cast<int>(lua_tonumber( L, -1 ));
|
||||
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned int BlueDragon_GetRangeFactor(const char* key, const int val)
|
||||
{
|
||||
lua_State* L = quest::CQuestManager::instance().GetLuaState();
|
||||
|
||||
const int stack_top = lua_gettop(L);
|
||||
|
||||
lua_getglobal( L, "BlueDragonSetting" );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pushstring( L, key );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no required table %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t cnt = static_cast<size_t>(luaL_getn(L, -1));
|
||||
|
||||
for( size_t i=1 ; i <= cnt ; ++i )
|
||||
{
|
||||
lua_rawgeti( L, -1, i );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: wrong table index %s %d", key, i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pushstring( L, "min" );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_isnumber(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no min value set %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int min = static_cast<int>(lua_tonumber(L, -1));
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_pushstring( L, "max" );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_isnumber(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no max value set %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int max = static_cast<int>(lua_tonumber(L, -1));
|
||||
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (min <= val && val <= max)
|
||||
{
|
||||
lua_pushstring( L, "pct" );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_isnumber(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no pct value set %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int pct = static_cast<int>(lua_tonumber(L, -1));
|
||||
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return pct;
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int BlueDragon_GetIndexFactor(const char* container, const size_t idx, const char* key)
|
||||
{
|
||||
lua_State* L = quest::CQuestManager::instance().GetLuaState();
|
||||
|
||||
const int stack_top = lua_gettop(L);
|
||||
|
||||
lua_getglobal( L, "BlueDragonSetting" );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pushstring( L, container );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no required table %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_rawgeti( L, -1, idx );
|
||||
|
||||
if (false == lua_istable(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: wrong table index %s %d", key, idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
lua_pushstring( L, key );
|
||||
lua_gettable( L, -2 );
|
||||
|
||||
if (false == lua_isnumber(L, -1))
|
||||
{
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
sys_err("BlueDragon: no min value set %s", key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const unsigned int ret = static_cast<unsigned int>(lua_tonumber(L, -1));
|
||||
|
||||
lua_settop( L, stack_top );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
enum BLUEDRAGON_STONE_EFFECT
|
||||
{
|
||||
DEF_BONUS = 1,
|
||||
ATK_BONUS = 2,
|
||||
REGEN_TIME_BONUS = 3,
|
||||
REGEN_PECT_BONUS = 4,
|
||||
};
|
||||
|
||||
extern unsigned int BlueDragon_GetRangeFactor (const char* key, const int val);
|
||||
extern unsigned int BlueDragon_GetSkillFactor (const size_t cnt, ...);
|
||||
extern unsigned int BlueDragon_GetIndexFactor (const char* container, const size_t idx, const char* key);
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
|
||||
struct FSkillBreath
|
||||
{
|
||||
EJobs Set1;
|
||||
EJobs Set2;
|
||||
ESex gender;
|
||||
LPCHARACTER pAttacker;
|
||||
|
||||
FSkillBreath(LPCHARACTER p)
|
||||
{
|
||||
pAttacker = p;
|
||||
|
||||
Set1 = static_cast<EJobs>(number(0,3));
|
||||
Set2 = static_cast<EJobs>(number(0,3));
|
||||
gender = static_cast<ESex>(number(0,2));
|
||||
}
|
||||
|
||||
void operator()(LPENTITY ent)
|
||||
{
|
||||
if (NULL != ent)
|
||||
{
|
||||
if (true == ent->IsType(ENTITY_CHARACTER))
|
||||
{
|
||||
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
|
||||
|
||||
if (true == ch->IsPC() && false == ch->IsDead())
|
||||
{
|
||||
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
|
||||
return;
|
||||
|
||||
if ((signed)BlueDragon_GetSkillFactor(2, "Skill0", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
|
||||
{
|
||||
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
|
||||
return;
|
||||
}
|
||||
|
||||
int overlapDamageCount = 0;
|
||||
|
||||
int pct = 0;
|
||||
if (ch->GetJob() == Set1)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch ( Set1 )
|
||||
{
|
||||
case JOB_WARRIOR: ptr = "musa"; break;
|
||||
case JOB_ASSASSIN: ptr = "assa"; break;
|
||||
case JOB_SURA: ptr = "sura"; break;
|
||||
case JOB_SHAMAN: ptr = "muda"; break;
|
||||
|
||||
default:
|
||||
case JOB_MAX_NUM: return;
|
||||
}
|
||||
|
||||
|
||||
int firstDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max"));
|
||||
pct += firstDamagePercent;
|
||||
|
||||
if (firstDamagePercent > 0)
|
||||
overlapDamageCount++;
|
||||
}
|
||||
|
||||
if (ch->GetJob() == Set2)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch ( Set2 )
|
||||
{
|
||||
case JOB_WARRIOR: ptr = "musa"; break;
|
||||
case JOB_ASSASSIN: ptr = "assa"; break;
|
||||
case JOB_SURA: ptr = "sura"; break;
|
||||
case JOB_SHAMAN: ptr = "muda"; break;
|
||||
|
||||
default:
|
||||
case JOB_MAX_NUM: return;
|
||||
}
|
||||
|
||||
int secondDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "damage", ptr, "max"));
|
||||
pct += secondDamagePercent;
|
||||
|
||||
if (secondDamagePercent > 0)
|
||||
overlapDamageCount++;
|
||||
}
|
||||
|
||||
if (GET_SEX(ch) == gender)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch (gender)
|
||||
{
|
||||
case SEX_MALE: ptr = "male"; break;
|
||||
case SEX_FEMALE: ptr = "female"; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
int thirdDamagePercent = number(BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill0", "gender", ptr, "max"));
|
||||
pct += thirdDamagePercent;
|
||||
|
||||
if (thirdDamagePercent > 0)
|
||||
overlapDamageCount++;
|
||||
}
|
||||
|
||||
switch (overlapDamageCount)
|
||||
{
|
||||
case 1:
|
||||
ch->EffectPacket(SE_PERCENT_DAMAGE1);
|
||||
break;
|
||||
case 2:
|
||||
ch->EffectPacket(SE_PERCENT_DAMAGE2);
|
||||
break;
|
||||
case 3:
|
||||
ch->EffectPacket(SE_PERCENT_DAMAGE3);
|
||||
break;
|
||||
}
|
||||
|
||||
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
|
||||
|
||||
pct += addPct;
|
||||
|
||||
int dam = number(BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill0", "default_damage", "max"));
|
||||
|
||||
dam += (dam * addPct) / 100;
|
||||
dam += (ch->GetMaxHP() * pct) / 100;
|
||||
|
||||
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE );
|
||||
|
||||
sys_log(0, "BlueDragon: Breath to %s pct(%d) dam(%d) overlap(%d)", ch->GetName(), pct, dam, overlapDamageCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FSkillWeakBreath
|
||||
{
|
||||
LPCHARACTER pAttacker;
|
||||
|
||||
FSkillWeakBreath(LPCHARACTER p)
|
||||
{
|
||||
pAttacker = p;
|
||||
}
|
||||
|
||||
void operator()(LPENTITY ent)
|
||||
{
|
||||
if (NULL != ent)
|
||||
{
|
||||
if (true == ent->IsType(ENTITY_CHARACTER))
|
||||
{
|
||||
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
|
||||
|
||||
if (true == ch->IsPC() && false == ch->IsDead())
|
||||
{
|
||||
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
|
||||
return;
|
||||
|
||||
if ((signed)BlueDragon_GetSkillFactor(2, "Skill1", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
|
||||
{
|
||||
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
|
||||
return;
|
||||
}
|
||||
|
||||
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
|
||||
|
||||
int dam = number( BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill1", "default_damage", "max") );
|
||||
dam += (dam * addPct) / 100;
|
||||
|
||||
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE );
|
||||
|
||||
sys_log(0, "BlueDragon: WeakBreath to %s addPct(%d) dam(%d)", ch->GetName(), addPct, dam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FSkillEarthQuake
|
||||
{
|
||||
EJobs Set1;
|
||||
EJobs Set2;
|
||||
ESex gender;
|
||||
long MaxDistance;
|
||||
LPCHARACTER pAttacker;
|
||||
LPCHARACTER pFarthestChar;
|
||||
|
||||
FSkillEarthQuake(LPCHARACTER p)
|
||||
{
|
||||
pAttacker = p;
|
||||
|
||||
MaxDistance = 0;
|
||||
pFarthestChar = NULL;
|
||||
|
||||
Set1 = static_cast<EJobs>(number(0,3));
|
||||
Set2 = static_cast<EJobs>(number(0,3));
|
||||
gender = static_cast<ESex>(number(0,2));
|
||||
}
|
||||
|
||||
void operator()(LPENTITY ent)
|
||||
{
|
||||
if (NULL != ent)
|
||||
{
|
||||
if (true == ent->IsType(ENTITY_CHARACTER))
|
||||
{
|
||||
LPCHARACTER ch = static_cast<LPCHARACTER>(ent);
|
||||
|
||||
if (true == ch->IsPC() && false == ch->IsDead())
|
||||
{
|
||||
if (NULL != ch->FindAffect(AFFECT_REVIVE_INVISIBLE, APPLY_NONE))
|
||||
return;
|
||||
|
||||
if ((signed)BlueDragon_GetSkillFactor(2, "Skill2", "damage_area") < DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()))
|
||||
{
|
||||
sys_log(0, "BlueDragon: Breath too far (%d)", DISTANCE_APPROX(pAttacker->GetX()-ch->GetX(), pAttacker->GetY()-ch->GetY()) );
|
||||
return;
|
||||
}
|
||||
|
||||
int sec = number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", "default", "max"));
|
||||
|
||||
if (ch->GetJob() == Set1)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch ( Set1 )
|
||||
{
|
||||
case JOB_WARRIOR: ptr = "musa"; break;
|
||||
case JOB_ASSASSIN: ptr = "assa"; break;
|
||||
case JOB_SURA: ptr = "sura"; break;
|
||||
case JOB_SHAMAN: ptr = "muda"; break;
|
||||
|
||||
default:
|
||||
case JOB_MAX_NUM: return;
|
||||
}
|
||||
|
||||
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max"));
|
||||
}
|
||||
|
||||
if (ch->GetJob() == Set2)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch ( Set2 )
|
||||
{
|
||||
case JOB_WARRIOR: ptr = "musa"; break;
|
||||
case JOB_ASSASSIN: ptr = "assa"; break;
|
||||
case JOB_SURA: ptr = "sura"; break;
|
||||
case JOB_SHAMAN: ptr = "muda"; break;
|
||||
|
||||
default:
|
||||
case JOB_MAX_NUM: return;
|
||||
}
|
||||
|
||||
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "stun_time", ptr, "max"));
|
||||
}
|
||||
|
||||
if (GET_SEX(ch) == gender)
|
||||
{
|
||||
const char* ptr = NULL;
|
||||
|
||||
switch (gender)
|
||||
{
|
||||
case SEX_MALE: ptr = "male"; break;
|
||||
case SEX_FEMALE: ptr = "female"; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
sec += number(BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "min"), BlueDragon_GetSkillFactor(4, "Skill2", "gender", ptr, "max"));
|
||||
}
|
||||
|
||||
int addPct = BlueDragon_GetRangeFactor("hp_damage", pAttacker->GetHPPct());
|
||||
|
||||
int dam = number( BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "min"), BlueDragon_GetSkillFactor(3, "Skill2", "default_damage", "max") );
|
||||
dam += (dam * addPct) / 100;
|
||||
|
||||
ch->Damage( pAttacker, dam, DAMAGE_TYPE_ICE);
|
||||
|
||||
SkillAttackAffect( ch, 1000, IMMUNE_STUN, AFFECT_STUN, POINT_NONE, 0, AFF_STUN, sec, "BDRAGON_STUN" );
|
||||
|
||||
sys_log(0, "BlueDragon: EarthQuake to %s addPct(%d) dam(%d) sec(%d)", ch->GetName(), addPct, dam, sec);
|
||||
|
||||
|
||||
VECTOR vec;
|
||||
vec.x = static_cast<float>(pAttacker->GetX() - ch->GetX());
|
||||
vec.y = static_cast<float>(pAttacker->GetY() - ch->GetY());
|
||||
vec.z = 0.0f;
|
||||
|
||||
Normalize( &vec, &vec );
|
||||
|
||||
const int nFlyDistance = 1000;
|
||||
|
||||
long tx = ch->GetX() + vec.x * nFlyDistance;
|
||||
long ty = ch->GetY() + vec.y * nFlyDistance;
|
||||
|
||||
for (int i=0 ; i < 5 ; ++i)
|
||||
{
|
||||
if (true == SECTREE_MANAGER::instance().IsMovablePosition( ch->GetMapIndex(), tx, ty ))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch( i )
|
||||
{
|
||||
case 0:
|
||||
tx = ch->GetX() + vec.x * nFlyDistance * -1;
|
||||
ty = ch->GetY() + vec.y * nFlyDistance * -1;
|
||||
break;
|
||||
case 1:
|
||||
tx = ch->GetX() + vec.x * nFlyDistance * -1;
|
||||
ty = ch->GetY() + vec.y * nFlyDistance;
|
||||
break;
|
||||
case 2:
|
||||
tx = ch->GetX() + vec.x * nFlyDistance;
|
||||
ty = ch->GetY() + vec.y * nFlyDistance * -1;
|
||||
break;
|
||||
case 3:
|
||||
tx = ch->GetX() + vec.x * number(1,100);
|
||||
ty = ch->GetY() + vec.y * number(1,100);
|
||||
break;
|
||||
case 4:
|
||||
tx = ch->GetX() + vec.x * number(1,10);
|
||||
ty = ch->GetY() + vec.y * number(1,10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ch->Sync( tx , ty );
|
||||
ch->Goto( tx , ty );
|
||||
ch->CalculateMoveDuration();
|
||||
|
||||
ch->SyncPacket();
|
||||
|
||||
long dist = DISTANCE_APPROX( pAttacker->GetX() - ch->GetX(), pAttacker->GetY() - ch->GetY() );
|
||||
|
||||
if (dist > MaxDistance)
|
||||
{
|
||||
MaxDistance = dist;
|
||||
pFarthestChar = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
#include "stdafx.h"
|
||||
#include "ClientPackageCryptInfo.h"
|
||||
#include "../../common/stl.h"
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
#include "../../libthecore/include/xdirent.h"
|
||||
#endif
|
||||
|
||||
CClientPackageCryptInfo::CClientPackageCryptInfo() : m_pSerializedCryptKeyStream(NULL), m_nCryptKeyPackageCnt(0)
|
||||
{
|
||||
}
|
||||
|
||||
CClientPackageCryptInfo::~CClientPackageCryptInfo()
|
||||
{
|
||||
m_vecPackageCryptKeys.clear();
|
||||
m_mapPackageSDB.clear();
|
||||
if( m_pSerializedCryptKeyStream )
|
||||
{
|
||||
delete[] m_pSerializedCryptKeyStream;
|
||||
m_pSerializedCryptKeyStream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CClientPackageCryptInfo::LoadPackageCryptFile( const char* pCryptFile )
|
||||
{
|
||||
FILE * fp = fopen(pCryptFile, "rb");
|
||||
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
int iSDBDataOffset;
|
||||
fread(&iSDBDataOffset, sizeof(int), 1, fp);
|
||||
|
||||
int iPackageCnt;
|
||||
fread( &iPackageCnt, sizeof(int), 1, fp );
|
||||
m_nCryptKeyPackageCnt += iPackageCnt;
|
||||
|
||||
int iCryptKeySize = iSDBDataOffset - 2*sizeof(int);
|
||||
|
||||
{
|
||||
|
||||
if (0 == iCryptKeySize)
|
||||
{
|
||||
sys_log(0, "[PackageCryptInfo] failed to load crypt key. (file: %s, key size: %d)", pCryptFile, iCryptKeySize);
|
||||
m_nCryptKeyPackageCnt -= iPackageCnt;
|
||||
}
|
||||
else
|
||||
{
|
||||
int nCurKeySize = (int)m_vecPackageCryptKeys.size();
|
||||
m_vecPackageCryptKeys.resize( nCurKeySize + sizeof(int) + iCryptKeySize);
|
||||
|
||||
memcpy( &m_vecPackageCryptKeys[nCurKeySize], &iCryptKeySize, sizeof(int));
|
||||
fread( &m_vecPackageCryptKeys[nCurKeySize + sizeof(int)], sizeof(BYTE), iCryptKeySize, fp );
|
||||
sys_log(0, "[PackageCryptInfo] %s loaded. (key size: %d, count: %d, total: %d)", pCryptFile, iCryptKeySize, iPackageCnt, m_nCryptKeyPackageCnt);
|
||||
}
|
||||
}
|
||||
|
||||
//about SDB data
|
||||
//total packagecnt (4byte)
|
||||
// for packagecnt
|
||||
// db name hash 4byte( stl.h stringhash ) +child node size(4byte)
|
||||
|
||||
//stream to client
|
||||
// sdb file cnt( 4byte )
|
||||
// for sdb file cnt
|
||||
// filename hash ( stl.h stringhash )
|
||||
// related map name size(4), relate map name
|
||||
// sdb block size( 1byte )
|
||||
// sdb blocks
|
||||
|
||||
int iSDBPackageCnt;
|
||||
fread(&iSDBPackageCnt, sizeof(int), 1, fp);
|
||||
|
||||
DWORD dwPackageNameHash, dwPackageStreamSize, dwSDBFileCnt, dwFileNameHash, dwMapNameSize;
|
||||
|
||||
std::string strRelatedMapName;
|
||||
|
||||
if (0 == iCryptKeySize && 0 == iSDBPackageCnt)
|
||||
return false;
|
||||
|
||||
for( int i = 0; i < iSDBPackageCnt; ++i )
|
||||
{
|
||||
fread(&dwPackageNameHash, sizeof(DWORD), 1, fp);
|
||||
fread(&dwPackageStreamSize, sizeof(DWORD), 1, fp);
|
||||
|
||||
fread(&dwSDBFileCnt, sizeof(DWORD), 1, fp);
|
||||
|
||||
sys_log(0, "[PackageCryptInfo] SDB Loaded. (Name Hash : %d, Stream Size: %d, File Count: %d)", dwPackageNameHash,dwPackageStreamSize, dwSDBFileCnt);
|
||||
|
||||
for( int j = 0; j < (int)dwSDBFileCnt; ++j )
|
||||
{
|
||||
fread(&dwFileNameHash, sizeof(DWORD), 1, fp);
|
||||
fread(&dwMapNameSize, sizeof(DWORD), 1, fp);
|
||||
|
||||
strRelatedMapName.resize( dwMapNameSize );
|
||||
fread(&strRelatedMapName[0], sizeof(BYTE), dwMapNameSize, fp);
|
||||
|
||||
sys_log(0, "[PackageCryptInfo] \t SDB each file info loaded.(MapName: %s, NameHash: %X)", strRelatedMapName.c_str(), dwFileNameHash);
|
||||
|
||||
BYTE bSDBStreamSize;
|
||||
std::vector<BYTE> vecSDBStream;
|
||||
fread(&bSDBStreamSize, sizeof(BYTE), 1, fp);
|
||||
|
||||
vecSDBStream.resize(bSDBStreamSize);
|
||||
fread(&vecSDBStream[0], sizeof(BYTE), bSDBStreamSize, fp);
|
||||
|
||||
//reconstruct it
|
||||
TPackageSDBMap::iterator it = m_mapPackageSDB.find( strRelatedMapName );
|
||||
if( it == m_mapPackageSDB.end() )
|
||||
{
|
||||
TPerFileSDBInfo fileSDBInfo;
|
||||
m_mapPackageSDB[strRelatedMapName] = fileSDBInfo;
|
||||
}
|
||||
|
||||
TSupplementaryDataBlockInfo SDBInfo;
|
||||
std::vector<TSupplementaryDataBlockInfo>& rSDBInfos = m_mapPackageSDB[strRelatedMapName].vecSDBInfos;
|
||||
{
|
||||
SDBInfo.dwPackageIdentifier = dwPackageNameHash;
|
||||
SDBInfo.dwFileIdentifier = dwFileNameHash;
|
||||
SDBInfo.vecSDBStream.resize( bSDBStreamSize );
|
||||
|
||||
memcpy(&SDBInfo.vecSDBStream[0], &vecSDBStream[0], bSDBStreamSize );
|
||||
|
||||
rSDBInfos.push_back( SDBInfo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CClientPackageCryptInfo::LoadPackageCryptInfo( const char* pCryptInfoDir )
|
||||
{
|
||||
DIR * pDir = opendir(pCryptInfoDir);
|
||||
|
||||
if (!pDir)
|
||||
return false;
|
||||
|
||||
m_nCryptKeyPackageCnt = 0;
|
||||
if( m_pSerializedCryptKeyStream )
|
||||
{
|
||||
delete[] m_pSerializedCryptKeyStream;
|
||||
m_pSerializedCryptKeyStream = NULL;
|
||||
}
|
||||
|
||||
m_mapPackageSDB.clear();
|
||||
m_vecPackageCryptKeys.clear();
|
||||
|
||||
const char szPrefixCryptInfoFile[] = "cshybridcrypt";
|
||||
|
||||
dirent * pDirEnt;
|
||||
while ((pDirEnt = readdir(pDir)))
|
||||
{
|
||||
//if (strncmp( &(pDirEnt->d_name[0]), szPrefixCryptInfoFile, strlen(szPrefixCryptInfoFile)) )
|
||||
if (std::string::npos == std::string(pDirEnt->d_name).find(szPrefixCryptInfoFile))
|
||||
{
|
||||
sys_log(0, "[PackageCryptInfo] %s is not crypt file. pass!", pDirEnt->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string strFullPathName = std::string(pCryptInfoDir) + std::string(pDirEnt->d_name);
|
||||
|
||||
sys_log(0, "[PackageCryptInfo] Try to load crypt file: %s", strFullPathName.c_str());
|
||||
|
||||
if (false == LoadPackageCryptFile( strFullPathName.c_str() ))
|
||||
sys_err("[PackageCryptInfo] Failed to load %s", strFullPathName.c_str());
|
||||
}
|
||||
|
||||
closedir(pDir);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CClientPackageCryptInfo::GetPackageCryptKeys( BYTE** ppData, int& iDataSize )
|
||||
{
|
||||
int nCryptKeySize = m_vecPackageCryptKeys.size();
|
||||
int iStreamSize = sizeof(int)+nCryptKeySize;
|
||||
|
||||
//NOTE : Crypt Key Info isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated )
|
||||
//it`s not safe but due to performance benefit we don`t do re-serialize.
|
||||
if( m_pSerializedCryptKeyStream )
|
||||
{
|
||||
*ppData = m_pSerializedCryptKeyStream;
|
||||
iDataSize = iStreamSize;
|
||||
return;
|
||||
}
|
||||
|
||||
if( nCryptKeySize > 0 )
|
||||
{
|
||||
m_pSerializedCryptKeyStream = new BYTE[iStreamSize];
|
||||
memcpy(&m_pSerializedCryptKeyStream[0], &m_nCryptKeyPackageCnt, sizeof(int) );
|
||||
memcpy(&m_pSerializedCryptKeyStream[sizeof(int)], &m_vecPackageCryptKeys[0], nCryptKeySize );
|
||||
|
||||
*ppData = m_pSerializedCryptKeyStream;
|
||||
iDataSize = iStreamSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
*ppData = NULL;
|
||||
iDataSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CClientPackageCryptInfo::GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize )
|
||||
{
|
||||
std::string strLowerMapName = pMapName;
|
||||
stl_lowers(strLowerMapName);
|
||||
|
||||
TPackageSDBMap::iterator it = m_mapPackageSDB.find( strLowerMapName.c_str() );
|
||||
if( it == m_mapPackageSDB.end() || it->second.vecSDBInfos.size() == 0 )
|
||||
{
|
||||
//sys_err("GetRelatedMapSDBStreams Failed(%s)", strLowerMapName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
*ppData = it->second.GetSerializedStream();
|
||||
iDataSize = it->second.GetSize();
|
||||
|
||||
//sys_log(0, "GetRelatedMapSDBStreams Size(%d)", iDataSize);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
#ifndef __INC_CLIENTPACKAGE_CRYPTINFO_H
|
||||
#define __INC_CLIENTPACKAGE_CRYPTINFO_H
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct SSupplementaryDataBlockInfo
|
||||
{
|
||||
DWORD dwPackageIdentifier;
|
||||
DWORD dwFileIdentifier;
|
||||
std::vector<BYTE> vecSDBStream;
|
||||
|
||||
void Serialize( BYTE* pStream )
|
||||
{
|
||||
memcpy(pStream, &dwPackageIdentifier, sizeof(DWORD));
|
||||
memcpy(pStream+4, &dwFileIdentifier, sizeof(DWORD));
|
||||
|
||||
BYTE bSize = vecSDBStream.size();
|
||||
memcpy(pStream+8, &bSize, sizeof(BYTE));
|
||||
memcpy(pStream+9, &vecSDBStream[0], bSize);
|
||||
}
|
||||
|
||||
DWORD GetSerializedSize() const
|
||||
{
|
||||
return sizeof(DWORD)*2 + sizeof(BYTE) + vecSDBStream.size();
|
||||
}
|
||||
|
||||
} TSupplementaryDataBlockInfo;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
class CClientPackageCryptInfo
|
||||
{
|
||||
public:
|
||||
CClientPackageCryptInfo();
|
||||
~CClientPackageCryptInfo();
|
||||
|
||||
bool LoadPackageCryptInfo( const char* pCryptInfoDir );
|
||||
void GetPackageCryptKeys( BYTE** ppData, int& iDataSize );
|
||||
|
||||
bool GetRelatedMapSDBStreams(const char* pMapName, BYTE** ppData, int& iDataSize );
|
||||
|
||||
private:
|
||||
bool LoadPackageCryptFile( const char* pCryptFile );
|
||||
|
||||
private:
|
||||
int m_nCryptKeyPackageCnt;
|
||||
std::vector<BYTE> m_vecPackageCryptKeys;
|
||||
BYTE* m_pSerializedCryptKeyStream;
|
||||
|
||||
typedef struct SPerFileSDBInfo
|
||||
{
|
||||
SPerFileSDBInfo() : m_pSerializedStream(NULL) {}
|
||||
~SPerFileSDBInfo()
|
||||
{
|
||||
if(m_pSerializedStream)
|
||||
{
|
||||
delete[]m_pSerializedStream;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD GetSize() const
|
||||
{
|
||||
DWORD dwSize = 4; //initial vecSDBInfo count
|
||||
|
||||
for(int i = 0; i < (int)vecSDBInfos.size(); ++i)
|
||||
{
|
||||
dwSize += vecSDBInfos[i].GetSerializedSize();
|
||||
}
|
||||
|
||||
return dwSize;
|
||||
}
|
||||
|
||||
BYTE* GetSerializedStream()
|
||||
{
|
||||
//NOTE : SDB Data isn`t updated during runtime. ( in case of file reloading all data is cleared & recreated )
|
||||
//it`s not safe but due to performance benefit we don`t do re-serialize.
|
||||
if(m_pSerializedStream)
|
||||
return m_pSerializedStream;
|
||||
|
||||
m_pSerializedStream = new BYTE[GetSize()];
|
||||
|
||||
int iWrittenOffset = 0;
|
||||
int iSDBInfoSize = vecSDBInfos.size();
|
||||
|
||||
//write size
|
||||
memcpy( m_pSerializedStream, &iSDBInfoSize, sizeof(int) );
|
||||
iWrittenOffset += sizeof(int);
|
||||
for(int i = 0; i < iSDBInfoSize; ++i)
|
||||
{
|
||||
vecSDBInfos[i].Serialize( m_pSerializedStream + iWrittenOffset );
|
||||
iWrittenOffset += vecSDBInfos[i].GetSerializedSize();
|
||||
}
|
||||
|
||||
return m_pSerializedStream;
|
||||
}
|
||||
|
||||
std::vector<TSupplementaryDataBlockInfo> vecSDBInfos;
|
||||
|
||||
private:
|
||||
BYTE* m_pSerializedStream;
|
||||
|
||||
} TPerFileSDBInfo;
|
||||
|
||||
typedef boost::unordered_map<std::string, TPerFileSDBInfo > TPackageSDBMap; //key: related map name
|
||||
TPackageSDBMap m_mapPackageSDB;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //__INC_CLIENTPACKAGE_CRYPTINFO_H
|
|
@ -0,0 +1,255 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "DragonLair.h"
|
||||
|
||||
#include "entity.h"
|
||||
#include "sectree_manager.h"
|
||||
#include "char.h"
|
||||
#include "guild.h"
|
||||
#include "locale_service.h"
|
||||
#include "regen.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern int passes_per_sec;
|
||||
|
||||
struct FWarpToDragronLairWithGuildMembers
|
||||
{
|
||||
DWORD dwGuildID;
|
||||
long mapIndex;
|
||||
long x, y;
|
||||
|
||||
FWarpToDragronLairWithGuildMembers( DWORD guildID, long map, long X, long Y )
|
||||
: dwGuildID(guildID), mapIndex(map), x(X), y(Y)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(LPENTITY ent)
|
||||
{
|
||||
if (NULL != ent && true == ent->IsType(ENTITY_CHARACTER))
|
||||
{
|
||||
LPCHARACTER pChar = static_cast<LPCHARACTER>(ent);
|
||||
|
||||
if (true == pChar->IsPC())
|
||||
{
|
||||
if (NULL != pChar->GetGuild())
|
||||
{
|
||||
if (dwGuildID == pChar->GetGuild()->GetID())
|
||||
{
|
||||
pChar->WarpSet(x, y, mapIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FWarpToVillage
|
||||
{
|
||||
void operator() (LPENTITY ent)
|
||||
{
|
||||
if (NULL != ent)
|
||||
{
|
||||
LPCHARACTER pChar = static_cast<LPCHARACTER>(ent);
|
||||
|
||||
if (NULL != pChar)
|
||||
{
|
||||
if (true == pChar->IsPC())
|
||||
{
|
||||
pChar->GoHome();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
EVENTINFO(tag_DragonLair_Collapse_EventInfo)
|
||||
{
|
||||
int step;
|
||||
CDragonLair* pLair;
|
||||
long InstanceMapIndex;
|
||||
|
||||
tag_DragonLair_Collapse_EventInfo()
|
||||
: step( 0 )
|
||||
, pLair( 0 )
|
||||
, InstanceMapIndex( 0 )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
EVENTFUNC( DragonLair_Collapse_Event )
|
||||
{
|
||||
tag_DragonLair_Collapse_EventInfo* pInfo = dynamic_cast<tag_DragonLair_Collapse_EventInfo*>(event->info);
|
||||
|
||||
if ( pInfo == NULL )
|
||||
{
|
||||
sys_err( "DragonLair_Collapse_Event> <Factor> Null pointer" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (0 == pInfo->step)
|
||||
{
|
||||
char buf[512];
|
||||
snprintf(buf, 512, LC_TEXT("용가리가 %d 초만에 죽어써효ㅠㅠ"), pInfo->pLair->GetEstimatedTime());
|
||||
SendNoticeMap(buf, pInfo->InstanceMapIndex, true);
|
||||
|
||||
pInfo->step++;
|
||||
|
||||
return PASSES_PER_SEC( 30 );
|
||||
}
|
||||
else if (1 == pInfo->step)
|
||||
{
|
||||
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pInfo->InstanceMapIndex );
|
||||
|
||||
if (NULL != pMap)
|
||||
{
|
||||
FWarpToVillage f;
|
||||
pMap->for_each( f );
|
||||
}
|
||||
|
||||
pInfo->step++;
|
||||
|
||||
return PASSES_PER_SEC( 30 );
|
||||
}
|
||||
else
|
||||
{
|
||||
SECTREE_MANAGER::instance().DestroyPrivateMap( pInfo->InstanceMapIndex );
|
||||
M2_DELETE(pInfo->pLair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CDragonLair::CDragonLair(DWORD guildID, long BaseMapID, long PrivateMapID)
|
||||
: GuildID_(guildID), BaseMapIndex_(BaseMapID), PrivateMapIndex_(PrivateMapID)
|
||||
{
|
||||
StartTime_ = get_global_time();
|
||||
}
|
||||
|
||||
CDragonLair::~CDragonLair()
|
||||
{
|
||||
}
|
||||
|
||||
DWORD CDragonLair::GetEstimatedTime() const
|
||||
{
|
||||
return get_global_time() - StartTime_;
|
||||
}
|
||||
|
||||
void CDragonLair::OnDragonDead(LPCHARACTER pDragon)
|
||||
{
|
||||
sys_log(0, "DragonLair: 도라곤이 죽어써효");
|
||||
|
||||
LogManager::instance().DragonSlayLog( GuildID_, pDragon->GetMobTable().dwVnum, StartTime_, get_global_time() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
CDragonLairManager::CDragonLairManager()
|
||||
{
|
||||
}
|
||||
|
||||
CDragonLairManager::~CDragonLairManager()
|
||||
{
|
||||
}
|
||||
|
||||
bool CDragonLairManager::Start(long MapIndexFrom, long BaseMapIndex, DWORD GuildID)
|
||||
{
|
||||
long instanceMapIndex = SECTREE_MANAGER::instance().CreatePrivateMap(BaseMapIndex);
|
||||
if (instanceMapIndex == 0) {
|
||||
sys_err("CDragonLairManager::Start() : no private map index available");
|
||||
return false;
|
||||
}
|
||||
|
||||
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap(MapIndexFrom);
|
||||
|
||||
if (NULL != pMap)
|
||||
{
|
||||
LPSECTREE_MAP pTargetMap = SECTREE_MANAGER::instance().GetMap(BaseMapIndex);
|
||||
|
||||
if (NULL == pTargetMap)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const TMapRegion* pRegionInfo = SECTREE_MANAGER::instance().GetMapRegion( pTargetMap->m_setting.iIndex );
|
||||
|
||||
if (NULL != pRegionInfo)
|
||||
{
|
||||
FWarpToDragronLairWithGuildMembers f(GuildID, instanceMapIndex, 844000, 1066900);
|
||||
|
||||
pMap->for_each( f );
|
||||
|
||||
LairMap_.insert( std::make_pair(GuildID, M2_NEW CDragonLair(GuildID, BaseMapIndex, instanceMapIndex)) );
|
||||
|
||||
std::string strMapBasePath( LocaleService_GetMapPath() );
|
||||
|
||||
strMapBasePath += "/" + pRegionInfo->strMapName + "/instance_regen.txt";
|
||||
|
||||
sys_log(0, "%s", strMapBasePath.c_str());
|
||||
|
||||
regen_do(strMapBasePath.c_str(), instanceMapIndex, pTargetMap->m_setting.iBaseX, pTargetMap->m_setting.iBaseY, NULL, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDragonLairManager::OnDragonDead(LPCHARACTER pDragon, DWORD KillerGuildID)
|
||||
{
|
||||
if (NULL == pDragon)
|
||||
return;
|
||||
|
||||
if (false == pDragon->IsMonster())
|
||||
return;
|
||||
|
||||
boost::unordered_map<DWORD, CDragonLair*>::iterator iter = LairMap_.find( KillerGuildID );
|
||||
|
||||
if (LairMap_.end() == iter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LPSECTREE_MAP pMap = SECTREE_MANAGER::instance().GetMap( pDragon->GetMapIndex() );
|
||||
|
||||
if (NULL == iter->second || NULL == pMap)
|
||||
{
|
||||
LairMap_.erase( iter );
|
||||
return;
|
||||
}
|
||||
|
||||
iter->second->OnDragonDead( pDragon );
|
||||
|
||||
// 애들 다 집으로 보내고 맵 없애기
|
||||
|
||||
tag_DragonLair_Collapse_EventInfo* info;
|
||||
info = AllocEventInfo<tag_DragonLair_Collapse_EventInfo>();
|
||||
|
||||
info->step = 0;
|
||||
info->pLair = iter->second;
|
||||
info->InstanceMapIndex = pDragon->GetMapIndex();
|
||||
|
||||
event_create(DragonLair_Collapse_Event, info, PASSES_PER_SEC(10));
|
||||
|
||||
LairMap_.erase( iter );
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include "../../common/stl.h"
|
||||
|
||||
class CDragonLair
|
||||
{
|
||||
public:
|
||||
CDragonLair (DWORD dwGuildID, long BaseMapID, long PrivateMapID);
|
||||
virtual ~CDragonLair ();
|
||||
|
||||
DWORD GetEstimatedTime () const;
|
||||
|
||||
void OnDragonDead (LPCHARACTER pDragon);
|
||||
|
||||
private:
|
||||
DWORD StartTime_;
|
||||
DWORD GuildID_;
|
||||
long BaseMapIndex_;
|
||||
long PrivateMapIndex_;
|
||||
};
|
||||
|
||||
class CDragonLairManager : public singleton<CDragonLairManager>
|
||||
{
|
||||
public:
|
||||
CDragonLairManager ();
|
||||
virtual ~CDragonLairManager ();
|
||||
|
||||
bool Start (long MapIndexFrom, long BaseMapIndex, DWORD GuildID);
|
||||
void OnDragonDead (LPCHARACTER pDragon, DWORD KillerGuildID);
|
||||
|
||||
size_t GetLairCount () const { return LairMap_.size(); }
|
||||
|
||||
private:
|
||||
boost::unordered_map<DWORD, CDragonLair*> LairMap_;
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,60 @@
|
|||
#ifndef __INC_METIN_II_GAME_DRAGON_SOUL_H__
|
||||
#define __INC_METIN_II_GAME_DRAGON_SOUL_H__
|
||||
|
||||
#include "../../common/length.h"
|
||||
|
||||
class CHARACTER;
|
||||
class CItem;
|
||||
|
||||
class DragonSoulTable;
|
||||
|
||||
class DSManager : public singleton<DSManager>
|
||||
{
|
||||
public:
|
||||
DSManager();
|
||||
~DSManager();
|
||||
bool ReadDragonSoulTableFile(const char * c_pszFileName);
|
||||
|
||||
void GetDragonSoulInfo(DWORD dwVnum, OUT BYTE& bType, OUT BYTE& bGrade, OUT BYTE& bStep, OUT BYTE& bRefine) const;
|
||||
// fixme : titempos로
|
||||
WORD GetBasePosition(const LPITEM pItem) const;
|
||||
bool IsValidCellForThisItem(const LPITEM pItem, const TItemPos& Cell) const;
|
||||
int GetDuration(const LPITEM pItem) const;
|
||||
|
||||
// 용혼석을 받아서 특정 용심을 추출하는 함수
|
||||
bool ExtractDragonHeart(LPCHARACTER ch, LPITEM pItem, LPITEM pExtractor = NULL);
|
||||
|
||||
// 특정 용혼석(pItem)을 장비창에서 제거할 때에 성공 여부를 결정하고,
|
||||
// 실패시 부산물을 주는 함수.(부산물은 dragon_soul_table.txt에 정의)
|
||||
// DestCell에 invalid한 값을 넣으면 성공 시, 용혼석을 빈 공간에 자동 추가.
|
||||
// 실패 시, 용혼석(pItem)은 delete됨.
|
||||
// 추출아이템이 있다면 추출 성공 확률이 pExtractor->GetValue(0)%만큼 증가함.
|
||||
// 부산물은 언제나 자동 추가.
|
||||
bool PullOut(LPCHARACTER ch, TItemPos DestCell, IN OUT LPITEM& pItem, LPITEM pExtractor = NULL);
|
||||
|
||||
// 용혼석 업그레이드 함수
|
||||
bool DoRefineGrade(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
|
||||
bool DoRefineStep(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
|
||||
bool DoRefineStrength(LPCHARACTER ch, TItemPos (&aItemPoses)[DRAGON_SOUL_REFINE_GRID_SIZE]);
|
||||
|
||||
bool DragonSoulItemInitialize(LPITEM pItem);
|
||||
|
||||
bool IsTimeLeftDragonSoul(LPITEM pItem) const;
|
||||
int LeftTime(LPITEM pItem) const;
|
||||
bool ActivateDragonSoul(LPITEM pItem);
|
||||
bool DeactivateDragonSoul(LPITEM pItem, bool bSkipRefreshOwnerActiveState = false);
|
||||
bool IsActiveDragonSoul(LPITEM pItem) const;
|
||||
private:
|
||||
void SendRefineResultPacket(LPCHARACTER ch, BYTE bSubHeader, const TItemPos& pos);
|
||||
|
||||
// 캐릭터의 용혼석 덱을 살펴보고, 활성화 된 용혼석이 없다면, 캐릭터의 용혼석 활성 상태를 off 시키는 함수.
|
||||
void RefreshDragonSoulState(LPCHARACTER ch);
|
||||
|
||||
DWORD MakeDragonSoulVnum(BYTE bType, BYTE grade, BYTE step, BYTE refine);
|
||||
bool PutAttributes(LPITEM pDS);
|
||||
bool RefreshItemAttributes(LPITEM pItem);
|
||||
|
||||
DragonSoulTable* m_pTable;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
// Local Includes
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "FSM.h"
|
||||
|
||||
// Constructor
|
||||
CFSM::CFSM()
|
||||
{
|
||||
// Initialize States
|
||||
m_stateInitial.Set(this, &CFSM::BeginStateInitial, &CFSM::StateInitial, &CFSM::EndStateInitial);
|
||||
|
||||
// Initialize State Machine
|
||||
m_pCurrentState = static_cast<CState *>(&m_stateInitial);
|
||||
m_pNewState = NULL;
|
||||
}
|
||||
|
||||
//======================================================================================================
|
||||
// Global Functions
|
||||
|
||||
// Update
|
||||
void CFSM::Update()
|
||||
{
|
||||
// Check New State
|
||||
if (m_pNewState)
|
||||
{
|
||||
if (NULL != m_pCurrentState)
|
||||
{
|
||||
m_pCurrentState->ExecuteEndState();
|
||||
}
|
||||
|
||||
// Set New State
|
||||
m_pCurrentState = m_pNewState;
|
||||
m_pNewState = 0;
|
||||
|
||||
// Execute Begin State
|
||||
m_pCurrentState->ExecuteBeginState();
|
||||
}
|
||||
|
||||
// Execute State
|
||||
m_pCurrentState->ExecuteState();
|
||||
}
|
||||
|
||||
//======================================================================================================
|
||||
// State Functions
|
||||
|
||||
// Is State
|
||||
bool CFSM::IsState(CState & State) const
|
||||
{
|
||||
return (m_pCurrentState == &State);
|
||||
}
|
||||
|
||||
// Goto State
|
||||
bool CFSM::GotoState(CState & NewState)
|
||||
{
|
||||
if (IsState(NewState) && m_pNewState == &NewState)
|
||||
return true;
|
||||
|
||||
// Set New State
|
||||
m_pNewState = &NewState;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef _fsm_fsm_h
|
||||
#define _fsm_fsm_h
|
||||
|
||||
// Local Includes
|
||||
#include "state.h"
|
||||
|
||||
// FSM Class
|
||||
class CFSM
|
||||
{
|
||||
protected:
|
||||
CState * m_pCurrentState; // Current State
|
||||
CState * m_pNewState; // New State
|
||||
CStateTemplate<CFSM> m_stateInitial; // Initial State
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
CFSM();
|
||||
|
||||
// Destructor
|
||||
virtual ~CFSM() {}
|
||||
|
||||
// Global Functions
|
||||
virtual void Update();
|
||||
|
||||
// State Functions
|
||||
bool IsState(CState &State) const;
|
||||
bool GotoState(CState &NewState);
|
||||
|
||||
virtual void BeginStateInitial() {}
|
||||
virtual void StateInitial() {}
|
||||
virtual void EndStateInitial() {}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,136 @@
|
|||
#include "stdafx.h"
|
||||
#include "FileMonitor_FreeBSD.h"
|
||||
#include "../../libthecore/include/log.h"
|
||||
|
||||
|
||||
#define INVALID_KERNEL_EVENT -1
|
||||
|
||||
FileMonitorFreeBSD::FileMonitorFreeBSD()
|
||||
{
|
||||
m_KernelEventQueue = INVALID_KERNEL_EVENT;
|
||||
}
|
||||
|
||||
FileMonitorFreeBSD::~FileMonitorFreeBSD()
|
||||
{
|
||||
if( m_KernelEventQueue != INVALID_KERNEL_EVENT )
|
||||
{
|
||||
close ( m_KernelEventQueue );
|
||||
m_KernelEventQueue = INVALID_KERNEL_EVENT;
|
||||
}
|
||||
|
||||
TMonitorFileHashMap::iterator it;
|
||||
for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it )
|
||||
{
|
||||
close(it->second.fhMonitor);
|
||||
}
|
||||
|
||||
m_FileLists.clear();
|
||||
|
||||
m_MonitoredEventLists.clear();
|
||||
m_TriggeredEventLists.clear();
|
||||
}
|
||||
|
||||
|
||||
void FileMonitorFreeBSD::Update(DWORD dwPulses)
|
||||
{
|
||||
if( m_KernelEventQueue == INVALID_KERNEL_EVENT || m_FileLists.size() == 0 )
|
||||
return;
|
||||
|
||||
int nEvent = kevent(m_KernelEventQueue, &m_TriggeredEventLists[0], (int)m_TriggeredEventLists.size(), &m_MonitoredEventLists[0], (int)m_MonitoredEventLists.size(), NULL );
|
||||
if( nEvent == INVALID_KERNEL_EVENT )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if( nEvent > 0 )
|
||||
{
|
||||
for( int i = 0; i < nEvent; ++i )
|
||||
{
|
||||
int nEventFlags = m_MonitoredEventLists[i].flags;
|
||||
eFileUpdatedOptions eUpdateOption = e_FileUpdate_None;
|
||||
|
||||
if (nEventFlags & EV_ERROR)
|
||||
eUpdateOption = e_FileUpdate_Error;
|
||||
|
||||
else if (nEventFlags & NOTE_DELETE)
|
||||
eUpdateOption = e_FileUpdate_Deleted;
|
||||
|
||||
else if (nEventFlags & NOTE_EXTEND || nEventFlags & NOTE_WRITE)
|
||||
eUpdateOption = e_FileUpdate_Modified;
|
||||
|
||||
else if (nEventFlags & NOTE_ATTRIB)
|
||||
eUpdateOption = e_FileUpdate_AttrModified;
|
||||
|
||||
else if (nEventFlags & NOTE_LINK)
|
||||
eUpdateOption = e_FileUpdate_Linked;
|
||||
|
||||
else if (nEventFlags & NOTE_RENAME)
|
||||
eUpdateOption = e_FileUpdate_Renamed;
|
||||
|
||||
else if (nEventFlags & NOTE_REVOKE)
|
||||
eUpdateOption = e_FileUpdate_Revoked;
|
||||
|
||||
if( eUpdateOption != e_FileUpdate_None )
|
||||
{
|
||||
TMonitorFileHashMap::iterator it;
|
||||
for( it = m_FileLists.begin(); it != m_FileLists.end(); ++it )
|
||||
{
|
||||
FileIOContext_FreeBSD& context = it->second;
|
||||
if( context.idxToEventList == i )
|
||||
{
|
||||
std::string strModifedFileName = it->first;
|
||||
context.pListenFunc( strModifedFileName, eUpdateOption );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FileMonitorFreeBSD::AddWatch(const std::string& strFileName, PFN_FileChangeListener pListenerFunc)
|
||||
{
|
||||
int iFileHandle = -1;
|
||||
if( (iFileHandle = open(strFileName.c_str(), O_RDONLY)) == -1)
|
||||
{
|
||||
sys_err("FileMonitorFreeBSD:AddWatch : can`t open file(%s).\n", strFileName.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
//create kqueue if not exists
|
||||
if( m_KernelEventQueue == INVALID_KERNEL_EVENT )
|
||||
m_KernelEventQueue = kqueue();
|
||||
|
||||
if( m_KernelEventQueue == INVALID_KERNEL_EVENT )
|
||||
{
|
||||
sys_err("FileMonitorFreeBSD:AddWatch : failed to create kqueue.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TMonitorFileHashMap::iterator it = m_FileLists.find( strFileName );
|
||||
if( it != m_FileLists.end() )
|
||||
{
|
||||
sys_log(0, "FileMonitorFreeBSD:AddWatch : trying to add duplicated watch on file(%s).\n", strFileName.c_str() );
|
||||
return;
|
||||
}
|
||||
|
||||
//set file context
|
||||
FileIOContext_FreeBSD context;
|
||||
{
|
||||
context.fhMonitor = iFileHandle;
|
||||
context.idxToEventList = (int)m_MonitoredEventLists.size();
|
||||
context.pListenFunc = pListenerFunc;
|
||||
}
|
||||
|
||||
m_FileLists[strFileName] = context;
|
||||
|
||||
//set events
|
||||
struct kevent kTriggerEvent, kMonitorEvent;
|
||||
|
||||
EV_SET(&kTriggerEvent, iFileHandle, EVFILT_VNODE,
|
||||
EV_ADD | EV_ENABLE | EV_ONESHOT,
|
||||
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE,
|
||||
0, 0);
|
||||
|
||||
m_TriggeredEventLists.push_back( kTriggerEvent );
|
||||
m_MonitoredEventLists.push_back( kMonitorEvent );
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef FILEMONITOR_FREEBSD_INCLUDED
|
||||
#define FILEMONITOR_FREEBSD_INCLUDED
|
||||
|
||||
#include "IFileMonitor.h"
|
||||
#include <unistd.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/signal.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
struct FileIOContext_FreeBSD
|
||||
{
|
||||
int fhMonitor;
|
||||
int idxToEventList; // evtTrigger & evtMonitor index should be same
|
||||
PFN_FileChangeListener pListenFunc;
|
||||
};
|
||||
|
||||
class FileMonitorFreeBSD : public IFileMonitor
|
||||
{
|
||||
private:
|
||||
FileMonitorFreeBSD(); //hidden
|
||||
|
||||
public:
|
||||
virtual ~FileMonitorFreeBSD();
|
||||
|
||||
void AddWatch (const std::string& strFileName, PFN_FileChangeListener pListenerFunc);
|
||||
void Update (DWORD dwPulses);
|
||||
|
||||
static FileMonitorFreeBSD& Instance()
|
||||
{
|
||||
static FileMonitorFreeBSD theMonitor;
|
||||
return theMonitor;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::unordered_map<std::string, FileIOContext_FreeBSD> TMonitorFileHashMap;
|
||||
typedef std::vector<struct kevent> TEventList;
|
||||
|
||||
TMonitorFileHashMap m_FileLists;
|
||||
TEventList m_MonitoredEventLists;
|
||||
TEventList m_TriggeredEventLists;
|
||||
|
||||
int m_KernelEventQueue;
|
||||
};
|
||||
|
||||
|
||||
#endif //FILEMONITOR_FREEBSD_INCLUDED
|
|
@ -0,0 +1,54 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "HackShield.h"
|
||||
|
||||
#include "HackShield_Impl.h"
|
||||
#include "config.h"
|
||||
|
||||
bool CHackShieldManager::Initialize()
|
||||
{
|
||||
impl_ = M2_NEW CHackShieldImpl;
|
||||
|
||||
if (NULL == impl_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return impl_->Initialize();
|
||||
}
|
||||
|
||||
void CHackShieldManager::Release()
|
||||
{
|
||||
if (NULL != impl_)
|
||||
{
|
||||
impl_->Release();
|
||||
|
||||
M2_DELETE(impl_);
|
||||
|
||||
impl_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CHackShieldManager::CreateClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
return impl_->CreateClientHandle(dwPlayerID);
|
||||
}
|
||||
|
||||
void CHackShieldManager::DeleteClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
impl_->DeleteClientHandle(dwPlayerID);
|
||||
}
|
||||
|
||||
bool CHackShieldManager::SendCheckPacket(LPCHARACTER ch)
|
||||
{
|
||||
return impl_->SendCheckPacket(ch);
|
||||
}
|
||||
|
||||
bool CHackShieldManager::VerifyAck(LPCHARACTER ch, const void* buf)
|
||||
{
|
||||
TPacketGCHSCheck* p = reinterpret_cast<TPacketGCHSCheck*>(const_cast<void*>(buf));
|
||||
|
||||
return impl_->VerifyAck(ch, p);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
#ifndef HACK_SHIELD_MANAGER_H_
|
||||
#define HACK_SHIELD_MANAGER_H_
|
||||
|
||||
class CHackShieldImpl;
|
||||
|
||||
class CHackShieldManager : public singleton<CHackShieldManager>
|
||||
{
|
||||
public:
|
||||
bool Initialize ();
|
||||
void Release ();
|
||||
|
||||
bool CreateClientHandle (DWORD dwPlayerID);
|
||||
void DeleteClientHandle (DWORD dwPlayerID);
|
||||
|
||||
bool SendCheckPacket (LPCHARACTER ch);
|
||||
bool VerifyAck (LPCHARACTER ch, const void* buf);
|
||||
|
||||
private:
|
||||
CHackShieldImpl* impl_;
|
||||
};
|
||||
|
||||
#endif /* HACK_SHIELD_MANAGER_H_ */
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "HackShield_Impl.h"
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
|
||||
#include "char.h"
|
||||
#include "packet.h"
|
||||
#include "desc.h"
|
||||
#include "log.h"
|
||||
|
||||
bool CHackShieldImpl::Initialize()
|
||||
{
|
||||
handle_ = _AhnHS_CreateServerObject("metin2client.bin.hsb");
|
||||
|
||||
if (ANTICPX_INVALID_HANDLE_VALUE == handle_)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
sys_log(0, "HShield: Success to CreateServerObject");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHackShieldImpl::Release()
|
||||
{
|
||||
_AhnHS_CloseServerHandle(handle_);
|
||||
|
||||
sys_log(0, "HShield: Server Handle Closed");
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( dwPlayerID );
|
||||
|
||||
if (iter != CliehtHandleMap_.end())
|
||||
{
|
||||
sys_log(0, "HShield: Client Handle is already created for Player(%u)", dwPlayerID);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AHNHS_CLIENT_HANDLE handle = _AhnHS_CreateClientObject(handle_);
|
||||
|
||||
if (ANTICPX_INVALID_HANDLE_VALUE == handle)
|
||||
{
|
||||
sys_log(0, "HShield: Failed to create client handle for Player(%u)", dwPlayerID);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CliehtHandleMap_.insert( std::make_pair(dwPlayerID, handle) );
|
||||
|
||||
sys_log(0, "HShield: Success to create client handle for Player(%u)", dwPlayerID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
ClientHandleContainer::iterator iter = CliehtHandleMap_.find( dwPlayerID );
|
||||
|
||||
if (iter == CliehtHandleMap_.end())
|
||||
{
|
||||
sys_log(0, "HShield: there is no client handle for Player(%u)", dwPlayerID);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_AhnHS_CloseClientHandle(iter->second);
|
||||
|
||||
CliehtHandleMap_.erase(iter);
|
||||
|
||||
sys_log(0, "HShield: client handle deleted for Player(%u)", dwPlayerID);
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch)
|
||||
{
|
||||
if (NULL == ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() );
|
||||
|
||||
if (iter == CliehtHandleMap_.end())
|
||||
{
|
||||
sys_log(0, "HShield: Client Handle not create for Player(%u)", ch->GetPlayerID());
|
||||
return false;
|
||||
}
|
||||
|
||||
TPacketGCHSCheck pack;
|
||||
pack.bHeader = HEADER_GC_HS_REQUEST;
|
||||
|
||||
memset( &pack.Req, 0, sizeof(pack.Req));
|
||||
unsigned long ret = _AhnHS_MakeRequest( iter->second, &(pack.Req) );
|
||||
|
||||
if (0 != ret)
|
||||
{
|
||||
sys_log(0, "HShield: _AhnHS_MakeRequest return error(%u) for Player(%u)", ret, ch->GetPlayerID());
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log(0, "HShield: _AhnHS_MakeRequest success ret(%d)", ret);
|
||||
}
|
||||
|
||||
if (NULL != ch->GetDesc())
|
||||
{
|
||||
ch->GetDesc()->Packet( &pack, sizeof(pack) );
|
||||
sys_log(0, "HShield: Send Check Request for Player(%u)", ch->GetPlayerID());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
sys_log(0, "HShield: Failed to get DESC for Player(%u)", ch->GetPlayerID());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf)
|
||||
{
|
||||
if (NULL == ch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NeedDisconnect = false;
|
||||
|
||||
ClientHandleContainer::const_iterator iter = CliehtHandleMap_.find( ch->GetPlayerID() );
|
||||
|
||||
if (iter == CliehtHandleMap_.end())
|
||||
{
|
||||
sys_log(0, "HShield: Cannot Find ClientHandle For Verify");
|
||||
|
||||
NeedDisconnect = true;
|
||||
}
|
||||
|
||||
unsigned long dwError = 0;
|
||||
|
||||
unsigned long ret = _AhnHS_VerifyResponseEx( iter->second, buf->Req.byBuffer, buf->Req.nLength, &dwError );
|
||||
|
||||
if (ANTICPX_RECOMMAND_CLOSE_SESSION == ret)
|
||||
{
|
||||
sys_log(0, "HShield: not a valid ack ret(%u) error(%u) from Player(%u)", ret, dwError, ch->GetPlayerID());
|
||||
NeedDisconnect = true;
|
||||
|
||||
ch->StopHackShieldCheckCycle();
|
||||
}
|
||||
|
||||
if (NULL != ch->GetDesc())
|
||||
{
|
||||
if (true == NeedDisconnect)
|
||||
{
|
||||
ch->GetDesc()->SetPhase(PHASE_CLOSE);
|
||||
LogManager::instance().HackShieldLog(dwError, ch);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ch->SetHackShieldCheckMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
sys_log(0, "HShield: Valid Ack from Player(%u)", ch->GetPlayerID());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool CHackShieldImpl::Initialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHackShieldImpl::Release()
|
||||
{
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::CreateClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHackShieldImpl::DeleteClientHandle(DWORD dwPlayerID)
|
||||
{
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::SendCheckPacket(LPCHARACTER ch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHackShieldImpl::VerifyAck(LPCHARACTER ch, TPacketGCHSCheck* buf)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
|
||||
#ifndef HACK_SHIELD_IMPL_H_
|
||||
#define HACK_SHIELD_IMPL_H_
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
// Live build only
|
||||
#define UNIX
|
||||
#include <AntiCpXSvr.h>
|
||||
#undef UNIX
|
||||
#endif
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct SPacketGCHSCheck
|
||||
{
|
||||
BYTE bHeader;
|
||||
#ifdef __FreeBSD__
|
||||
AHNHS_TRANS_BUFFER Req;
|
||||
#endif
|
||||
} TPacketGCHSCheck;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
class CHackShieldImpl
|
||||
{
|
||||
public:
|
||||
bool Initialize ();
|
||||
void Release ();
|
||||
|
||||
bool CreateClientHandle (DWORD dwPlayerID);
|
||||
void DeleteClientHandle (DWORD dwPlayerID);
|
||||
|
||||
bool SendCheckPacket (LPCHARACTER ch);
|
||||
bool VerifyAck (LPCHARACTER ch, TPacketGCHSCheck* buf);
|
||||
|
||||
private:
|
||||
#ifdef __FreeBSD__
|
||||
AHNHS_SERVER_HANDLE handle_;
|
||||
|
||||
typedef boost::unordered_map<DWORD, AHNHS_CLIENT_HANDLE> ClientHandleContainer;
|
||||
ClientHandleContainer CliehtHandleMap_;
|
||||
|
||||
typedef boost::unordered_map<DWORD, bool> ClientCheckContainer;
|
||||
ClientCheckContainer ClientCheckMap_;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* HACK_SHIELD_IMPL_H_ */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue