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