Compare commits

..

6 Commits

Author SHA1 Message Date
Aaron Kimbrell
0dc4fab531 fix some missed things 2025-09-12 23:25:49 -05:00
Aaron Kimbrell
500a72dc11 Change LUBitstream to make more sense and only read what it needs from the bitstream at each level 2025-09-12 22:25:55 -05:00
Aaron Kimbrell
b36b440eba remove stray log 2025-09-11 00:27:24 -05:00
Aaron Kimbrell
d1e65f16f7 fix conflict 2025-09-10 23:14:05 -05:00
Aaron Kimbrell
e854d6f56a chore: convert all auth packets to LUBitStreams
Split things out to their correct files as well
2025-09-10 23:04:55 -05:00
Aaron Kimbrell
824d563fd2 WIP 2025-09-08 21:45:33 -05:00
453 changed files with 5464 additions and 10953 deletions

View File

@@ -1,29 +0,0 @@
# GitHub Copilot Instructions
* c++20 standard, please use the latest features except NO modules.
* use `.contains` for searching in associative containers
* use const as much as possible. If it can be const, it should be made const
* DO NOT USE const_cast EVER.
* use `cstdint` bitwidth types ALWAYS for integral types.
* NEVER use std::wstring. If wide strings are necessary, use std::u16string with conversion utilties in GeneralUtils.h.
* Functions are ALWAYS PascalCase.
* local variables are camelCase
* NEVER use snake case
* indentation is TABS, not SPACES.
* TABS are 4 spaces by default
* Use trailing braces ALWAYS
* global variables are prefixed with `g_`
* if global variables or functions are needed, they should be located in an anonymous namespace
* Use `GeneralUtils::TryParse` for ANY parsing of strings to integrals.
* Use brace initialization when possible.
* ALWAYS default initialize variables.
* Pointers should be avoided unless necessary. Use references when the pointer has been checked and should not be null
* headers should be as compact as possible. Do NOT include extra data that isnt needed.
* Remember to include logs (LOG macro uses printf style logging) while putting verbose logs under LOG_DEBUG.
* NEVER USE `RakNet::BitStream::ReadBit`
* NEVER assume pointers are good, always check if they are null. Once a pointer is checked and is known to be non-null, further accesses no longer need checking
* Be wary of TOCTOU. Prevent all possible issues relating to TOCTOU.
* new memory allocations should never be used unless absolutely necessary.
* new for reconstruction of objects is allowed
* Prefer following the format of the file over correct formatting. Consistency over correctness.
* When using auto, ALWAYS put a * for pointers.

View File

@@ -1,14 +1,14 @@
name: Docker name: CI
on: on:
push: push:
branches: branches:
- main - "main"
tags: tags:
- "v*.*.*" - "v*.*.*"
pull_request: pull_request:
branches: branches:
- main - "main"
env: env:
REGISTRY: ghcr.io REGISTRY: ghcr.io
@@ -20,21 +20,15 @@ jobs:
permissions: permissions:
contents: read contents: read
packages: write packages: write
attestations: write
id-token: write
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Log in to the Container registry - name: Log in to the Container registry
if: github.event_name != 'pull_request' uses: docker/login-action@v3
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -42,32 +36,21 @@ jobs:
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0 uses: docker/metadata-action@v5
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: | tags: |
type=ref,event=pr type=ref,event=pr
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=raw,value=canary,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=semver,pattern={{version}} type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
- name: Build and push Docker image - name: Build and push Docker image
id: push uses: docker/build-push-action@v5
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with: with:
context: . context: .
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Sign Docker image
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

View File

@@ -3,8 +3,6 @@ name: CI
on: on:
push: push:
branches: [ main ] branches: [ main ]
tags:
- "v*.*.*"
pull_request: pull_request:
branches: [ main ] branches: [ main ]
@@ -12,51 +10,38 @@ jobs:
build-and-test: build-and-test:
name: Build & Test (${{ matrix.os }}) name: Build & Test (${{ matrix.os }})
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
continue-on-error: ${{ github.event_name == 'pull_request' }} continue-on-error: true
strategy: strategy:
matrix: matrix:
include: os: [ windows-2022, ubuntu-22.04, macos-13 ]
- os: windows-2025
artifact: windows
debug_preset: windows-msvc-relwithdebinfo
- os: ubuntu-24.04
artifact: linux
debug_preset: linux-gnu-relwithdebinfo
- os: macos-15-intel
artifact: macos
debug_preset: macos-relwithdebinfo
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2
with: with:
submodules: true submodules: true
- name: Add msbuild to PATH (Windows only) - name: Add msbuild to PATH (Windows only)
if: ${{ matrix.os == 'windows-2025' }} if: ${{ matrix.os == 'windows-2022' }}
uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3 uses: microsoft/setup-msbuild@767f00a3f09872d96a0cb9fcd5e6a4ff33311330
with: with:
vs-version: '[18,19)' vs-version: '[17,18)'
msbuild-architecture: x64 msbuild-architecture: x64
- name: Get CMake - name: Install libssl and switch to XCode 15.2 (Mac Only)
uses: lukka/get-cmake@591817e96fcad43505fb4eae36172462abb3a42e # v4.3.3 if: ${{ matrix.os == 'macos-13' }}
with:
cmakeVersion: "latest"
- name: cmake
uses: lukka/run-cmake@5d55ea7949e25f69f0ecb516d8d572297e03a956 # v10.9
with:
workflowPreset: "${{ matrix.debug_preset }}"
- name: Extract Linux debug symbols
if: matrix.os == 'ubuntu-24.04'
run: | run: |
find build -type f -name '*Server' | while read bin; do brew install openssl@3
objcopy --only-keep-debug "$bin" "${bin}.debug" sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
objcopy --strip-debug --add-gnu-debuglink="${bin}.debug" "$bin" - name: Get CMake 3.x
done uses: lukka/get-cmake@28983e0d3955dba2bb0a6810caae0c6cf268ec0c
- name: artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with: with:
name: build-${{matrix.artifact}} cmakeVersion: "~3.25.0" # <--= optional, use most recent 3.25.x version
- name: cmake
uses: lukka/run-cmake@67c73a83a46f86c4e0b96b741ac37ff495478c38
with:
workflowPreset: "ci-${{matrix.os}}"
- name: artifacts
uses: actions/upload-artifact@6027e3dd177782cd8ab9af838c04fd81a07f1d47
with:
name: build-${{matrix.os}}
path: | path: |
build/*/*Server* build/*/*Server*
build/*/*.ini build/*/*.ini
@@ -67,30 +52,5 @@ jobs:
build/*/navmeshes/ build/*/navmeshes/
build/*/migrations/ build/*/migrations/
build/*/*.dcf build/*/*.dcf
!build/*/*.pdb
!build/*/d*/ !build/*/d*/
!build/*/*.dSYM/
!build/**/*.debug
- name: debug symbols (Windows)
if: matrix.os == 'windows-2025'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: debug-${{matrix.artifact}}
path: |
build/*/*.pdb
build/*/d*/
retention-days: 30
- name: debug symbols (Linux)
if: matrix.os == 'ubuntu-24.04'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: debug-${{matrix.artifact}}
path: build/**/*.debug
retention-days: 30
- name: debug symbols (macOS)
if: matrix.os == 'macos-15-intel'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: debug-${{matrix.artifact}}
path: build/**/*.dSYM/
retention-days: 30

View File

@@ -1,93 +0,0 @@
name: Canary
on:
workflow_run:
workflows: ["CI"]
branches: [main]
types: [completed]
permissions:
contents: write
actions: read
jobs:
canary-release:
name: Publish Canary Release
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
ref: ${{ github.event.workflow_run.head_sha }}
- name: Get last release tag
id: last_tag
run: |
tag=$(git describe --tags --abbrev=0 --match "v*.*.*" 2>/dev/null || echo "none")
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Generate changelog since last release tag
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0
id: cliff
with:
config: cliff.toml
args: --unreleased --strip header
env:
OUTPUT: CHANGES.md
GITHUB_REPO: ${{ github.repository }}
- name: Prepend header to changelog
run: |
last="${{ steps.last_tag.outputs.tag }}"
sha="${{ github.event.workflow_run.head_sha }}"
short="${sha:0:7}"
if [ "$last" != "none" ]; then
header="Changes since **$last** ([full diff](https://github.com/${{ github.repository }}/compare/${last}...${sha}))\n\n"
else
header="Changes up to \`${short}\`\n\n"
fi
printf "%b" "$header" | cat - CHANGES.md > CHANGES.tmp && mv CHANGES.tmp CHANGES.md
- name: Download artifacts from CI run
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts/
- name: Package artifacts
run: |
declare -A platform_map=(
["build-windows"]="darkflame-universe-windows"
["build-linux"]="darkflame-universe-linux"
["build-macos"]="darkflame-universe-macos"
)
cd artifacts
for dir in build-*/; do
name="${dir%/}"
out="${platform_map[$name]:-$name}"
zip -r "../${out}.zip" "$dir"
done
cd ..
ls -lh *.zip
- name: Delete existing canary release
run: gh release delete canary --yes --cleanup-tag 2>/dev/null || true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create canary pre-release
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
with:
tag: canary
name: "Canary ${{ steps.last_tag.outputs.tag }}+${{ github.event.workflow_run.head_sha }}"
bodyFile: CHANGES.md
artifacts: "*.zip"
artifactContentType: application/zip
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: true
draft: false
allowUpdates: true
removeArtifacts: true
commit: ${{ github.event.workflow_run.head_sha }}

View File

@@ -1,38 +0,0 @@
name: PR Title Check
on:
pull_request_target:
types: [opened, edited, synchronize, reopened]
permissions:
pull-requests: write
statuses: write
jobs:
check-title:
name: Conventional Commit Title
runs-on: ubuntu-latest
steps:
- name: Check PR title follows Conventional Commits
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
perf
refactor
docs
chore
test
ci
revert
requireScope: false
subjectPattern: ^.+$
subjectPatternError: |
The PR title "{title}" must have a description after the type/scope prefix.
Example: "feat: add new login flow" or "fix(auth): handle null token"
wip: true
validateSingleCommit: false

View File

@@ -1,70 +0,0 @@
name: Release
on:
workflow_run:
workflows: ["CI"]
types: [completed]
permissions:
contents: write
actions: read
jobs:
release:
name: Create Release
# Only run when CI completed successfully on a tag push (not a PR branch named like a version)
if: |
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
startsWith(github.event.workflow_run.head_branch, 'v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
ref: ${{ github.event.workflow_run.head_sha }}
- name: Generate changelog
uses: orhun/git-cliff-action@f50e11560dce63f7c33227798f90b924471a88b5 # v4.8.0
id: cliff
with:
config: cliff.toml
args: --latest --strip header
env:
OUTPUT: CHANGES.md
GITHUB_REPO: ${{ github.repository }}
- name: Download artifacts from CI run
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
with:
run_id: ${{ github.event.workflow_run.id }}
path: artifacts/
- name: Package artifacts
run: |
declare -A platform_map=(
["build-windows"]="darkflame-universe-windows"
["build-linux"]="darkflame-universe-linux"
["build-macos"]="darkflame-universe-macos"
)
cd artifacts
for dir in build-*/; do
name="${dir%/}"
out="${platform_map[$name]:-$name}"
zip -r "../${out}.zip" "$dir"
done
cd ..
ls -lh *.zip
- name: Create GitHub Release
uses: ncipollo/release-action@339a81892b84b4eeb0f6e744e4574d79d0d9b8dd # v1.21.0
with:
tag: ${{ github.event.workflow_run.head_branch }}
name: ${{ github.event.workflow_run.head_branch }}
bodyFile: CHANGES.md
artifacts: "*.zip"
artifactContentType: application/zip
token: ${{ secrets.GITHUB_TOKEN }}
prerelease: false
draft: false

2
.gitignore vendored
View File

@@ -126,5 +126,3 @@ docker-compose.override.yml
# CMake scripts # CMake scripts
!cmake/* !cmake/*
!cmake/toolchains/* !cmake/toolchains/*
.mcp.json
.claude/

View File

@@ -15,7 +15,6 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
@@ -68,11 +67,7 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled no-register # Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas. # Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
if(UNIX) if(UNIX)
if(APPLE) add_link_options("-Wl,-rpath,$ORIGIN/")
add_link_options("-Wl,-rpath,@loader_path/")
else()
add_link_options("-Wl,-rpath,$ORIGIN/")
endif()
add_compile_options("-fPIC") add_compile_options("-fPIC")
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)

View File

@@ -49,7 +49,7 @@
"inherits": "default", "inherits": "default",
"displayName": "[Multi] Windows (MSVC)", "displayName": "[Multi] Windows (MSVC)",
"description": "Set architecture to 64-bit (b/c RakNet)", "description": "Set architecture to 64-bit (b/c RakNet)",
"generator": "Visual Studio 18 2026", "generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/msvc", "binaryDir": "${sourceDir}/build/msvc",
"architecture": { "architecture": {
"value": "x64" "value": "x64"
@@ -162,13 +162,6 @@
"rhs": "Darwin" "rhs": "Darwin"
}, },
"binaryDir": "${sourceDir}/build/macos" "binaryDir": "${sourceDir}/build/macos"
},
{
"name": "macos-relwithdebinfo",
"inherits": ["macos", "relwithdebinfo-config"],
"displayName": "[RelWithDebInfo] MacOS",
"description": "Create a RelWithDebInfo build for MacOS",
"binaryDir": "${sourceDir}/build/macos-relwithdebinfo"
} }
], ],
"buildPresets": [ "buildPresets": [
@@ -262,7 +255,7 @@
{ {
"name": "macos-relwithdebinfo", "name": "macos-relwithdebinfo",
"inherits": "default", "inherits": "default",
"configurePreset": "macos-relwithdebinfo", "configurePreset": "macos",
"displayName": "[RelWithDebInfo] MacOS", "displayName": "[RelWithDebInfo] MacOS",
"description": "This preset is used to build in release mode with debug info on MacOS", "description": "This preset is used to build in release mode with debug info on MacOS",
"configuration": "RelWithDebInfo" "configuration": "RelWithDebInfo"
@@ -381,7 +374,7 @@
{ {
"name": "macos-relwithdebinfo", "name": "macos-relwithdebinfo",
"inherits": "default", "inherits": "default",
"configurePreset": "macos-relwithdebinfo", "configurePreset": "macos",
"displayName": "[RelWithDebInfo] MacOS", "displayName": "[RelWithDebInfo] MacOS",
"description": "Runs all tests on a MacOS configuration", "description": "Runs all tests on a MacOS configuration",
"configuration": "RelWithDebInfo" "configuration": "RelWithDebInfo"
@@ -610,7 +603,7 @@
"steps": [ "steps": [
{ {
"type": "configure", "type": "configure",
"name": "macos-relwithdebinfo" "name": "macos"
}, },
{ {
"type": "build", "type": "build",
@@ -623,7 +616,7 @@
] ]
}, },
{ {
"name": "ci-macos-15-intel", "name": "ci-macos-13",
"displayName": "[Release] MacOS", "displayName": "[Release] MacOS",
"description": "CI workflow preset for MacOS", "description": "CI workflow preset for MacOS",
"steps": [ "steps": [

View File

@@ -16,9 +16,7 @@ RUN --mount=type=cache,target=/app/build,id=build-cache \
cd /app/build && \ cd /app/build && \
cmake .. && \ cmake .. && \
make -j$(nproc --ignore 1) && \ make -j$(nproc --ignore 1) && \
cp -r /app/build/* /tmp/persisted-build/ && \ cp -r /app/build/* /tmp/persisted-build/
mkdir -p /tmp/persisted-build/mariadbcpp && \
cp /app/build/thirdparty/mariadb-connector-cpp/src/mariadb_connector_cpp-build/libmariadbcpp.so /tmp/persisted-build/mariadbcpp/
FROM debian:12 as runtime FROM debian:12 as runtime

View File

@@ -1,35 +0,0 @@
[changelog]
header = ""
body = """
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.scope %}**{{ commit.scope }}**: {% endif %}{{ commit.message }} ([{{ commit.id | truncate(length=7, end="") }}](https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}/commit/{{ commit.id }}))\
{% if commit.github.username %} by [@{{ commit.github.username }}](https://github.com/{{ commit.github.username }}){% endif %}
{% endfor %}
{% endfor %}
"""
footer = ""
trim = true
[git]
conventional_commits = true
filter_unconventional = true
split_commits = false
commit_parsers = [
{ message = "^feat", group = "Features" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^perf", group = "Performance" },
{ message = "^refactor", group = "Refactoring" },
{ message = "^docs", group = "Documentation" },
{ message = "^chore", group = "Chores" },
{ message = "^test", group = "Testing" },
{ message = "^ci", group = "CI/CD" },
{ message = "^revert", group = "Reverts" },
]
filter_commits = false
tag_pattern = "v[0-9].*"
skip_tags = "canary"
ignore_tags = ""
topo_order = false
sort_commits = "oldest"

View File

@@ -7,7 +7,6 @@ FetchContent_Declare(
GIT_REPOSITORY https://github.com/google/googletest.git GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1 GIT_TAG release-1.12.1
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
GIT_SHALLOW 1
) )
# For Windows: Prevent overriding the parent project's compiler/linker settings # For Windows: Prevent overriding the parent project's compiler/linker settings

View File

@@ -10,15 +10,14 @@ if(WIN32 AND NOT MARIADB_BUILD_SOURCE)
file(MAKE_DIRECTORY "${MARIADB_MSI_DIR}") file(MAKE_DIRECTORY "${MARIADB_MSI_DIR}")
file(MAKE_DIRECTORY "${MARIADB_CONNECTOR_DIR}") file(MAKE_DIRECTORY "${MARIADB_CONNECTOR_DIR}")
# These values track the published Windows MSI packages used by the prebuilt path. # These values need to be updated whenever a new minor release replaces an old one
# Keep the Connector/C++ package version aligned with the checked out submodule tag when possible. # Go to https://mariadb.com/downloads/connectors/ to find the up-to-date URL parts
# Go to https://mariadb.com/downloads/connectors/ to find the up-to-date URL parts. set(MARIADB_CONNECTOR_C_VERSION "3.2.7")
set(MARIADB_CONNECTOR_C_VERSION "3.4.8") set(MARIADB_CONNECTOR_C_BUCKET "2319651")
set(MARIADB_CONNECTOR_C_BUCKET "4516894") set(MARIADB_CONNECTOR_C_MD5 "f8636d733f1d093af9d4f22f3239f885")
set(MARIADB_CONNECTOR_C_MD5 "50f6fc0c77b8d3bacbeac0126e179861") set(MARIADB_CONNECTOR_CPP_VERSION "1.0.2")
set(MARIADB_CONNECTOR_CPP_VERSION "1.1.7") set(MARIADB_CONNECTOR_CPP_BUCKET "2531525")
set(MARIADB_CONNECTOR_CPP_BUCKET "4464908") set(MARIADB_CONNECTOR_CPP_MD5 "3034bbd6ca00a0125345f9fd1a178401")
set(MARIADB_CONNECTOR_CPP_MD5 "08644a7ff084b5933325cadb904796e5")
set(MARIADB_CONNECTOR_C_MSI "mariadb-connector-c-${MARIADB_CONNECTOR_C_VERSION}-win64.msi") set(MARIADB_CONNECTOR_C_MSI "mariadb-connector-c-${MARIADB_CONNECTOR_C_VERSION}-win64.msi")
set(MARIADB_CONNECTOR_CPP_MSI "mariadb-connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}-win64.msi") set(MARIADB_CONNECTOR_CPP_MSI "mariadb-connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}-win64.msi")
@@ -80,39 +79,23 @@ else() # Build from source
-DWITH_EXTERNAL_ZLIB=ON -DWITH_EXTERNAL_ZLIB=ON
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR} -DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
-DCMAKE_C_FLAGS=-w # disable zlib warnings -DCMAKE_C_FLAGS=-w # disable zlib warnings
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0\ -Wno-inconsistent-missing-override\ -include\ cstdint) -DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
else() else()
set(MARIADB_EXTRA_CMAKE_ARGS set(MARIADB_EXTRA_CMAKE_ARGS
-DCMAKE_C_FLAGS=-w # disable zlib warnings -DCMAKE_C_FLAGS=-w # disable zlib warnings
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0\ -include\ cstdint) -DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
endif() endif()
set(MARIADBCPP_BUILD_DIR "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp/src/mariadb_connector_cpp-build")
set(MARIADBCPP_INSTALL_DIR ${PROJECT_BINARY_DIR}/prefix) set(MARIADBCPP_INSTALL_DIR ${PROJECT_BINARY_DIR}/prefix)
set(MARIADBCPP_LIBRARY_DIR ${PROJECT_BINARY_DIR}/mariadbcpp)
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_LIBRARY_DIR}/plugin)
set(MARIADBCPP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp) set(MARIADBCPP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp)
set(MARIADB_INCLUDE_DIR "${MARIADBCPP_SOURCE_DIR}/include") set(MARIADB_INCLUDE_DIR "${MARIADBCPP_SOURCE_DIR}/include")
if(WIN32)
set(MARIADBCPP_LIBRARY_DIR ${PROJECT_BINARY_DIR}/mariadbcpp)
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_LIBRARY_DIR}/plugin)
set(MARIADB_INSTALL_COMMAND)
else()
set(MARIADBCPP_LIBRARY_DIR ${MARIADBCPP_BUILD_DIR})
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_BUILD_DIR}/libmariadb)
set(MARIADB_INSTALL_COMMAND INSTALL_COMMAND ${CMAKE_COMMAND} -E true)
endif()
if(CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set(MARIADB_POLICY_VERSION_ARG -DCMAKE_POLICY_VERSION_MINIMUM=3.5)
endif()
ExternalProject_Add(mariadb_connector_cpp ExternalProject_Add(mariadb_connector_cpp
PREFIX "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp" PREFIX "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp"
SOURCE_DIR ${MARIADBCPP_SOURCE_DIR} SOURCE_DIR ${MARIADBCPP_SOURCE_DIR}
BINARY_DIR ${MARIADBCPP_BUILD_DIR}
INSTALL_DIR ${MARIADBCPP_INSTALL_DIR} INSTALL_DIR ${MARIADBCPP_INSTALL_DIR}
CMAKE_ARGS -Wno-dev CMAKE_ARGS -Wno-dev
${MARIADB_POLICY_VERSION_ARG}
-DWITH_UNIT_TESTS=OFF -DWITH_UNIT_TESTS=OFF
-DMARIADB_LINK_DYNAMIC=OFF -DMARIADB_LINK_DYNAMIC=OFF
-DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN} -DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN}
@@ -120,7 +103,6 @@ else() # Build from source
-DINSTALL_LIBDIR=${MARIADBCPP_LIBRARY_DIR} -DINSTALL_LIBDIR=${MARIADBCPP_LIBRARY_DIR}
-DINSTALL_PLUGINDIR=${MARIADBCPP_PLUGIN_DIR} -DINSTALL_PLUGINDIR=${MARIADBCPP_PLUGIN_DIR}
${MARIADB_EXTRA_CMAKE_ARGS} ${MARIADB_EXTRA_CMAKE_ARGS}
${MARIADB_INSTALL_COMMAND}
BUILD_ALWAYS true BUILD_ALWAYS true
) )

View File

@@ -20,6 +20,7 @@
//Auth includes: //Auth includes:
#include "AuthPackets.h" #include "AuthPackets.h"
#include "CommonPackets.h"
#include "ServiceType.h" #include "ServiceType.h"
#include "MessageType/Server.h" #include "MessageType/Server.h"
#include "MessageType/Auth.h" #include "MessageType/Auth.h"
@@ -52,7 +53,6 @@ int main(int argc, char** argv) {
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Server::SetupLogger("AuthServer"); Server::SetupLogger("AuthServer");
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
Game::config->LogSettings();
LOG("Starting Auth server..."); LOG("Starting Auth server...");
LOG("Version: %s", PROJECT_VERSION); LOG("Version: %s", PROJECT_VERSION);
@@ -165,17 +165,12 @@ int main(int argc, char** argv) {
} }
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 4) return; CINSTREAM;
LUBitStream luBitStream;
if (packet->data[0] == ID_USER_PACKET_ENUM) { if (!luBitStream.ReadHeader(inStream) && luBitStream.rakNetID != ID_USER_PACKET_ENUM) return;
if (static_cast<ServiceType>(packet->data[1]) == ServiceType::COMMON) { else if (luBitStream.serviceType == ServiceType::COMMON) {
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) { CommonPackets::Handle(inStream, packet->systemAddress);
AuthPackets::HandleHandshake(Game::server, packet); } else if (luBitStream.serviceType == ServiceType::AUTH) {
} AuthPackets::Handle(inStream, packet->systemAddress);
} else if (static_cast<ServiceType>(packet->data[1]) == ServiceType::AUTH) {
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
AuthPackets::HandleLoginRequest(Game::server, packet);
}
}
} }
} }

View File

@@ -3,7 +3,6 @@
#include "MessageType/Chat.h" #include "MessageType/Chat.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Game.h" #include "Game.h"
#include "dConfig.h"
#include "Logger.h" #include "Logger.h"
#include "eObjectBits.h" #include "eObjectBits.h"
@@ -35,7 +34,7 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
if (!receiver.ignoredPlayers.empty()) { if (!receiver.ignoredPlayers.empty()) {
LOG_DEBUG("Player %llu already has an ignore list, but is requesting it again.", playerId); LOG_DEBUG("Player %llu already has an ignore list, but is requesting it again.", playerId);
} else { } else {
auto ignoreList = Database::Get()->GetIgnoreList(playerId); auto ignoreList = Database::Get()->GetIgnoreList(static_cast<uint32_t>(playerId));
if (ignoreList.empty()) { if (ignoreList.empty()) {
LOG_DEBUG("Player %llu has no ignores", playerId); LOG_DEBUG("Player %llu has no ignores", playerId);
return; return;
@@ -44,6 +43,7 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) {
for (auto& ignoredPlayer : ignoreList) { for (auto& ignoredPlayer : ignoreList) {
receiver.ignoredPlayers.emplace_back(ignoredPlayer.name, ignoredPlayer.id); receiver.ignoredPlayers.emplace_back(ignoredPlayer.name, ignoredPlayer.id);
GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::CHARACTER); GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::PERSISTENT);
} }
} }
@@ -73,8 +73,8 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
return; return;
} }
const int32_t MAX_IGNORES = Game::config->GetValue("max_ignores", 32); constexpr int32_t MAX_IGNORES = 32;
if (receiver.ignoredPlayers.size() >= MAX_IGNORES) { if (receiver.ignoredPlayers.size() > MAX_IGNORES) {
LOG_DEBUG("Player %llu has too many ignores", playerId); LOG_DEBUG("Player %llu has too many ignores", playerId);
return; return;
} }
@@ -114,8 +114,9 @@ void ChatIgnoreList::AddIgnore(Packet* packet) {
} }
if (ignoredPlayerId != LWOOBJID_EMPTY) { if (ignoredPlayerId != LWOOBJID_EMPTY) {
Database::Get()->AddIgnore(playerId, ignoredPlayerId); Database::Get()->AddIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(ignoredPlayerId));
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER); GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT);
receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId); receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId);
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str()); LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
@@ -156,7 +157,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) {
return; return;
} }
Database::Get()->RemoveIgnore(playerId, toRemove->playerId); Database::Get()->RemoveIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(toRemove->playerId));
receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end()); receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end());
CBITSTREAM; CBITSTREAM;

View File

@@ -35,6 +35,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
FriendData fd; FriendData fd;
fd.isFTP = false; // not a thing in DLU fd.isFTP = false; // not a thing in DLU
fd.friendID = friendData.friendID; fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
@@ -160,7 +161,9 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Set the bits // Set the bits
GeneralUtils::SetBit(queryPlayerID, eObjectBits::CHARACTER); GeneralUtils::SetBit(queryPlayerID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(queryPlayerID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(queryFriendID, eObjectBits::CHARACTER); GeneralUtils::SetBit(queryFriendID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(queryFriendID, eObjectBits::PERSISTENT);
// Since this player can either be the friend of someone else or be friends with someone else // Since this player can either be the friend of someone else or be friends with someone else
// their column in the database determines what bit gets set. When the value hits 3, they // their column in the database determines what bit gets set. When the value hits 3, they
@@ -315,6 +318,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
} }
// Convert friendID to LWOOBJID // Convert friendID to LWOOBJID
GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER); GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER);
Database::Get()->RemoveFriend(playerID, friendID); Database::Get()->RemoveFriend(playerID, friendID);
@@ -435,11 +439,6 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
inStream.IgnoreBytes(4); inStream.IgnoreBytes(4);
inStream.Read(channel); inStream.Read(channel);
inStream.Read(size); inStream.Read(size);
if (size > MAX_MESSAGE_LENGTH) {
LOG("Received a probably spoofed chat message, ignoring msg");
return;
}
inStream.IgnoreBytes(77); inStream.IgnoreBytes(77);
LUWString message(size); LUWString message(size);
@@ -484,11 +483,6 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!"); if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!");
inStream.Read(size); inStream.Read(size);
if (size > MAX_MESSAGE_LENGTH) {
LOG("Received a probably spoofed chat message, ignoring msg");
return;
}
inStream.IgnoreBytes(77); inStream.IgnoreBytes(77);
inStream.Read(LUReceiverName); inStream.Read(LUReceiverName);

View File

@@ -59,7 +59,6 @@ int main(int argc, char** argv) {
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Server::SetupLogger("ChatServer"); Server::SetupLogger("ChatServer");
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
Game::config->LogSettings();
//Read our config: //Read our config:
@@ -202,11 +201,8 @@ int main(int argc, char** argv) {
//Delete our objects here: //Delete our objects here:
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
Game::server = nullptr;
delete Game::logger; delete Game::logger;
Game::logger = nullptr;
delete Game::config; delete Game::config;
Game::config = nullptr;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@@ -105,7 +105,15 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
auto* team = TeamContainer::GetTeam(playerID); auto* team = TeamContainer::GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
TeamContainer::RemoveMember(team, playerID, false, false, true); const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId);
if (!otherMember) continue;
TeamContainer::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
}
} }
ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut); ChatWeb::SendWSPlayerUpdate(player, eActivityType::PlayerLoggedOut);
@@ -168,7 +176,6 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
return toReturn; return toReturn;
} }
// TODO Make this a pointer again or do something to make it so you cant edit the LWOOBJID_EMPTY entry? it should be ignored in all cases anyways though...
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY]; return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
} }

View File

@@ -477,7 +477,7 @@ TeamData* TeamContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
} }
} }
newTeam->lootFlag = 0; newTeam->lootFlag = 1;
TeamStatusUpdate(newTeam); TeamStatusUpdate(newTeam);

View File

@@ -3,7 +3,6 @@
#include <stdexcept> #include <stdexcept>
#include "Amf3.h" #include "Amf3.h"
#include "StringifiedEnum.h"
/** /**
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf * AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
@@ -54,7 +53,7 @@ std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream)
case eAmf::VectorObject: case eAmf::VectorObject:
[[fallthrough]]; [[fallthrough]];
case eAmf::Dictionary: case eAmf::Dictionary:
throw std::invalid_argument(StringifiedEnum::ToString(marker).data()); throw marker;
default: default:
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker))); throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
} }
@@ -89,11 +88,6 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
// Right shift by 1 bit to get index if reference or size of next string if value // Right shift by 1 bit to get index if reference or size of next string if value
length = length >> 1; length = length >> 1;
if (isReference) { if (isReference) {
constexpr int32_t maxStringSize = 1024 * 1024;
if (length > maxStringSize) {
LOG("1MB string attempted to be allocated in AMF deserialize, possible spoof, aborting deserialize.");
throw std::invalid_argument("1MB string attempted to be allocated in AMF deserialize, possible spoof, aborting deserialize.");
}
std::string value(length, 0); std::string value(length, 0);
inStream.Read(&value[0], length); inStream.Read(&value[0], length);
// Empty strings are never sent by reference // Empty strings are never sent by reference
@@ -123,12 +117,6 @@ std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& i
if (key.size() == 0) break; if (key.size() == 0) break;
arrayValue->Insert(key, Read(inStream)); arrayValue->Insert(key, Read(inStream));
} }
constexpr int32_t maxArraySize = 10'000;
if (sizeOfDenseArray > maxArraySize) {
LOG("Someone sent 10,000 dense array entries, probably a bad packet.");
throw std::invalid_argument("Someone sent 10,000 dense array entries, probably a bad packet.");
}
// Finally read dense portion // Finally read dense portion
for (uint32_t i = 0; i < sizeOfDenseArray; i++) { for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
arrayValue->Insert(i, Read(inStream)); arrayValue->Insert(i, Read(inStream));

View File

@@ -369,39 +369,11 @@ public:
template<typename AmfType = AMFArrayValue> template<typename AmfType = AMFArrayValue>
AmfType& PushDebug(const std::string_view name) { AmfType& PushDebug(const std::string_view name) {
size_t i = 0;
for (; i < m_Dense.size(); i++) {
const auto& cast = dynamic_cast<AMFArrayValue*>(m_Dense[i].get());
if (!cast) continue;
const auto& nameValue = cast->Get<std::string>("name");
if (!nameValue || nameValue->GetValue() != name) continue;
// found a duplicate, return this instead
auto valueCast = dynamic_cast<AmfType*>(cast->Get("value"));
if (valueCast) return *valueCast;
}
auto* value = PushArray(); auto* value = PushArray();
value->Insert<std::string>("name", name.data()); value->Insert("name", name.data());
return value->Insert<AmfType>("value", std::make_unique<AmfType>()); return value->Insert<AmfType>("value", std::make_unique<AmfType>());
} }
AMFArrayValue& PushDebug(const NiPoint3& point) {
PushDebug<AMFDoubleValue>("X") = point.x;
PushDebug<AMFDoubleValue>("Y") = point.y;
PushDebug<AMFDoubleValue>("Z") = point.z;
return *this;
}
AMFArrayValue& PushDebug(const NiQuaternion& rot) {
PushDebug<AMFDoubleValue>("W") = rot.w;
PushDebug<AMFDoubleValue>("X") = rot.x;
PushDebug<AMFDoubleValue>("Y") = rot.y;
PushDebug<AMFDoubleValue>("Z") = rot.z;
return *this;
}
private: private:
/** /**
* The associative portion. These values are key'd with strings to an AMFValue. * The associative portion. These values are key'd with strings to an AMFValue.

View File

@@ -52,7 +52,8 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
if (actualUncompressedSize != -1) { if (actualUncompressedSize != -1) {
uint32_t previousSize = completeUncompressedModel.size(); uint32_t previousSize = completeUncompressedModel.size();
completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()), actualUncompressedSize); completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
} else { } else {
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err); LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
break; break;

View File

@@ -49,24 +49,24 @@ if (UNIX)
elseif (WIN32) elseif (WIN32)
include(FetchContent) include(FetchContent)
# TODO Keep an eye on the zlib repository for an update to disable testing. Don't forget to update CMakePresets
FetchContent_Declare( FetchContent_Declare(
zlib zlib
URL https://github.com/madler/zlib/archive/refs/tags/v1.3.2.zip URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip
URL_HASH MD5=adbba6eef8960c3412818b2e241f46dc URL_HASH MD5=9d6a627693163bbbf3f26403a3a0b0b1
GIT_PROGRESS TRUE GIT_PROGRESS TRUE
GIT_SHALLOW 1
) )
# Disable warning about no project version. # Disable warning about no project version.
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW) set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
# Disable warning about the minimum version of cmake used for bcrypt being deprecated in the future # Disable warning about the minimum version of cmake used for bcrypt being deprecated in the future
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE)
# Disable zlib tests
set(ZLIB_BUILD_TESTING OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(zlib) FetchContent_MakeAvailable(zlib)
set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR}) set(ZLIB_INCLUDE_DIRS ${zlib_SOURCE_DIR} ${zlib_BINARY_DIR})
set_target_properties(zlib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ZLIB_INCLUDE_DIRS}")
add_library(ZLIB::ZLIB ALIAS zlib)
else () else ()
message( message(
FATAL_ERROR FATAL_ERROR

View File

@@ -308,9 +308,8 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
for (const auto& t : std::filesystem::directory_iterator(folder)) { for (const auto& t : std::filesystem::directory_iterator(folder)) {
if (t.is_directory() || t.is_symlink()) continue; if (t.is_directory() || t.is_symlink()) continue;
auto filename = t.path().filename().string(); auto filename = t.path().filename().string();
// Ensure the file has a name in the format of xxxxxxxx_anything_goes_here.sql const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
const auto migrationNumber = TryParse<uint32_t>(GeneralUtils::SplitString(filename, '_').at(0)); filenames.emplace(index, std::move(filename));
if (migrationNumber.has_value()) filenames.emplace(migrationNumber.value(), std::move(filename));
} }
// Now sort the map by the oldest migration. // Now sort the map by the oldest migration.

View File

@@ -3,7 +3,7 @@
// C++ // C++
#include <charconv> #include <charconv>
#include <cstdint> #include <cstdint>
#include <cmath> #include <cmath>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <optional> #include <optional>
@@ -19,7 +19,6 @@
#include "dPlatforms.h" #include "dPlatforms.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
#include "DluAssert.h"
#include <glm/ext/vector_float3.hpp> #include <glm/ext/vector_float3.hpp>
@@ -205,12 +204,6 @@ namespace GeneralUtils {
return isParsed ? static_cast<T>(result) : std::optional<T>{}; return isParsed ? static_cast<T>(result) : std::optional<T>{};
} }
// A version of TryParse that will return `errorVal` if `str` failed to parse.
template <Numeric T>
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
return TryParse<T>(str).value_or(errorVal);
}
template<typename T> template<typename T>
requires(!Numeric<T>) requires(!Numeric<T>)
[[nodiscard]] std::optional<T> TryParse(std::string_view str); [[nodiscard]] std::optional<T> TryParse(std::string_view str);
@@ -243,12 +236,6 @@ namespace GeneralUtils {
return std::nullopt; return std::nullopt;
} }
// A version of TryParse that will return `errorVal` if `str` failed to parse.
template <std::floating_point T>
[[nodiscard]] T TryParse(std::string_view str, const T errorVal) {
return TryParse<T>(str).value_or(errorVal);
}
#endif #endif
/** /**
@@ -270,11 +257,6 @@ namespace GeneralUtils {
return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt; return z ? std::make_optional<T>(x.value(), y.value(), z.value()) : std::nullopt;
} }
// Alternative overload of TryParse with a default value
[[nodiscard]] inline NiPoint3 TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ, const NiPoint3 errorVal) {
return TryParse<NiPoint3>(strX, strY, strZ).value_or(errorVal);
}
/** /**
* The TryParse overload for handling NiPoint3 by passing a span of three strings * The TryParse overload for handling NiPoint3 by passing a span of three strings
* @param str The string vector representing the X, Y, and Z coordinates * @param str The string vector representing the X, Y, and Z coordinates
@@ -285,11 +267,6 @@ namespace GeneralUtils {
return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt; return (str.size() == 3) ? TryParse<T>(str[0], str[1], str[2]) : std::nullopt;
} }
// Alternative overload of TryParse with a default value
[[nodiscard]] inline NiPoint3 TryParse(const std::span<const std::string> str, const NiPoint3 errorVal) {
return TryParse<NiPoint3>(str).value_or(errorVal);
}
template <typename T> template <typename T>
std::u16string to_u16string(const T value) { std::u16string to_u16string(const T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value)); return GeneralUtils::ASCIIToUTF16(std::to_string(value));
@@ -328,7 +305,7 @@ namespace GeneralUtils {
template<typename Container> template<typename Container>
inline Container::value_type GetRandomElement(const Container& container) { inline Container::value_type GetRandomElement(const Container& container) {
DluAssert(!container.empty()); DluAssert(!container.empty());
return container[GenerateRandomNumber<typename Container::size_type>(0, container.size() - 1)]; return container[GenerateRandomNumber<typename Container::value_type>(0, container.size() - 1)];
} }
/** /**

View File

@@ -10,151 +10,163 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
using LDFKey = std::string_view;
using LDFTypeAndValue = std::string_view;
using LDFType = std::string_view;
using LDFValue = std::string_view;
//! Returns a pointer to a LDFData value based on string format //! Returns a pointer to a LDFData value based on string format
std::unique_ptr<LDFBaseData> LDFBaseData::DataFromString(const std::string_view& format) { LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
std::unique_ptr<LDFBaseData> toReturn;
// A valid LDF must be at least 3 characters long (=0:) is the shortest valid LDF (empty UTF-16 key with no initial value) // A valid LDF must be at least 3 characters long (=0:) is the shortest valid LDF (empty UTF-16 key with no initial value)
if (!format.empty() && format.length() > 2) { if (format.empty() || format.length() <= 2) return nullptr;
auto equalsPosition = format.find('='); auto equalsPosition = format.find('=');
// You can have an empty key, just make sure the type and value might exist // You can have an empty key, just make sure the type and value might exist
if (equalsPosition != std::string::npos && equalsPosition != (format.size() - 1)) { if (equalsPosition == std::string::npos || equalsPosition == (format.size() - 1)) return nullptr;
const std::string_view keyValue = format.substr(0, equalsPosition); std::pair<LDFKey, LDFTypeAndValue> keyValue;
const std::string_view typeAndValue = format.substr(equalsPosition + 1, format.size()); keyValue.first = format.substr(0, equalsPosition);
keyValue.second = format.substr(equalsPosition + 1, format.size());
const auto key = GeneralUtils::ASCIIToUTF16(keyValue); std::u16string key = GeneralUtils::ASCIIToUTF16(keyValue.first);
const auto colonPosition = typeAndValue.find(':'); auto colonPosition = keyValue.second.find(':');
// If : is the first thing after an =, then this is an invalid LDF since // If : is the first thing after an =, then this is an invalid LDF since
// we dont have a type to use. // we dont have a type to use.
if (colonPosition != std::string::npos && colonPosition != 0) { if (colonPosition == std::string::npos || colonPosition == 0) return nullptr;
const std::string_view ldfType = typeAndValue.substr(0, colonPosition);
const std::string_view ldfValue = typeAndValue.substr(colonPosition + 1, typeAndValue.size());
// Only allow empty values for string values. std::pair<LDFType, LDFValue> ldfTypeAndValue;
if (!ldfValue.empty() || (ldfType == "0" /* UTF-16 */ || ldfType == "13" /* UTF-8 */)) { ldfTypeAndValue.first = keyValue.second.substr(0, colonPosition);
const eLDFType type = GeneralUtils::TryParse<eLDFType>(ldfType, LDF_TYPE_UNKNOWN); ldfTypeAndValue.second = keyValue.second.substr(colonPosition + 1, keyValue.second.size());
switch (type) {
case LDF_TYPE_UTF_16: {
std::u16string data = GeneralUtils::UTF8ToUTF16(ldfValue);
toReturn.reset(new LDFData<std::u16string>(key, data));
break;
}
case LDF_TYPE_S32: { // Only allow empty values for string values.
const auto data = GeneralUtils::TryParse<int32_t>(ldfValue); if (ldfTypeAndValue.second.size() == 0 && !(ldfTypeAndValue.first == "0" || ldfTypeAndValue.first == "13")) return nullptr;
if (data) {
toReturn.reset(new LDFData<int32_t>(key, data.value()));
} else {
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfValue.data(), format.data());
}
break; eLDFType type;
} char* storage;
try {
case LDF_TYPE_FLOAT: { type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10));
const auto data = GeneralUtils::TryParse<float>(ldfValue); } catch (std::exception) {
if (data) { LOG("Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data());
toReturn.reset(new LDFData<float>(key, data.value())); return nullptr;
} else {
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfValue.data(), format.data());
}
break;
}
case LDF_TYPE_DOUBLE: {
const auto data = GeneralUtils::TryParse<double>(ldfValue);
if (data) {
toReturn.reset(new LDFData<double>(key, data.value()));
} else {
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfValue.data(), format.data());
}
break;
}
case LDF_TYPE_U32:
{
uint32_t data;
bool parsed = true;
// Have to do this really weird parsing to allow for copy ellision
if (ldfValue == "true") {
data = 1;
} else if (ldfValue == "false") {
data = 0;
} else {
const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfValue);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfValue.data(), format.data());
parsed = false;
} else {
data = dataOptional.value();
}
}
if (parsed) toReturn.reset(new LDFData<uint32_t>(key, data));
break;
}
case LDF_TYPE_BOOLEAN: {
bool data;
bool parsed = true;
// Have to do this really weird parsing to allow for copy ellision
if (ldfValue == "true") {
data = true;
} else if (ldfValue == "false") {
data = false;
} else {
const auto dataOptional = GeneralUtils::TryParse<bool>(ldfValue);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfValue.data(), format.data());
parsed = false;
} else {
data = dataOptional.value();
}
}
if (parsed) toReturn.reset(new LDFData<bool>(key, data));
break;
}
case LDF_TYPE_U64: {
const auto data = GeneralUtils::TryParse<uint64_t>(ldfValue);
if (data) {
toReturn.reset(new LDFData<uint64_t>(key, data.value()));
} else {
LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfValue.data(), format.data());
}
break;
}
case LDF_TYPE_OBJID: {
const auto data = GeneralUtils::TryParse<LWOOBJID>(ldfValue);
if (data) {
toReturn.reset(new LDFData<LWOOBJID>(key, data.value()));
} else {
LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfValue.data(), format.data());
}
break;
}
case LDF_TYPE_UTF_8: {
toReturn.reset(new LDFData<std::string>(key, ldfValue.data()));
break;
}
case LDF_TYPE_UNKNOWN:
[[fallthrough]];
default: {
LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfValue.data(), format.data());
break;
}
}
}
}
}
} }
return toReturn; LDFBaseData* returnValue = nullptr;
switch (type) {
case LDF_TYPE_UTF_16: {
std::u16string data = GeneralUtils::UTF8ToUTF16(ldfTypeAndValue.second);
returnValue = new LDFData<std::u16string>(key, data);
break;
}
case LDF_TYPE_S32: {
const auto data = GeneralUtils::TryParse<int32_t>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<int32_t>(key, data.value());
break;
}
case LDF_TYPE_FLOAT: {
const auto data = GeneralUtils::TryParse<float>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<float>(key, data.value());
break;
}
case LDF_TYPE_DOUBLE: {
const auto data = GeneralUtils::TryParse<double>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<double>(key, data.value());
break;
}
case LDF_TYPE_U32:
{
uint32_t data;
if (ldfTypeAndValue.second == "true") {
data = 1;
} else if (ldfTypeAndValue.second == "false") {
data = 0;
} else {
const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfTypeAndValue.second);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
data = dataOptional.value();
}
returnValue = new LDFData<uint32_t>(key, data);
break;
}
case LDF_TYPE_BOOLEAN: {
bool data;
if (ldfTypeAndValue.second == "true") {
data = true;
} else if (ldfTypeAndValue.second == "false") {
data = false;
} else {
const auto dataOptional = GeneralUtils::TryParse<bool>(ldfTypeAndValue.second);
if (!dataOptional) {
LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
data = dataOptional.value();
}
returnValue = new LDFData<bool>(key, data);
break;
}
case LDF_TYPE_U64: {
const auto data = GeneralUtils::TryParse<uint64_t>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<uint64_t>(key, data.value());
break;
}
case LDF_TYPE_OBJID: {
const auto data = GeneralUtils::TryParse<LWOOBJID>(ldfTypeAndValue.second);
if (!data) {
LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<LWOOBJID>(key, data.value());
break;
}
case LDF_TYPE_UTF_8: {
std::string data = ldfTypeAndValue.second.data();
returnValue = new LDFData<std::string>(key, data);
break;
}
case LDF_TYPE_UNKNOWN: {
LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
break;
}
default: {
LOG("Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data());
break;
}
}
return returnValue;
} }

View File

@@ -1,12 +1,11 @@
#ifndef LDFFORMAT_H #ifndef __LDFFORMAT__H__
#define LDFFORMAT_H #define __LDFFORMAT__H__
// Custom Classes // Custom Classes
#include "dCommonVars.h" #include "dCommonVars.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
// C++ // C++
#include <map>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <sstream> #include <sstream>
@@ -47,17 +46,17 @@ public:
virtual std::string GetValueAsString() const = 0; virtual std::string GetValueAsString() const = 0;
virtual std::unique_ptr<LDFBaseData> Copy() const = 0; virtual LDFBaseData* Copy() const = 0;
/** /**
* Given an input string, return the data as a LDF key. * Given an input string, return the data as a LDF key.
*/ */
static std::unique_ptr<LDFBaseData> DataFromString(const std::string_view& format); static LDFBaseData* DataFromString(const std::string_view& format);
}; };
template<typename T> template<typename T>
class LDFData : public LDFBaseData { class LDFData: public LDFBaseData {
private: private:
std::u16string key; std::u16string key;
T value; T value;
@@ -165,8 +164,8 @@ public:
return this->GetValueString(); return this->GetValueString();
} }
std::unique_ptr<LDFBaseData> Copy() const override { LDFBaseData* Copy() const override {
return std::make_unique<LDFData<T>>(key, value); return new LDFData<T>(key, value);
} }
inline static const T Default = {}; inline static const T Default = {};
@@ -227,89 +226,4 @@ template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return
template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; } template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
struct LwoNameValue { #endif //!__LDFFORMAT__H__
using LDFPtr = std::unique_ptr<LDFBaseData>;
using ValueType = std::map<std::u16string, LDFPtr>;
LwoNameValue& operator=(const LwoNameValue& other) {
this->values = other.Copy();
return *this;
}
template<typename T>
void Insert(const std::u16string& key, const T& value) {
this->values.insert_or_assign(key, std::unique_ptr(std::make_unique<LDFData<T>>(key, value)));
}
void Insert(const std::u16string& key, const char* value) {
this->Insert<std::string>(key, value);
}
void Insert(const std::u16string& key, const char16_t* value) {
this->Insert<std::u16string>(key, value);
}
template<typename T>
void Insert(const std::string& key, const T& value) {
this->Insert<T>(GeneralUtils::UTF8ToUTF16(key), value);
}
void Insert(const std::string& key, const char* value) {
this->Insert<std::string>(GeneralUtils::UTF8ToUTF16(key), value);
}
void Insert(const std::string& key, const char16_t* value) {
this->Insert<std::u16string>(GeneralUtils::UTF8ToUTF16(key), value);
}
const LDFPtr& ParseInsert(const std::string& data) {
LDFPtr toInsert(LDFBaseData::DataFromString(data));
return toInsert ?
this->values.insert_or_assign(toInsert->GetKey(), std::move(toInsert)).first->second :
this->values.insert_or_assign(u"FAILED_TO_PARSE_" + GeneralUtils::UTF8ToUTF16(data), std::make_unique<LDFData<std::string>>("", "")).first->second;
}
const LDFPtr& ParseInsert(const std::u16string& data) {
return this->ParseInsert(GeneralUtils::UTF16ToWTF8(data));
}
ValueType::const_iterator begin() const {
return this->values.cbegin();
}
ValueType::const_iterator end() const {
return this->values.cend();
}
void Erase(const std::u16string& key) {
this->values.erase(key);
}
void Erase(const std::string& key) {
this->Erase(GeneralUtils::ASCIIToUTF16(key));
}
ValueType::iterator find(const ValueType::key_type& key) {
return this->values.find(key);
}
ValueType::const_iterator find(const ValueType::key_type& key) const {
return this->values.find(key);
}
LwoNameValue() = default;
LwoNameValue(const LwoNameValue& other) {
this->values = other.Copy();
}
ValueType values;
private:
ValueType Copy() const {
ValueType copy;
for (const auto& [key, value] : this->values) copy.insert_or_assign(key, value->Copy());
return copy;
}
};
#endif //!LDFFORMAT_H

View File

@@ -96,17 +96,3 @@ bool Logger::GetLogToConsole() const {
} }
return toReturn; return toReturn;
} }
FuncEntry::FuncEntry(const char* funcName, const char* fileName, const uint32_t line) {
m_FuncName = funcName;
if (!m_FuncName) m_FuncName = "Unknown";
m_Line = line;
m_FileName = fileName;
LOG("--> %s::%s:%i", m_FileName, m_FuncName, m_Line);
}
FuncEntry::~FuncEntry() {
if (!m_FuncName || !m_FileName) return;
LOG("<-- %s::%s:%i", m_FileName, m_FuncName, m_Line);
}

View File

@@ -32,19 +32,6 @@ constexpr const char* GetFileNameFromAbsolutePath(const char* path) {
#define LOG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->Log(str_, message, ##__VA_ARGS__); } while(0) #define LOG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->Log(str_, message, ##__VA_ARGS__); } while(0)
#define LOG_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0) #define LOG_DEBUG(message, ...) do { auto str_ = FILENAME_AND_LINE; Game::logger->LogDebug(str_, message, ##__VA_ARGS__); } while(0)
// Place this right at the start of a function. Will log a message when called and then once you leave the function.
#define LOG_ENTRY auto str_ = GetFileNameFromAbsolutePath(__FILE__); FuncEntry funcEntry_(__FUNCTION__, str_, __LINE__)
class FuncEntry {
public:
FuncEntry(const char* funcName, const char* fileName, const uint32_t line);
~FuncEntry();
private:
const char* m_FuncName = nullptr;
const char* m_FileName = nullptr;
uint32_t m_Line = 0;
};
// Writer class for writing data to files. // Writer class for writing data to files.
class Writer { class Writer {
public: public:

View File

@@ -5,43 +5,13 @@
#include "TinyXmlUtils.h" #include "TinyXmlUtils.h"
#include <ranges> #include <ranges>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <sstream>
namespace {
// The base LXFML xml file to use when creating new models.
std::string g_base = R"(<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<LXFML versionMajor="5" versionMinor="0">
<Meta>
<Application name="LEGO Universe" versionMajor="0" versionMinor="0"/>
<Brand name="LEGOUniverse"/>
<BrickSet version="457"/>
</Meta>
<Bricks>
</Bricks>
<RigidSystems>
</RigidSystems>
<GroupSystems>
<GroupSystem>
</GroupSystem>
</GroupSystems>
</LXFML>)";
}
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) { Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
Result toReturn; Result toReturn;
// Handle empty or invalid input
if (data.empty()) {
return toReturn;
}
tinyxml2::XMLDocument doc; tinyxml2::XMLDocument doc;
// Use length-based parsing to avoid expensive string copy const auto err = doc.Parse(data.data());
const auto err = doc.Parse(data.data(), data.size());
if (err != tinyxml2::XML_SUCCESS) { if (err != tinyxml2::XML_SUCCESS) {
LOG("Failed to parse xml %s.", StringifiedEnum::ToString(err).data());
return toReturn; return toReturn;
} }
@@ -50,6 +20,7 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
auto lxfml = reader["LXFML"]; auto lxfml = reader["LXFML"];
if (!lxfml) { if (!lxfml) {
LOG("Failed to find LXFML element.");
return toReturn; return toReturn;
} }
@@ -78,19 +49,16 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
// Calculate the lowest and highest points on the entire model // Calculate the lowest and highest points on the entire model
for (const auto& transformation : transformations | std::views::values) { for (const auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ','); auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) continue; if (split.size() < 12) {
LOG("Not enough in the split?");
auto xOpt = GeneralUtils::TryParse<float>(split[9]); continue;
auto yOpt = GeneralUtils::TryParse<float>(split[10]); }
auto zOpt = GeneralUtils::TryParse<float>(split[11]);
auto x = GeneralUtils::TryParse<float>(split[9]).value();
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) continue; auto y = GeneralUtils::TryParse<float>(split[10]).value();
auto z = GeneralUtils::TryParse<float>(split[11]).value();
auto x = xOpt.value(); if (x < lowest.x) lowest.x = x;
auto y = yOpt.value(); if (y < lowest.y) lowest.y = y;
auto z = zOpt.value();
if (x < lowest.x) lowest.x = x;
if (y < lowest.y) lowest.y = y;
if (z < lowest.z) lowest.z = z; if (z < lowest.z) lowest.z = z;
if (highest.x < x) highest.x = x; if (highest.x < x) highest.x = x;
@@ -119,19 +87,13 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
for (auto& transformation : transformations | std::views::values) { for (auto& transformation : transformations | std::views::values) {
auto split = GeneralUtils::SplitString(transformation, ','); auto split = GeneralUtils::SplitString(transformation, ',');
if (split.size() < 12) { if (split.size() < 12) {
LOG("Not enough in the split?");
continue; continue;
} }
auto xOpt = GeneralUtils::TryParse<float>(split[9]); auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
auto yOpt = GeneralUtils::TryParse<float>(split[10]); auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
auto zOpt = GeneralUtils::TryParse<float>(split[11]); auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
if (!xOpt.has_value() || !yOpt.has_value() || !zOpt.has_value()) {
continue;
}
auto x = xOpt.value() - newRootPos.x + curPosition.x;
auto y = yOpt.value() - newRootPos.y + curPosition.y;
auto z = zOpt.value() - newRootPos.z + curPosition.z;
std::stringstream stream; std::stringstream stream;
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
stream << split[i]; stream << split[i];
@@ -166,345 +128,3 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoin
toReturn.center = newRootPos; toReturn.center = newRootPos;
return toReturn; return toReturn;
} }
// Deep-clone an XMLElement (attributes, text, and child elements) into a target document
// with maximum depth protection to prevent infinite loops
static tinyxml2::XMLElement* CloneElementDeep(const tinyxml2::XMLElement* src, tinyxml2::XMLDocument& dstDoc, int maxDepth = 100) {
if (!src || maxDepth <= 0) return nullptr;
auto* dst = dstDoc.NewElement(src->Name());
// copy attributes
for (const tinyxml2::XMLAttribute* attr = src->FirstAttribute(); attr; attr = attr->Next()) {
dst->SetAttribute(attr->Name(), attr->Value());
}
// copy children (elements and text)
for (const tinyxml2::XMLNode* child = src->FirstChild(); child; child = child->NextSibling()) {
if (const tinyxml2::XMLElement* childElem = child->ToElement()) {
// Recursively clone child elements with decremented depth
auto* clonedChild = CloneElementDeep(childElem, dstDoc, maxDepth - 1);
if (clonedChild) dst->InsertEndChild(clonedChild);
} else if (const tinyxml2::XMLText* txt = child->ToText()) {
auto* n = dstDoc.NewText(txt->Value());
dst->InsertEndChild(n);
} else if (const tinyxml2::XMLComment* c = child->ToComment()) {
auto* n = dstDoc.NewComment(c->Value());
dst->InsertEndChild(n);
}
}
return dst;
}
std::vector<Lxfml::Result> Lxfml::Split(const std::string_view data, const NiPoint3& curPosition) {
std::vector<Result> results;
// Handle empty or invalid input
if (data.empty()) {
return results;
}
// Prevent processing extremely large inputs that could cause hangs
if (data.size() > 10000000) { // 10MB limit
return results;
}
tinyxml2::XMLDocument doc;
// Use length-based parsing to avoid expensive string copy
const auto err = doc.Parse(data.data(), data.size());
if (err != tinyxml2::XML_SUCCESS) {
return results;
}
auto* lxfml = doc.FirstChildElement("LXFML");
if (!lxfml) {
return results;
}
// Build maps: partRef -> Part element, partRef -> Brick element, boneRef -> partRef, brickRef -> Brick element
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToPart;
std::unordered_map<std::string, tinyxml2::XMLElement*> partRefToBrick;
std::unordered_map<std::string, std::string> boneRefToPartRef;
std::unordered_map<std::string, tinyxml2::XMLElement*> brickByRef;
auto* bricksParent = lxfml->FirstChildElement("Bricks");
if (bricksParent) {
for (auto* brick = bricksParent->FirstChildElement("Brick"); brick; brick = brick->NextSiblingElement("Brick")) {
const char* brickRef = brick->Attribute("refID");
if (brickRef) brickByRef.emplace(std::string(brickRef), brick);
for (auto* part = brick->FirstChildElement("Part"); part; part = part->NextSiblingElement("Part")) {
const char* partRef = part->Attribute("refID");
if (partRef) {
partRefToPart.emplace(std::string(partRef), part);
partRefToBrick.emplace(std::string(partRef), brick);
}
auto* bone = part->FirstChildElement("Bone");
if (bone) {
const char* boneRef = bone->Attribute("refID");
if (boneRef) boneRefToPartRef.emplace(std::string(boneRef), partRef ? std::string(partRef) : std::string());
}
}
}
}
// Collect RigidSystem elements
std::vector<tinyxml2::XMLElement*> rigidSystems;
auto* rigidSystemsParent = lxfml->FirstChildElement("RigidSystems");
if (rigidSystemsParent) {
for (auto* rs = rigidSystemsParent->FirstChildElement("RigidSystem"); rs; rs = rs->NextSiblingElement("RigidSystem")) {
rigidSystems.push_back(rs);
}
}
// Collect top-level groups (immediate children of GroupSystem)
std::vector<tinyxml2::XMLElement*> groupRoots;
auto* groupSystemsParent = lxfml->FirstChildElement("GroupSystems");
if (groupSystemsParent) {
for (auto* gs = groupSystemsParent->FirstChildElement("GroupSystem"); gs; gs = gs->NextSiblingElement("GroupSystem")) {
for (auto* group = gs->FirstChildElement("Group"); group; group = group->NextSiblingElement("Group")) {
groupRoots.push_back(group);
}
}
}
// Track used bricks and rigidsystems
std::unordered_set<std::string> usedBrickRefs;
std::unordered_set<tinyxml2::XMLElement*> usedRigidSystems;
// Track used groups to avoid processing them twice
std::unordered_set<tinyxml2::XMLElement*> usedGroups;
// Helper to create output document from sets of brick refs and rigidsystem pointers
auto makeOutput = [&](const std::unordered_set<std::string>& bricksToInclude, const std::vector<tinyxml2::XMLElement*>& rigidSystemsToInclude, const std::vector<tinyxml2::XMLElement*>& groupsToInclude = {}) {
tinyxml2::XMLDocument outDoc;
outDoc.Parse(g_base.c_str());
auto* outRoot = outDoc.FirstChildElement("LXFML");
auto* outBricks = outRoot->FirstChildElement("Bricks");
auto* outRigidSystems = outRoot->FirstChildElement("RigidSystems");
auto* outGroupSystems = outRoot->FirstChildElement("GroupSystems");
// clone and insert bricks
for (const auto& bref : bricksToInclude) {
auto it = brickByRef.find(bref);
if (it == brickByRef.end()) continue;
tinyxml2::XMLElement* cloned = CloneElementDeep(it->second, outDoc);
if (cloned) outBricks->InsertEndChild(cloned);
}
// clone and insert rigidsystems
for (auto* rsPtr : rigidSystemsToInclude) {
tinyxml2::XMLElement* cloned = CloneElementDeep(rsPtr, outDoc);
if (cloned) outRigidSystems->InsertEndChild(cloned);
}
// clone and insert group(s) if requested
if (outGroupSystems && !groupsToInclude.empty()) {
// clear default children
while (outGroupSystems->FirstChild()) outGroupSystems->DeleteChild(outGroupSystems->FirstChild());
// create a GroupSystem element and append requested groups
auto* newGS = outDoc.NewElement("GroupSystem");
for (auto* gptr : groupsToInclude) {
tinyxml2::XMLElement* clonedG = CloneElementDeep(gptr, outDoc);
if (clonedG) newGS->InsertEndChild(clonedG);
}
outGroupSystems->InsertEndChild(newGS);
}
// Print to string
tinyxml2::XMLPrinter printer;
outDoc.Print(&printer);
// Normalize position and compute center using existing helper
std::string xmlString = printer.CStr();
if (xmlString.size() > 5000000) { // 5MB limit for normalization
Result emptyResult;
emptyResult.lxfml = xmlString;
return emptyResult;
}
auto normalized = NormalizePosition(xmlString, curPosition);
return normalized;
};
// 1) Process groups (each top-level Group becomes one output; nested groups are included)
for (auto* groupRoot : groupRoots) {
// Skip if this group was already processed as part of another group
if (usedGroups.find(groupRoot) != usedGroups.end()) continue;
// Helper to collect all partRefs in a group's subtree
std::function<void(const tinyxml2::XMLElement*, std::unordered_set<std::string>&)> collectParts = [&](const tinyxml2::XMLElement* g, std::unordered_set<std::string>& partRefs) {
if (!g) return;
const char* partAttr = g->Attribute("partRefs");
if (partAttr) {
for (auto& tok : GeneralUtils::SplitString(partAttr, ',')) partRefs.insert(tok);
}
for (auto* child = g->FirstChildElement("Group"); child; child = child->NextSiblingElement("Group")) collectParts(child, partRefs);
};
// Collect all groups that need to be merged into this output
std::vector<tinyxml2::XMLElement*> groupsToInclude{ groupRoot };
usedGroups.insert(groupRoot);
// Build initial sets of bricks and boneRefs from the starting group
std::unordered_set<std::string> partRefs;
collectParts(groupRoot, partRefs);
std::unordered_set<std::string> bricksIncluded;
std::unordered_set<std::string> boneRefsIncluded;
for (const auto& pref : partRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
auto partIt = partRefToPart.find(pref);
if (partIt != partRefToPart.end()) {
auto* bone = partIt->second->FirstChildElement("Bone");
if (bone) {
const char* bref = bone->Attribute("refID");
if (bref) boneRefsIncluded.insert(std::string(bref));
}
}
}
// Iteratively include any RigidSystems that reference any boneRefsIncluded
// and check if those rigid systems' bricks span other groups
bool changed = true;
std::vector<tinyxml2::XMLElement*> rigidSystemsToInclude;
int maxIterations = 1000; // Safety limit to prevent infinite loops
int iteration = 0;
while (changed && iteration < maxIterations) {
changed = false;
iteration++;
// First, expand rigid systems based on current boneRefsIncluded
for (auto* rs : rigidSystems) {
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
// parse boneRefs of this rigid system (from its <Rigid> children)
bool intersects = false;
std::vector<std::string> rsBoneRefs;
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
const char* battr = rigid->Attribute("boneRefs");
if (!battr) continue;
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
rsBoneRefs.push_back(tok);
if (boneRefsIncluded.find(tok) != boneRefsIncluded.end()) intersects = true;
}
}
if (!intersects) continue;
// include this rigid system and all boneRefs it references
usedRigidSystems.insert(rs);
rigidSystemsToInclude.push_back(rs);
for (const auto& br : rsBoneRefs) {
boneRefsIncluded.insert(br);
auto bpIt = boneRefToPartRef.find(br);
if (bpIt != boneRefToPartRef.end()) {
auto partRef = bpIt->second;
auto pbIt = partRefToBrick.find(partRef);
if (pbIt != partRefToBrick.end()) {
const char* bref = pbIt->second->Attribute("refID");
if (bref && bricksIncluded.insert(std::string(bref)).second) changed = true;
}
}
}
}
// Second, check if the newly included bricks span any other groups
// If so, merge those groups into the current output
for (auto* otherGroup : groupRoots) {
if (usedGroups.find(otherGroup) != usedGroups.end()) continue;
// Collect partRefs from this other group
std::unordered_set<std::string> otherPartRefs;
collectParts(otherGroup, otherPartRefs);
// Check if any of these partRefs correspond to bricks we've already included
bool spansOtherGroup = false;
for (const auto& pref : otherPartRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref && bricksIncluded.find(std::string(bref)) != bricksIncluded.end()) {
spansOtherGroup = true;
break;
}
}
}
if (spansOtherGroup) {
// Merge this group into the current output
usedGroups.insert(otherGroup);
groupsToInclude.push_back(otherGroup);
changed = true;
// Add all partRefs, boneRefs, and bricks from this group
for (const auto& pref : otherPartRefs) {
auto pit = partRefToBrick.find(pref);
if (pit != partRefToBrick.end()) {
const char* bref = pit->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
auto partIt = partRefToPart.find(pref);
if (partIt != partRefToPart.end()) {
auto* bone = partIt->second->FirstChildElement("Bone");
if (bone) {
const char* bref = bone->Attribute("refID");
if (bref) boneRefsIncluded.insert(std::string(bref));
}
}
}
}
}
}
if (iteration >= maxIterations) {
// Iteration limit reached, stop processing to prevent infinite loops
// The file is likely malformed, so just skip further processing
return results;
}
// include bricks from bricksIncluded into used set
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
// make output doc and push result (include all merged groups' XML)
auto normalized = makeOutput(bricksIncluded, rigidSystemsToInclude, groupsToInclude);
results.push_back(normalized);
}
// 2) Process remaining RigidSystems (each becomes its own file)
for (auto* rs : rigidSystems) {
if (usedRigidSystems.find(rs) != usedRigidSystems.end()) continue;
std::unordered_set<std::string> bricksIncluded;
// collect boneRefs referenced by this rigid system
for (auto* rigid = rs->FirstChildElement("Rigid"); rigid; rigid = rigid->NextSiblingElement("Rigid")) {
const char* battr = rigid->Attribute("boneRefs");
if (!battr) continue;
for (auto& tok : GeneralUtils::SplitString(battr, ',')) {
auto bpIt = boneRefToPartRef.find(tok);
if (bpIt != boneRefToPartRef.end()) {
auto partRef = bpIt->second;
auto pbIt = partRefToBrick.find(partRef);
if (pbIt != partRefToBrick.end()) {
const char* bref = pbIt->second->Attribute("refID");
if (bref) bricksIncluded.insert(std::string(bref));
}
}
}
}
// mark used
for (const auto& b : bricksIncluded) usedBrickRefs.insert(b);
usedRigidSystems.insert(rs);
std::vector<tinyxml2::XMLElement*> rsVec{ rs };
auto normalized = makeOutput(bricksIncluded, rsVec);
results.push_back(normalized);
}
// 3) Any remaining bricks not included become their own files
for (const auto& [bref, brickPtr] : brickByRef) {
if (usedBrickRefs.find(bref) != usedBrickRefs.end()) continue;
std::unordered_set<std::string> bricksIncluded{ bref };
auto normalized = makeOutput(bricksIncluded, {});
results.push_back(normalized);
usedBrickRefs.insert(bref);
}
return results;
}

View File

@@ -6,7 +6,6 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector>
#include "NiPoint3.h" #include "NiPoint3.h"
@@ -19,7 +18,6 @@ namespace Lxfml {
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0. // Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
// Returns a struct of its new center and the updated LXFML containing these edits. // Returns a struct of its new center and the updated LXFML containing these edits.
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO); [[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
[[nodiscard]] std::vector<Result> Split(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
// these are only for the migrations due to a bug in one of the implementations. // these are only for the migrations due to a bug in one of the implementations.
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data); [[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);

View File

@@ -53,21 +53,16 @@ Lxfml::Result Lxfml::NormalizePositionOnlyFirstPart(const std::string_view data)
continue; continue;
} }
try { auto x = GeneralUtils::TryParse<float>(split[9]).value();
auto x = GeneralUtils::TryParse<float>(split[9]).value(); auto y = GeneralUtils::TryParse<float>(split[10]).value();
auto y = GeneralUtils::TryParse<float>(split[10]).value(); auto z = GeneralUtils::TryParse<float>(split[11]).value();
auto z = GeneralUtils::TryParse<float>(split[11]).value(); if (x < lowest.x) lowest.x = x;
if (x < lowest.x) lowest.x = x; if (y < lowest.y) lowest.y = y;
if (y < lowest.y) lowest.y = y; if (z < lowest.z) lowest.z = z;
if (z < lowest.z) lowest.z = z;
if (highest.x < x) highest.x = x; if (highest.x < x) highest.x = x;
if (highest.y < y) highest.y = y; if (highest.y < y) highest.y = y;
if (highest.z < z) highest.z = z; if (highest.z < z) highest.z = z;
} catch (std::exception& e) {
LOG("Failed to parse a split value of either (%s), (%s), or (%s).", split[9].c_str(), split[10].c_str(), split[11].c_str());
return toReturn; // Early return since we failed to parse this lxfml.
}
} }
auto delta = (highest - lowest) / 2.0f; auto delta = (highest - lowest) / 2.0f;

View File

@@ -1,77 +1,105 @@
#include "Metrics.h" #include "Metrics.hpp"
#include "StringifiedEnum.h"
#include <chrono> #include <chrono>
namespace { std::unordered_map<MetricVariable, Metric*> Metrics::m_Metrics = {};
std::unordered_map<MetricVariable, Metric> g_Metrics = {}; std::vector<MetricVariable> Metrics::m_Variables = {
std::vector<MetricVariable> g_Variables = { MetricVariable::GameLoop,
MetricVariable::GameLoop, MetricVariable::PacketHandling,
MetricVariable::PacketHandling, MetricVariable::UpdateEntities,
MetricVariable::UpdateEntities, MetricVariable::UpdateSpawners,
MetricVariable::UpdateSpawners, MetricVariable::Physics,
MetricVariable::Physics, MetricVariable::UpdateReplica,
MetricVariable::UpdateReplica, MetricVariable::Ghosting,
MetricVariable::Ghosting, MetricVariable::CPUTime,
MetricVariable::CPUTime, MetricVariable::Sleep,
MetricVariable::Sleep, MetricVariable::Frame,
MetricVariable::Frame, };
};
}
void Metrics::AddMeasurement(MetricVariable variable, int64_t value) { void Metrics::AddMeasurement(MetricVariable variable, int64_t value) {
auto& metric = g_Metrics[variable]; const auto& iter = m_Metrics.find(variable);
Metric* metric;
if (iter == m_Metrics.end()) {
metric = new Metric();
m_Metrics[variable] = metric;
} else {
metric = iter->second;
}
AddMeasurement(metric, value); AddMeasurement(metric, value);
} }
void Metrics::AddMeasurement(Metric& metric, int64_t value) { void Metrics::AddMeasurement(Metric* metric, int64_t value) {
const auto index = metric.measurementIndex; const auto index = metric->measurementIndex;
metric.measurements[index] = value; metric->measurements[index] = value;
if (metric.max == -1 || value > metric.max) { if (metric->max == -1 || value > metric->max) {
metric.max = value; metric->max = value;
} else if (metric.min == -1 || metric.min > value) { } else if (metric->min == -1 || metric->min > value) {
metric.min = value; metric->min = value;
} }
if (metric.measurementSize < MAX_MEASURMENT_POINTS) { if (metric->measurementSize < MAX_MEASURMENT_POINTS) {
metric.measurementSize++; metric->measurementSize++;
} }
metric.measurementIndex = (index + 1) % MAX_MEASURMENT_POINTS; metric->measurementIndex = (index + 1) % MAX_MEASURMENT_POINTS;
} }
const Metric& Metrics::GetMetric(MetricVariable variable) { const Metric* Metrics::GetMetric(MetricVariable variable) {
auto& metric = g_Metrics[variable]; const auto& iter = m_Metrics.find(variable);
if (iter == m_Metrics.end()) {
return nullptr;
}
Metric* metric = iter->second;
int64_t average = 0; int64_t average = 0;
for (size_t i = 0; i < metric.measurementSize; i++) { for (size_t i = 0; i < metric->measurementSize; i++) {
average += metric.measurements[i]; average += metric->measurements[i];
} }
average /= metric.measurementSize; average /= metric->measurementSize;
metric.average = average; metric->average = average;
return metric; return metric;
} }
void Metrics::StartMeasurement(MetricVariable variable) { void Metrics::StartMeasurement(MetricVariable variable) {
auto& metric = g_Metrics[variable]; const auto& iter = m_Metrics.find(variable);
metric.activeMeasurement = std::chrono::high_resolution_clock::now(); Metric* metric;
if (iter == m_Metrics.end()) {
metric = new Metric();
m_Metrics[variable] = metric;
} else {
metric = iter->second;
}
metric->activeMeasurement = std::chrono::high_resolution_clock::now();
} }
void Metrics::EndMeasurement(MetricVariable variable) { void Metrics::EndMeasurement(MetricVariable variable) {
const auto end = std::chrono::high_resolution_clock::now(); const auto end = std::chrono::high_resolution_clock::now();
auto& metric = g_Metrics[variable]; const auto& iter = m_Metrics.find(variable);
const auto elapsed = end - metric.activeMeasurement; if (iter == m_Metrics.end()) {
return;
}
Metric* metric = iter->second;
const auto elapsed = end - metric->activeMeasurement;
const auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count(); const auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
@@ -82,12 +110,44 @@ float Metrics::ToMiliseconds(int64_t nanoseconds) {
return static_cast<float>(nanoseconds) / 1e6; return static_cast<float>(nanoseconds) / 1e6;
} }
const std::string_view Metrics::MetricVariableToString(MetricVariable variable) { std::string Metrics::MetricVariableToString(MetricVariable variable) {
return StringifiedEnum::ToString(variable); switch (variable) {
case MetricVariable::GameLoop:
return "GameLoop";
case MetricVariable::PacketHandling:
return "PacketHandling";
case MetricVariable::UpdateEntities:
return "UpdateEntities";
case MetricVariable::UpdateSpawners:
return "UpdateSpawners";
case MetricVariable::Physics:
return "Physics";
case MetricVariable::UpdateReplica:
return "UpdateReplica";
case MetricVariable::Sleep:
return "Sleep";
case MetricVariable::CPUTime:
return "CPUTime";
case MetricVariable::Frame:
return "Frame";
case MetricVariable::Ghosting:
return "Ghosting";
default:
return "Invalid";
}
} }
const std::vector<MetricVariable>& Metrics::GetAllMetrics() { const std::vector<MetricVariable>& Metrics::GetAllMetrics() {
return g_Variables; return m_Variables;
}
void Metrics::Clear() {
for (const auto& pair : m_Metrics) {
delete pair.second;
}
m_Metrics.clear();
} }
/* RSS Memory utilities /* RSS Memory utilities

View File

@@ -1,48 +0,0 @@
#pragma once
#include "dCommonVars.h"
#include <vector>
#include <map>
#include <string_view>
#include <unordered_map>
#include <chrono>
#define MAX_MEASURMENT_POINTS 1024
enum class MetricVariable : int32_t {
GameLoop,
PacketHandling,
UpdateEntities,
UpdateSpawners,
Physics,
UpdateReplica,
Ghosting,
CPUTime,
Sleep,
Frame,
};
struct Metric {
int64_t measurements[MAX_MEASURMENT_POINTS] = {};
size_t measurementIndex = 0;
size_t measurementSize = 0;
int64_t max = -1;
int64_t min = -1;
int64_t average = 0;
std::chrono::time_point<std::chrono::high_resolution_clock> activeMeasurement;
};
namespace Metrics {
void AddMeasurement(MetricVariable variable, int64_t value);
void AddMeasurement(Metric& metric, int64_t value);
const Metric& GetMetric(MetricVariable variable);
void StartMeasurement(MetricVariable variable);
void EndMeasurement(MetricVariable variable);
float ToMiliseconds(int64_t nanoseconds);
const std::string_view MetricVariableToString(MetricVariable variable);
const std::vector<MetricVariable>& GetAllMetrics();
size_t GetPeakRSS();
size_t GetCurrentRSS();
size_t GetProcessID();
};

61
dCommon/Metrics.hpp Normal file
View File

@@ -0,0 +1,61 @@
#pragma once
#include "dCommonVars.h"
#include <vector>
#include <map>
#include <unordered_map>
#include <chrono>
#define MAX_MEASURMENT_POINTS 1024
enum class MetricVariable : int32_t
{
GameLoop,
PacketHandling,
UpdateEntities,
UpdateSpawners,
Physics,
UpdateReplica,
Ghosting,
CPUTime,
Sleep,
Frame,
};
struct Metric
{
int64_t measurements[MAX_MEASURMENT_POINTS] = {};
size_t measurementIndex = 0;
size_t measurementSize = 0;
int64_t max = -1;
int64_t min = -1;
int64_t average = 0;
std::chrono::time_point<std::chrono::high_resolution_clock> activeMeasurement;
};
class Metrics
{
public:
~Metrics();
static void AddMeasurement(MetricVariable variable, int64_t value);
static void AddMeasurement(Metric* metric, int64_t value);
static const Metric* GetMetric(MetricVariable variable);
static void StartMeasurement(MetricVariable variable);
static void EndMeasurement(MetricVariable variable);
static float ToMiliseconds(int64_t nanoseconds);
static std::string MetricVariableToString(MetricVariable variable);
static const std::vector<MetricVariable>& GetAllMetrics();
static size_t GetPeakRSS();
static size_t GetCurrentRSS();
static size_t GetProcessID();
static void Clear();
private:
Metrics();
static std::unordered_map<MetricVariable, Metric*> m_Metrics;
static std::vector<MetricVariable> m_Variables;
};

View File

@@ -1,34 +0,0 @@
#ifndef NICOLOR_H
#define NICOLOR_H
struct NiColor {
float m_Red;
float m_Green;
float m_Blue;
constexpr NiColor(float red, float green, float blue) : m_Red(red), m_Green(green), m_Blue(blue) {}
constexpr NiColor() : NiColor(0.0f, 0.0f, 0.0f) {}
/* reduce RGB files to grayscale, with or without alpha
* using the equation given in Poynton's ColorFAQ at
* <http://www.inforamp.net/~poynton/> // dead link
* Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net
*
* Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
*
* We approximate this with
*
* Y = 0.21268 * R + 0.7151 * G + 0.07217 * B
*
* which can be expressed with integers as
*
* Y = (6969 * R + 23434 * G + 2365 * B)/32768
*
* The calculation is to be done in a linear colorspace.
*
* Other integer coefficents can be used via png_set_rgb_to_gray().
*/
float ToXYZ() const { return (m_Red * 0.212671f) + (m_Green * 0.71516f) + (m_Blue * 0.072169f); };
};
#endif // NICOLOR_H

View File

@@ -1,8 +1,6 @@
#ifndef __NIPOINT3_H__ #ifndef __NIPOINT3_H__
#define __NIPOINT3_H__ #define __NIPOINT3_H__
#ifndef GLM_ENABLE_EXPERIMENTAL
# define GLM_ENABLE_EXPERIMENTAL
#endif
/*! /*!
\file NiPoint3.hpp \file NiPoint3.hpp
\brief Defines a point in space in XYZ coordinates \brief Defines a point in space in XYZ coordinates

View File

@@ -1,8 +1,6 @@
#ifndef NIQUATERNION_H #ifndef NIQUATERNION_H
#define NIQUATERNION_H #define NIQUATERNION_H
#ifndef GLM_ENABLE_EXPERIMENTAL
# define GLM_ENABLE_EXPERIMENTAL
#endif
// Custom Classes // Custom Classes
#include "NiPoint3.h" #include "NiPoint3.h"

View File

@@ -45,12 +45,6 @@ Sd0::Sd0(std::istream& buffer) {
uint32_t bufferSize = buffer.tellg(); uint32_t bufferSize = buffer.tellg();
buffer.seekg(0, std::ios::beg); buffer.seekg(0, std::ios::beg);
WriteSize(firstChunk, bufferSize); WriteSize(firstChunk, bufferSize);
// its expected that if we got here, we got an old sd0 buffer where we ignored the sd0 part
// that means this can be at most the compressed chunk limit.
if (bufferSize > MAX_UNCOMPRESSED_CHUNK_SIZE) {
LOG("Possible bad chunk size of %i specified, rejecting.", bufferSize);
return;
}
firstChunk.resize(firstChunk.size() + bufferSize); firstChunk.resize(firstChunk.size() + bufferSize);
auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true)); auto* dataStart = reinterpret_cast<char*>(firstChunk.data() + GetDataOffset(true));
if (!buffer.read(dataStart, bufferSize)) { if (!buffer.read(dataStart, bufferSize)) {
@@ -77,12 +71,6 @@ Sd0::Sd0(std::istream& buffer) {
WriteSize(chunk, chunkSize); WriteSize(chunk, chunkSize);
// Assuming a good buffer that is large enough to take up 2 zlib buffers
// any buffer should be compressed enough to take up less size than its uncompressed counterpart
if (chunkSize > MAX_UNCOMPRESSED_CHUNK_SIZE) {
LOG("Possible bad chunk size of %i specified, rejecting.", chunkSize);
break;
}
chunk.resize(chunkSize + dataOffset); chunk.resize(chunkSize + dataOffset);
auto* dataStart = reinterpret_cast<char*>(chunk.data() + dataOffset); auto* dataStart = reinterpret_cast<char*>(chunk.data() + dataOffset);
if (!buffer.read(dataStart, chunkSize)) { if (!buffer.read(dataStart, chunkSize)) {
@@ -107,11 +95,6 @@ void Sd0::FromData(const uint8_t* data, size_t bufferSize) {
startOffset, numToCopy, startOffset, numToCopy,
compressedChunk.data(), compressedChunk.size()); compressedChunk.data(), compressedChunk.size());
if (compressedSize == -1) {
LOG("Failed to compress chunk, aborting");
break;
}
auto& chunk = m_Chunks.emplace_back(); auto& chunk = m_Chunks.emplace_back();
bool firstBuffer = m_Chunks.size() == 1; bool firstBuffer = m_Chunks.size() == 1;
auto dataOffset = GetDataOffset(firstBuffer); auto dataOffset = GetDataOffset(firstBuffer);
@@ -136,12 +119,6 @@ std::string Sd0::GetAsStringUncompressed() const {
auto dataOffset = GetDataOffset(first); auto dataOffset = GetDataOffset(first);
first = false; first = false;
const auto chunkSize = chunk.size(); const auto chunkSize = chunk.size();
if (chunkSize <= static_cast<size_t>(dataOffset)) {
LOG("Bad chunkSize for data, aborting");
toReturn = "";
totalSize = 0;
break;
}
auto oldSize = toReturn.size(); auto oldSize = toReturn.size();
toReturn.resize(oldSize + MAX_UNCOMPRESSED_CHUNK_SIZE); toReturn.resize(oldSize + MAX_UNCOMPRESSED_CHUNK_SIZE);
@@ -151,13 +128,6 @@ std::string Sd0::GetAsStringUncompressed() const {
reinterpret_cast<uint8_t*>(toReturn.data()) + oldSize, MAX_UNCOMPRESSED_CHUNK_SIZE, reinterpret_cast<uint8_t*>(toReturn.data()) + oldSize, MAX_UNCOMPRESSED_CHUNK_SIZE,
error); error);
if (uncompressedSize == -1) {
LOG("Failed to decompress chunk, aborting");
toReturn = "";
totalSize = 0;
break;
}
totalSize += uncompressedSize; totalSize += uncompressedSize;
} }

View File

@@ -3,12 +3,12 @@
#include "zlib.h" #include "zlib.h"
namespace ZCompression { namespace ZCompression {
uint32_t GetMaxCompressedLength(uint32_t nLenSrc) { int32_t GetMaxCompressedLength(int32_t nLenSrc) {
uint32_t n16kBlocks = (nLenSrc + 16383) / 16384; // round up any fraction of a block int32_t n16kBlocks = (nLenSrc + 16383) / 16384; // round up any fraction of a block
return (nLenSrc + 6 + (n16kBlocks * 5)); return (nLenSrc + 6 + (n16kBlocks * 5));
} }
int32_t Compress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst) { int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst) {
z_stream zInfo = { 0 }; z_stream zInfo = { 0 };
zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_in = zInfo.avail_in = nLenSrc;
zInfo.total_out = zInfo.avail_out = nLenDst; zInfo.total_out = zInfo.avail_out = nLenDst;
@@ -27,7 +27,7 @@ namespace ZCompression {
return(nRet); return(nRet);
} }
int32_t Decompress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst, int32_t& nErr) { int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr) {
// Get the size of the decompressed data // Get the size of the decompressed data
z_stream zInfo = { 0 }; z_stream zInfo = { 0 };
zInfo.total_in = zInfo.avail_in = nLenSrc; zInfo.total_in = zInfo.avail_in = nLenSrc;

View File

@@ -3,10 +3,10 @@
#include <cstdint> #include <cstdint>
namespace ZCompression { namespace ZCompression {
uint32_t GetMaxCompressedLength(uint32_t nLenSrc); int32_t GetMaxCompressedLength(int32_t nLenSrc);
int32_t Compress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst); int32_t Compress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst);
int32_t Decompress(const uint8_t* abSrc, uint32_t nLenSrc, uint8_t* abDst, uint32_t nLenDst, int32_t& nErr); int32_t Decompress(const uint8_t* abSrc, int32_t nLenSrc, uint8_t* abDst, int32_t nLenDst, int32_t& nErr);
} }

View File

@@ -7,7 +7,7 @@
#include "zlib.h" #include "zlib.h"
constexpr uint32_t CRC32_INIT = 0xFFFFFFFF; constexpr uint32_t CRC32_INIT = 0xFFFFFFFF;
constexpr auto NULL_TERMINATOR = std::string_view{ "\0\0\0", 4 }; constexpr auto NULL_TERMINATOR = std::string_view{"\0\0\0", 4};
AssetManager::AssetManager(const std::filesystem::path& path) { AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) { if (!std::filesystem::is_directory(path)) {
@@ -25,7 +25,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::exists(m_Path / ".." / "versions")) { if (!std::filesystem::exists(m_Path / ".." / "versions")) {
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded."); throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
} }
m_AssetBundleType = eAssetBundleType::Packed; m_AssetBundleType = eAssetBundleType::Packed;
m_RootPath = (m_Path / ".."); m_RootPath = (m_Path / "..");
@@ -34,7 +34,7 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) { if (!std::filesystem::exists(m_Path / ".." / ".." / "versions")) {
throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded."); throw std::runtime_error("No \"versions\" directory found in the parent directories of \"res\" - packed asset bundle cannot be loaded.");
} }
m_AssetBundleType = eAssetBundleType::Packed; m_AssetBundleType = eAssetBundleType::Packed;
m_RootPath = (m_Path / ".." / ".."); m_RootPath = (m_Path / ".." / "..");
@@ -54,15 +54,15 @@ AssetManager::AssetManager(const std::filesystem::path& path) {
} }
switch (m_AssetBundleType) { switch (m_AssetBundleType) {
case eAssetBundleType::Packed: { case eAssetBundleType::Packed: {
this->LoadPackIndex(); this->LoadPackIndex();
break; break;
} }
case eAssetBundleType::None: case eAssetBundleType::None:
[[fallthrough]]; [[fallthrough]];
case eAssetBundleType::Unpacked: { case eAssetBundleType::Unpacked: {
break; break;
} }
} }
} }
@@ -79,7 +79,7 @@ bool AssetManager::HasFile(std::string fixedName) const {
std::replace(fixedName.begin(), fixedName.end(), '\\', '/'); std::replace(fixedName.begin(), fixedName.end(), '\\', '/');
if (std::filesystem::exists(m_ResPath / fixedName)) return true; if (std::filesystem::exists(m_ResPath / fixedName)) return true;
if (this->m_AssetBundleType == eAssetBundleType::Unpacked || !m_PackIndex) return false; if (this->m_AssetBundleType == eAssetBundleType::Unpacked) return false;
std::replace(fixedName.begin(), fixedName.end(), '/', '\\'); std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName; if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
@@ -145,12 +145,8 @@ bool AssetManager::GetFile(std::string fixedName, char** data, uint32_t* len) co
} }
const auto& pack = this->m_PackIndex->GetPacks().at(packIndex); const auto& pack = this->m_PackIndex->GetPacks().at(packIndex);
bool success = false; const bool success = pack.ReadFileFromPack(crc, data, len);
try {
success = pack.ReadFileFromPack(crc, data, len);
} catch (std::exception& e) {
LOG("Failed to read file %s from pack file", fixedName.c_str());
}
return success; return success;
} }

View File

@@ -81,9 +81,6 @@ public:
[[nodiscard]] [[nodiscard]]
AssetStream GetFile(const char* name) const; AssetStream GetFile(const char* name) const;
[[nodiscard]]
AssetStream GetFile(const std::string& name) const { return GetFile(name.c_str()); };
private: private:
void LoadPackIndex(); void LoadPackIndex();

View File

@@ -46,7 +46,6 @@ bool Pack::HasFile(const uint32_t crc) const {
} }
bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const { bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) const {
const auto pathStr = m_FilePath.string();
// Time for some wacky C file reading for speed reasons // Time for some wacky C file reading for speed reasons
PackRecord pkRecord{}; PackRecord pkRecord{};
@@ -66,21 +65,16 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons
bool isCompressed = (pkRecord.m_IsCompressed & 0xff) > 0; bool isCompressed = (pkRecord.m_IsCompressed & 0xff) > 0;
auto inPackSize = isCompressed ? pkRecord.m_CompressedSize : pkRecord.m_UncompressedSize; auto inPackSize = isCompressed ? pkRecord.m_CompressedSize : pkRecord.m_UncompressedSize;
FILE* file = nullptr; FILE* file;
#ifdef _WIN32 #ifdef _WIN32
fopen_s(&file, pathStr.c_str(), "rb"); fopen_s(&file, m_FilePath.string().c_str(), "rb");
#elif __APPLE__ #elif __APPLE__
// macOS has 64bit file IO by default // macOS has 64bit file IO by default
file = fopen(pathStr.c_str(), "rb"); file = fopen(m_FilePath.string().c_str(), "rb");
#else #else
file = fopen64(pathStr.c_str(), "rb"); file = fopen64(m_FilePath.string().c_str(), "rb");
#endif #endif
if (!file) {
LOG("No file found for path %s", pathStr.c_str());
throw std::runtime_error("Could not find file " + pathStr);
}
fseek(file, pos, SEEK_SET); fseek(file, pos, SEEK_SET);
if (!isCompressed) { if (!isCompressed) {
@@ -108,18 +102,14 @@ bool Pack::ReadFileFromPack(const uint32_t crc, char** data, uint32_t* len) cons
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file); int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
pos += 4; // Move pointer position 4 to the right pos += 4; // Move pointer position 4 to the right
std::unique_ptr<char[]> chunk(new char[size]); char* chunk = static_cast<char*>(malloc(size));
int32_t readInData2 = fread(chunk.get(), sizeof(int8_t), size, file); int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
pos += size; // Move pointer position the amount of bytes read to the right pos += size; // Move pointer position the amount of bytes read to the right
int32_t err; int32_t err;
const auto countToRead = ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk.get()), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err); currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), Sd0::MAX_UNCOMPRESSED_CHUNK_SIZE, err);
if (countToRead == -1) {
LOG("Error decompressing zlib data from file %s", pathStr.c_str());
throw std::runtime_error("Error decompressing zlib data from file " + pathStr);
}
currentReadPos += countToRead;
free(chunk);
} }
*data = decompressedData; *data = decompressedData;

View File

@@ -1,166 +0,0 @@
#ifndef SCENE_COLOR_H
#define SCENE_COLOR_H
#include "NiColor.h"
#include <array>
#include <cstdint>
namespace SceneColor {
// these are not random values, they are the actual template colors used by the game
static constexpr std::array<NiColor, 146> TEMPLATE_COLORS = {{
{ 0.5019608f, 0.5019608f, 0.5019608f },
{ 1.0f, 0.0f, 0.0f },
{ 0.0f, 1.0f, 0.0f },
{ 0.0f, 0.0f, 1.0f },
{ 1.0f, 1.0f, 0.0f },
{ 1.0f, 0.0f, 1.0f },
{ 0.0f, 1.0f, 1.0f },
{ 0.5019608f, 0.0f, 1.0f },
{ 1.0f, 0.5019608f, 0.0f },
{ 1.0f, 0.5019608f, 0.5019608f },
{ 0.5019608f, 0.2509804f, 0.0f },
{ 0.5019608f, 0.0f, 0.2509804f },
{ 0.0f, 0.5019608f, 0.2509804f },
{ 0.2509804f, 0.0f, 0.5019608f },
{ 0.8745098f, 0.0f, 0.2509804f },
{ 0.2509804f, 0.8745098f, 0.5019608f },
{ 1.0f, 0.7490196f, 0.0f },
{ 1.0f, 0.2509804f, 0.0627451f },
{ 0.2509804f, 0.0f, 0.8745098f },
{ 0.7490196f, 0.0627451f, 0.0627451f },
{ 0.0627451f, 0.7490196f, 0.0627451f },
{ 1.0f, 0.5019608f, 1.0f },
{ 0.9372549f, 0.8705882f, 0.8039216f },
{ 0.8039216f, 0.5843138f, 0.4588235f },
{ 0.9921569f, 0.8509804f, 0.7098039f },
{ 0.4705882f, 0.8588235f, 0.8862745f },
{ 0.5294118f, 0.6627451f, 0.4196078f },
{ 1.0f, 0.6431373f, 0.454902f },
{ 0.9803922f, 0.9058824f, 0.7098039f },
{ 0.6235294f, 0.5058824f, 0.4392157f },
{ 0.9921569f, 0.4862745f, 0.4313726f },
{ 0.0f, 0.0f, 0.0f },
{ 0.6745098f, 0.8980392f, 0.9333333f },
{ 0.1215686f, 0.4588235f, 0.9960784f },
{ 0.6352941f, 0.6352941f, 0.8156863f },
{ 0.4f, 0.6f, 0.8f },
{ 0.05098039f, 0.5960785f, 0.7294118f },
{ 0.4509804f, 0.4f, 0.7411765f },
{ 0.8705882f, 0.3647059f, 0.5137255f },
{ 0.7960784f, 0.254902f, 0.3294118f },
{ 0.7058824f, 0.4039216f, 0.3019608f },
{ 1.0f, 0.4980392f, 0.2862745f },
{ 0.9176471f, 0.4941176f, 0.3647059f },
{ 0.6901961f, 0.7176471f, 0.7764706f },
{ 1.0f, 1.0f, 0.6f },
{ 0.1098039f, 0.827451f, 0.6352941f },
{ 1.0f, 0.6666667f, 0.8f },
{ 0.8666667f, 0.2666667f, 0.572549f },
{ 0.1137255f, 0.6745098f, 0.8392157f },
{ 0.7372549f, 0.3647059f, 0.345098f },
{ 0.8666667f, 0.5803922f, 0.4588235f },
{ 0.6039216f, 0.8078431f, 0.9215686f },
{ 1.0f, 0.7372549f, 0.8509804f },
{ 0.9921569f, 0.8588235f, 0.427451f },
{ 0.1686275f, 0.4235294f, 0.7686275f },
{ 0.9372549f, 0.8039216f, 0.7215686f },
{ 0.4313726f, 0.3176471f, 0.3764706f },
{ 0.8078431f, 1.0f, 0.1137255f },
{ 0.427451f, 0.682353f, 0.5058824f },
{ 0.7647059f, 0.3921569f, 0.772549f },
{ 0.8f, 0.4f, 0.4f },
{ 0.9058824f, 0.7764706f, 0.5921569f },
{ 0.9882353f, 0.8509804f, 0.4588235f },
{ 0.6588235f, 0.8941177f, 0.627451f },
{ 0.5843138f, 0.5686275f, 0.5490196f },
{ 0.1098039f, 0.6745098f, 0.4705882f },
{ 0.06666667f, 0.3921569f, 0.7058824f },
{ 0.9411765f, 0.9098039f, 0.5686275f },
{ 1.0f, 0.1137255f, 0.8078431f },
{ 0.6980392f, 0.9254902f, 0.3647059f },
{ 0.3647059f, 0.4627451f, 0.7960784f },
{ 0.7921569f, 0.2156863f, 0.4039216f },
{ 0.2313726f, 0.6901961f, 0.5607843f },
{ 0.9882353f, 0.7058824f, 0.8352941f },
{ 1.0f, 0.9568627f, 0.3098039f },
{ 1.0f, 0.7411765f, 0.5333334f },
{ 0.9647059f, 0.3921569f, 0.6862745f },
{ 0.6666667f, 0.9411765f, 0.8196079f },
{ 0.8039216f, 0.2901961f, 0.2980392f },
{ 0.9294118f, 0.8196079f, 0.6117647f },
{ 0.5921569f, 0.6039216f, 0.6666667f },
{ 0.7843137f, 0.2196078f, 0.3529412f },
{ 0.9372549f, 0.5960785f, 0.6666667f },
{ 0.9921569f, 0.7372549f, 0.7058824f },
{ 0.1019608f, 0.282353f, 0.4627451f },
{ 0.1882353f, 0.7294118f, 0.5607843f },
{ 0.772549f, 0.2941177f, 0.5490196f },
{ 0.09803922f, 0.454902f, 0.8235294f },
{ 0.7294118f, 0.7215686f, 0.4235294f },
{ 1.0f, 0.4588235f, 0.2196078f },
{ 1.0f, 0.1686275f, 0.1686275f },
{ 0.972549f, 0.8352941f, 0.4078431f },
{ 0.9019608f, 0.6588235f, 0.8431373f },
{ 0.254902f, 0.2901961f, 0.2980392f },
{ 1.0f, 0.4313726f, 0.2901961f },
{ 0.1098039f, 0.6627451f, 0.7882353f },
{ 1.0f, 0.8117647f, 0.6705883f },
{ 0.772549f, 0.8156863f, 0.9019608f },
{ 0.9921569f, 0.8666667f, 0.9019608f },
{ 0.08235294f, 0.5019608f, 0.4705882f },
{ 0.9882353f, 0.454902f, 0.9921569f },
{ 0.9686275f, 0.5607843f, 0.654902f },
{ 0.5568628f, 0.2705882f, 0.5215687f },
{ 0.454902f, 0.2588235f, 0.7843137f },
{ 0.6156863f, 0.5058824f, 0.7294118f },
{ 1.0f, 0.2862745f, 0.4235294f },
{ 0.8392157f, 0.5411765f, 0.3490196f },
{ 0.4431373f, 0.2941177f, 0.1372549f },
{ 1.0f, 0.282353f, 0.8156863f },
{ 0.9333333f, 0.1254902f, 0.3019608f },
{ 1.0f, 0.3254902f, 0.2862745f },
{ 0.7529412f, 0.2666667f, 0.5607843f },
{ 0.1215686f, 0.8078431f, 0.7960784f },
{ 0.4705882f, 0.3176471f, 0.6627451f },
{ 1.0f, 0.6078432f, 0.6666667f },
{ 0.9882353f, 0.1568628f, 0.2784314f },
{ 0.4627451f, 1.0f, 0.4784314f },
{ 0.6235294f, 0.8862745f, 0.7490196f },
{ 0.6470588f, 0.4117647f, 0.3098039f },
{ 0.5411765f, 0.4745098f, 0.3647059f },
{ 0.2705882f, 0.8078431f, 0.6352941f },
{ 0.8039216f, 0.772549f, 0.7607843f },
{ 0.5019608f, 0.854902f, 0.9215686f },
{ 0.9254902f, 0.9176471f, 0.7450981f },
{ 1.0f, 0.8117647f, 0.282353f },
{ 0.9921569f, 0.3686275f, 0.3254902f },
{ 0.9803922f, 0.654902f, 0.4235294f },
{ 0.09411765f, 0.654902f, 0.7098039f },
{ 0.9215686f, 0.7803922f, 0.8745098f },
{ 0.9882353f, 0.5372549f, 0.6745098f },
{ 0.8588235f, 0.8431373f, 0.8235294f },
{ 0.8705882f, 0.6666667f, 0.5333334f },
{ 0.4666667f, 0.8666667f, 0.9058824f },
{ 1.0f, 1.0f, 0.4f },
{ 0.572549f, 0.4313726f, 0.682353f },
{ 0.1960784f, 0.2901961f, 0.6980392f },
{ 0.9686275f, 0.3254902f, 0.5803922f },
{ 1.0f, 0.627451f, 0.5372549f },
{ 0.5607843f, 0.3137255f, 0.6156863f },
{ 1.0f, 1.0f, 1.0f },
{ 0.6352941f, 0.6784314f, 0.8156863f },
{ 0.9882353f, 0.4235294f, 0.5215687f },
{ 0.8039216f, 0.6431373f, 0.8705882f },
{ 0.9882353f, 0.9098039f, 0.5137255f },
{ 0.772549f, 0.8901961f, 0.5176471f },
{ 1.0f, 0.682353f, 0.2588235f },
}};
static constexpr NiColor FALLBACK_COLOR{ 1.0f, 1.0f, 1.0f };
inline const NiColor& Get(uint8_t index) {
return (index < TEMPLATE_COLORS.size()) ? TEMPLATE_COLORS[index] : FALLBACK_COLOR;
}
} // namespace SceneColor
#endif // SCENE_COLOR_H

View File

@@ -47,8 +47,6 @@ void dConfig::LoadConfig() {
void dConfig::ReloadConfig() { void dConfig::ReloadConfig() {
this->m_ConfigValues.clear(); this->m_ConfigValues.clear();
LoadConfig(); LoadConfig();
for (const auto& handler : m_ConfigHandlers) handler();
LogSettings();
} }
const std::string& dConfig::GetValue(std::string key) { const std::string& dConfig::GetValue(std::string key) {
@@ -60,18 +58,6 @@ const std::string& dConfig::GetValue(std::string key) {
return this->m_ConfigValues[key]; return this->m_ConfigValues[key];
} }
void dConfig::AddConfigHandler(std::function<void()> handler) {
m_ConfigHandlers.push_back(handler);
}
void dConfig::LogSettings() const {
LOG("Configuration settings:");
for (const auto& [key, value] : m_ConfigValues) {
const auto& valueLog = key.find("password") != std::string::npos ? "<HIDDEN>" : value;
LOG(" %s = %s", key.c_str(), valueLog.c_str());
}
}
void dConfig::ProcessLine(const std::string& line) { void dConfig::ProcessLine(const std::string& line) {
auto splitLoc = line.find('='); auto splitLoc = line.find('=');
auto key = line.substr(0, splitLoc); auto key = line.substr(0, splitLoc);
@@ -84,7 +70,3 @@ void dConfig::ProcessLine(const std::string& line) {
this->m_ConfigValues.insert(std::make_pair(key, value)); this->m_ConfigValues.insert(std::make_pair(key, value));
} }
std::string dConfig::GetValue(const std::string& key, const char* emptyValue) {
return GetValue(key, std::string(emptyValue));
};

View File

@@ -1,12 +1,8 @@
#pragma once #pragma once
#include <fstream> #include <fstream>
#include <functional>
#include <map> #include <map>
#include <string> #include <string>
#include "GeneralUtils.h"
class dConfig { class dConfig {
public: public:
dConfig(const std::string& filepath); dConfig(const std::string& filepath);
@@ -24,14 +20,6 @@ public:
*/ */
const std::string& GetValue(std::string key); const std::string& GetValue(std::string key);
// Gets a value from the config and returns the parsed value, or the default value should parsing have failed.
template<typename T>
T GetValue(const std::string& key, const T emptyValue = T()) {
return GeneralUtils::TryParse<T>(GetValue(key)).value_or(emptyValue);
}
std::string GetValue(const std::string& key, const char* emptyValue);
/** /**
* Loads the config from a file * Loads the config from a file
*/ */
@@ -41,21 +29,10 @@ public:
* Reloads the config file to reset values * Reloads the config file to reset values
*/ */
void ReloadConfig(); void ReloadConfig();
// Adds a function to be called when the config is (re)loaded
void AddConfigHandler(std::function<void()> handler);
void LogSettings() const;
private: private:
void ProcessLine(const std::string& line); void ProcessLine(const std::string& line);
private:
std::map<std::string, std::string> m_ConfigValues; std::map<std::string, std::string> m_ConfigValues;
std::vector<std::function<void()>> m_ConfigHandlers;
std::string m_ConfigFilePath; std::string m_ConfigFilePath;
}; };
template<>
inline std::string dConfig::GetValue(const std::string& key, const std::string emptyValue) {
const auto& value = GetValue(key);
return value.empty() ? emptyValue : value;
};

View File

@@ -3,7 +3,9 @@
namespace MessageType { namespace MessageType {
enum class Master : uint32_t { enum class Master : uint32_t {
REQUEST_ZONE_TRANSFER = 1, REQUEST_PERSISTENT_ID = 1,
REQUEST_PERSISTENT_ID_RESPONSE,
REQUEST_ZONE_TRANSFER,
REQUEST_ZONE_TRANSFER_RESPONSE, REQUEST_ZONE_TRANSFER_RESPONSE,
SERVER_INFO, SERVER_INFO,
REQUEST_SESSION_KEY, REQUEST_SESSION_KEY,

View File

@@ -16,8 +16,8 @@
// These are the same define, but they mean two different things in different contexts // These are the same define, but they mean two different things in different contexts
// so a different define to distinguish what calculation is happening will help clarity. // so a different define to distinguish what calculation is happening will help clarity.
#define FRAMES_TO_MS(x) (1000 / (x)) #define FRAMES_TO_MS(x) 1000 / x
#define MS_TO_FRAMES(x) (1000 / (x)) #define MS_TO_FRAMES(x) 1000 / x
//=========== FRAME TIMINGS =========== //=========== FRAME TIMINGS ===========
constexpr uint32_t highFramerate = 60; constexpr uint32_t highFramerate = 60;
@@ -58,7 +58,6 @@ constexpr LWOCLONEID LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
constexpr uint32_t MAX_MESSAGE_LENGTH = 0x500000; //!< Prevent exceptionally large msgs from being processed. Should always be used to check user provided inputs.
constexpr float PI = 3.14159f; constexpr float PI = 3.14159f;
@@ -111,6 +110,18 @@ private:
constexpr LWOSCENEID LWOSCENEID_INVALID = -1; constexpr LWOSCENEID LWOSCENEID_INVALID = -1;
struct LWONameValue {
uint32_t length = 0; //!< The length of the name
std::u16string name; //!< The name
LWONameValue() = default;
LWONameValue(const std::u16string& name) {
this->name = name;
this->length = static_cast<uint32_t>(name.length());
}
};
struct FriendData { struct FriendData {
public: public:
bool isOnline = false; bool isOnline = false;

View File

@@ -18,10 +18,7 @@ enum class eCharacterVersion : uint32_t {
SPEED_BASE, SPEED_BASE,
// Fixes nexus force explorer missions // Fixes nexus force explorer missions
NJ_JAYMISSIONS, NJ_JAYMISSIONS,
NEXUS_FORCE_EXPLORER, // Fixes pet ids in player inventories UP_TO_DATE, // will become NEXUS_FORCE_EXPLORER
PET_IDS, // Fixes pet ids in player inventories
INVENTORY_PERSISTENT_IDS, // Fixes racing meta missions
UP_TO_DATE, // will become RACING_META_MISSIONS
}; };
#endif //!__ECHARACTERVERSION__H__ #endif //!__ECHARACTERVERSION__H__

View File

@@ -1,24 +0,0 @@
#ifndef __ELOGINRESPONSE__H__
#define __ELOGINRESPONSE__H__
#include <cstdint>
enum class eLoginResponse : uint8_t {
GENERAL_FAILED = 0,
SUCCESS,
BANNED,
// Unused 3
// Unused 4
PERMISSIONS_NOT_HIGH_ENOUGH = 5,
INVALID_USER,
ACCOUNT_LOCKED,
WRONG_PASS,
ACCOUNT_ACTIVATION_PENDING,
ACCOUNT_DISABLED,
GAME_TIME_EXPIRED,
FREE_TRIAL_ENDED,
PLAY_SCHEDULE_TIME_UP,
ACCOUNT_NOT_ACTIVATED
};
#endif //!__ELOGINRESPONSE__H__

View File

@@ -50,10 +50,7 @@ enum class eMissionState : int {
/** /**
* The mission has been completed before and has now been completed again. Used for daily missions. * The mission has been completed before and has now been completed again. Used for daily missions.
*/ */
COMPLETE_READY_TO_COMPLETE = 12, COMPLETE_READY_TO_COMPLETE = 12
// The mission is failed (don't know where this is used)
FAILED = 16,
}; };
#endif //!__MISSIONSTATE__H__ #endif //!__MISSIONSTATE__H__

View File

@@ -1,12 +1,13 @@
#ifndef EOBJECTBITS_H #ifndef __EOBJECTBITS__H__
#define EOBJECTBITS_H #define __EOBJECTBITS__H__
#include <cstdint> #include <cstdint>
enum class eObjectBits : size_t { enum class eObjectBits : size_t {
PERSISTENT = 32,
CLIENT = 46, CLIENT = 46,
SPAWNED = 58, SPAWNED = 58,
CHARACTER = 60 CHARACTER = 60
}; };
#endif //!EOBJECTBITS_H #endif //!__EOBJECTBITS__H__

View File

@@ -1,24 +0,0 @@
#ifndef __ESERVERDISCONNECTIDENTIFIERS__H__
#define __ESERVERDISCONNECTIDENTIFIERS__H__
#include <cstdint>
enum class eServerDisconnectIdentifiers : uint32_t {
UNKNOWN_SERVER_ERROR = 0,
WRONG_GAME_VERSION,
WRONG_SERVER_VERSION,
CONNECTION_ON_INVALID_PORT,
DUPLICATE_LOGIN,
SERVER_SHUTDOWN,
SERVER_MAP_LOAD_FAILURE,
INVALID_SESSION_KEY,
ACCOUNT_NOT_IN_PENDING_LIST,
CHARACTER_NOT_FOUND,
CHARACTER_CORRUPTED,
KICK,
SAVE_FAILURE,
FREE_TRIAL_EXPIRED,
PLAY_SCHEDULE_TIME_DONE
};
#endif //!__ESERVERDISCONNECTIDENTIFIERS__H__

View File

@@ -1,5 +1,6 @@
#include "CDActivitiesTable.h" #include "CDActivitiesTable.h"
void CDActivitiesTable::LoadValuesFromDatabase() { void CDActivitiesTable::LoadValuesFromDatabase() {
// First, get the size of the table // First, get the size of the table
uint32_t size = 0; uint32_t size = 0;
@@ -55,13 +56,3 @@ std::vector<CDActivities> CDActivitiesTable::Query(std::function<bool(CDActiviti
return data; return data;
} }
std::optional<const CDActivities> CDActivitiesTable::GetActivity(const uint32_t activityID) {
auto& entries = GetEntries();
for (const auto& entry : entries) {
if (entry.ActivityID == activityID) {
return entry;
}
}
return std::nullopt;
}

View File

@@ -2,7 +2,6 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <optional>
struct CDActivities { struct CDActivities {
uint32_t ActivityID; uint32_t ActivityID;
@@ -32,5 +31,4 @@ public:
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDActivities> Query(std::function<bool(CDActivities)> predicate); std::vector<CDActivities> Query(std::function<bool(CDActivities)> predicate);
std::optional<const CDActivities> GetActivity(const uint32_t activityID);
}; };

View File

@@ -154,8 +154,8 @@ std::map<LOT, uint32_t> CDItemComponentTable::ParseCraftingCurrencies(const CDIt
// Checking for 2 here, not sure what to do when there's more stuff than expected // Checking for 2 here, not sure what to do when there's more stuff than expected
if (amountSplit.size() == 2) { if (amountSplit.size() == 2) {
currencies.insert({ currencies.insert({
GeneralUtils::TryParse<LOT>(amountSplit[0], LOT_NULL), std::stoull(amountSplit[0]),
GeneralUtils::TryParse<uint32_t>(amountSplit[1], 0) std::stoi(amountSplit[1])
}); });
} }
} }

View File

@@ -93,14 +93,13 @@ std::vector<CDMissions> CDMissionsTable::Query(std::function<bool(CDMissions)> p
} }
const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const { const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const {
const CDMissions* toReturn = &Default;
for (const auto& entry : GetEntries()) { for (const auto& entry : GetEntries()) {
if (entry.id == missionID) { if (entry.id == missionID) {
toReturn = &entry; return const_cast<CDMissions*>(&entry);
} }
} }
return toReturn; return &Default;
} }
const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& found) const { const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& found) const {

View File

@@ -66,7 +66,6 @@ public:
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate); std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate);
// Cannot be null.
const CDMissions* GetPtrByMissionID(uint32_t missionID) const; const CDMissions* GetPtrByMissionID(uint32_t missionID) const;
const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const; const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const;

View File

@@ -38,11 +38,3 @@ std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObje
return data; return data;
} }
std::vector<CDObjectSkills> CDObjectSkillsTable::Get(const LOT lot) const {
std::vector<CDObjectSkills> toReturn;
for (const auto& entry : GetEntries()) {
if (entry.objectTemplate == lot) toReturn.push_back(entry);
}
return toReturn;
}

View File

@@ -4,13 +4,12 @@
#include "CDTable.h" #include "CDTable.h"
#include <cstdint> #include <cstdint>
#include <vector>
struct CDObjectSkills { struct CDObjectSkills {
uint32_t objectTemplate; //!< The LOT of the item uint32_t objectTemplate; //!< The LOT of the item
uint32_t skillID; //!< The Skill ID of the object uint32_t skillID; //!< The Skill ID of the object
uint32_t castOnType; //!< ??? uint32_t castOnType; //!< ???
int32_t AICombatWeight; //!< ??? uint32_t AICombatWeight; //!< ???
}; };
class CDObjectSkillsTable : public CDTable<CDObjectSkillsTable, std::vector<CDObjectSkills>> { class CDObjectSkillsTable : public CDTable<CDObjectSkillsTable, std::vector<CDObjectSkills>> {
@@ -18,6 +17,5 @@ public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDObjectSkills> Query(std::function<bool(CDObjectSkills)> predicate); std::vector<CDObjectSkills> Query(std::function<bool(CDObjectSkills)> predicate);
std::vector<CDObjectSkills> Get(const LOT lot) const;
}; };

View File

@@ -69,7 +69,7 @@ const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
entry.name = tableData.getStringField("name", ""); entry.name = tableData.getStringField("name", "");
UNUSED(entry.placeable = tableData.getIntField("placeable", -1)); UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
entry.type = tableData.getStringField("type", ""); entry.type = tableData.getStringField("type", "");
UNUSED(entry.description = tableData.getStringField(4, "")); UNUSED(ntry.description = tableData.getStringField(4, ""));
UNUSED(entry.localize = tableData.getIntField("localize", -1)); UNUSED(entry.localize = tableData.getIntField("localize", -1));
UNUSED(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1)); UNUSED(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1));
UNUSED(entry.displayName = tableData.getStringField("displayName", "")); UNUSED(entry.displayName = tableData.getStringField("displayName", ""));

View File

@@ -56,7 +56,7 @@ CDRailActivatorComponent CDRailActivatorComponentTable::GetEntryByID(int32_t id)
std::pair<uint32_t, std::u16string> CDRailActivatorComponentTable::EffectPairFromString(std::string& str) { std::pair<uint32_t, std::u16string> CDRailActivatorComponentTable::EffectPairFromString(std::string& str) {
const auto split = GeneralUtils::SplitString(str, ':'); const auto split = GeneralUtils::SplitString(str, ':');
if (split.size() == 2) { if (split.size() == 2) {
return { GeneralUtils::TryParse(split.at(0), 0), GeneralUtils::ASCIIToUTF16(split.at(1)) }; return { std::stoi(split.at(0)), GeneralUtils::ASCIIToUTF16(split.at(1)) };
} }
return {}; return {};

View File

@@ -48,7 +48,7 @@ public:
virtual void Commit() = 0; virtual void Commit() = 0;
virtual bool GetAutoCommit() = 0; virtual bool GetAutoCommit() = 0;
virtual void SetAutoCommit(bool value) = 0; virtual void SetAutoCommit(bool value) = 0;
virtual void DeleteCharacter(const LWOOBJID characterId) = 0; virtual void DeleteCharacter(const uint32_t characterId) = 0;
}; };
#endif //!__GAMEDATABASE__H__ #endif //!__GAMEDATABASE__H__

View File

@@ -14,7 +14,7 @@ enum class eActivityType : uint32_t {
class IActivityLog { class IActivityLog {
public: public:
// Update the activity log for the given account. // Update the activity log for the given account.
virtual void UpdateActivityLog(const LWOOBJID characterId, const eActivityType activityType, const LWOMAPID mapId) = 0; virtual void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) = 0;
}; };
#endif //!__IACTIVITYLOG__H__ #endif //!__IACTIVITYLOG__H__

View File

@@ -9,7 +9,7 @@ class IBehaviors {
public: public:
struct Info { struct Info {
LWOOBJID behaviorId{}; LWOOBJID behaviorId{};
LWOOBJID characterId{}; uint32_t characterId{};
std::string behaviorInfo; std::string behaviorInfo;
}; };

View File

@@ -11,7 +11,7 @@ public:
std::string clientVersion; std::string clientVersion;
std::string otherPlayer; std::string otherPlayer;
std::string selection; std::string selection;
LWOOBJID characterId{}; uint32_t characterId{};
}; };
// Add a new bug report to the database. // Add a new bug report to the database.

View File

@@ -14,7 +14,7 @@ public:
struct Info { struct Info {
std::string name; std::string name;
std::string pendingName; std::string pendingName;
LWOOBJID id{}; uint32_t id{};
uint32_t accountId{}; uint32_t accountId{};
bool needsRename{}; bool needsRename{};
LWOCLONEID cloneId{}; LWOCLONEID cloneId{};
@@ -25,25 +25,25 @@ public:
virtual std::vector<std::string> GetApprovedCharacterNames() = 0; virtual std::vector<std::string> GetApprovedCharacterNames() = 0;
// Get the character info for the given character id. // Get the character info for the given character id.
virtual std::optional<ICharInfo::Info> GetCharacterInfo(const LWOOBJID charId) = 0; virtual std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) = 0;
// Get the character info for the given character name. // Get the character info for the given character name.
virtual std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view name) = 0; virtual std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view name) = 0;
// Get the character ids for the given account. // Get the character ids for the given account.
virtual std::vector<LWOOBJID> GetAccountCharacterIds(const LWOOBJID accountId) = 0; virtual std::vector<uint32_t> GetAccountCharacterIds(const uint32_t accountId) = 0;
// Insert a new character into the database. // Insert a new character into the database.
virtual void InsertNewCharacter(const ICharInfo::Info info) = 0; virtual void InsertNewCharacter(const ICharInfo::Info info) = 0;
// Set the name of the given character. // Set the name of the given character.
virtual void SetCharacterName(const LWOOBJID characterId, const std::string_view name) = 0; virtual void SetCharacterName(const uint32_t characterId, const std::string_view name) = 0;
// Set the pending name of the given character. // Set the pending name of the given character.
virtual void SetPendingCharacterName(const LWOOBJID characterId, const std::string_view name) = 0; virtual void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) = 0;
// Updates the given character ids last login to be right now. // Updates the given character ids last login to be right now.
virtual void UpdateLastLoggedInCharacter(const LWOOBJID characterId) = 0; virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0;
virtual bool IsNameInUse(const std::string_view name) = 0; virtual bool IsNameInUse(const std::string_view name) = 0;
}; };

View File

@@ -8,13 +8,13 @@
class ICharXml { class ICharXml {
public: public:
// Get the character xml for the given character id. // Get the character xml for the given character id.
virtual std::string GetCharacterXml(const LWOOBJID charId) = 0; virtual std::string GetCharacterXml(const uint32_t charId) = 0;
// Update the character xml for the given character id. // Update the character xml for the given character id.
virtual void UpdateCharacterXml(const LWOOBJID charId, const std::string_view lxfml) = 0; virtual void UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) = 0;
// Insert the character xml for the given character id. // Insert the character xml for the given character id.
virtual void InsertCharacterXml(const LWOOBJID characterId, const std::string_view lxfml) = 0; virtual void InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) = 0;
}; };
#endif //!__ICHARXML__H__ #endif //!__ICHARXML__H__

View File

@@ -8,7 +8,7 @@ class ICommandLog {
public: public:
// Insert a new slash command log entry. // Insert a new slash command log entry.
virtual void InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) = 0; virtual void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) = 0;
}; };
#endif //!__ICOMMANDLOG__H__ #endif //!__ICOMMANDLOG__H__

View File

@@ -8,25 +8,25 @@
class IFriends { class IFriends {
public: public:
struct BestFriendStatus { struct BestFriendStatus {
LWOOBJID playerCharacterId{}; uint32_t playerCharacterId{};
LWOOBJID friendCharacterId{}; uint32_t friendCharacterId{};
uint32_t bestFriendStatus{}; uint32_t bestFriendStatus{};
}; };
// Get the friends list for the given character id. // Get the friends list for the given character id.
virtual std::vector<FriendData> GetFriendsList(const LWOOBJID charId) = 0; virtual std::vector<FriendData> GetFriendsList(const uint32_t charId) = 0;
// Get the best friend status for the given player and friend character ids. // Get the best friend status for the given player and friend character ids.
virtual std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) = 0; virtual std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0;
// Set the best friend status for the given player and friend character ids. // Set the best friend status for the given player and friend character ids.
virtual void SetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId, const uint32_t bestFriendStatus) = 0; virtual void SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) = 0;
// Add a friend to the given character id. // Add a friend to the given character id.
virtual void AddFriend(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) = 0; virtual void AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0;
// Remove a friend from the given character id. // Remove a friend from the given character id.
virtual void RemoveFriend(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) = 0; virtual void RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0;
}; };
#endif //!__IFRIENDS__H__ #endif //!__IFRIENDS__H__

View File

@@ -9,12 +9,12 @@ class IIgnoreList {
public: public:
struct Info { struct Info {
std::string name; std::string name;
LWOOBJID id; uint32_t id;
}; };
virtual std::vector<Info> GetIgnoreList(const LWOOBJID playerId) = 0; virtual std::vector<Info> GetIgnoreList(const uint32_t playerId) = 0;
virtual void AddIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) = 0; virtual void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
virtual void RemoveIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) = 0; virtual void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
}; };
#endif //!__IIGNORELIST__H__ #endif //!__IIGNORELIST__H__

View File

@@ -5,13 +5,12 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include "dCommonVars.h"
class ILeaderboard { class ILeaderboard {
public: public:
struct Entry { struct Entry {
LWOOBJID charId{}; uint32_t charId{};
uint32_t lastPlayedTimestamp{}; uint32_t lastPlayedTimestamp{};
float primaryScore{}; float primaryScore{};
float secondaryScore{}; float secondaryScore{};
@@ -37,12 +36,12 @@ public:
virtual std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) = 0; virtual std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) = 0; virtual std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) = 0; virtual std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) = 0;
virtual std::optional<Score> GetPlayerScore(const LWOOBJID playerId, const uint32_t gameId) = 0; virtual std::optional<Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) = 0;
virtual void SaveScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) = 0; virtual void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void UpdateScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) = 0; virtual void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void IncrementNumWins(const LWOOBJID playerId, const uint32_t gameId) = 0; virtual void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) = 0;
virtual void IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) = 0; virtual void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) = 0;
}; };
#endif //!__ILEADERBOARD__H__ #endif //!__ILEADERBOARD__H__

View File

@@ -16,13 +16,13 @@ public:
virtual void InsertNewMail(const MailInfo& mail) = 0; virtual void InsertNewMail(const MailInfo& mail) = 0;
// Get the mail for the given character id. // Get the mail for the given character id.
virtual std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) = 0; virtual std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) = 0;
// Get the mail for the given mail id. // Get the mail for the given mail id.
virtual std::optional<MailInfo> GetMail(const uint64_t mailId) = 0; virtual std::optional<MailInfo> GetMail(const uint64_t mailId) = 0;
// Get the number of unread mail for the given character id. // Get the number of unread mail for the given character id.
virtual uint32_t GetUnreadMailCount(const LWOOBJID characterId) = 0; virtual uint32_t GetUnreadMailCount(const uint32_t characterId) = 0;
// Mark the given mail as read. // Mark the given mail as read.
virtual void MarkMailRead(const uint64_t mailId) = 0; virtual void MarkMailRead(const uint64_t mailId) = 0;

View File

@@ -6,19 +6,14 @@
class IObjectIdTracker { class IObjectIdTracker {
public: public:
// Only the first 48 bits of the ids are the id, the last 16 bits are reserved for flags.
struct Range {
uint64_t minID{}; // Only the first 48 bits are the id, the last 16 bits are reserved for flags.
uint64_t maxID{}; // Only the first 48 bits are the id, the last 16 bits are reserved for flags.
};
// Get the current persistent id. // Get the current persistent id.
virtual std::optional<uint64_t> GetCurrentPersistentId() = 0; virtual std::optional<uint32_t> GetCurrentPersistentId() = 0;
// Insert the default persistent id. // Insert the default persistent id.
virtual void InsertDefaultPersistentId() = 0; virtual void InsertDefaultPersistentId() = 0;
virtual Range GetPersistentIdRange() = 0; // Update the persistent id.
virtual void UpdatePersistentId(const uint32_t newId) = 0;
}; };
#endif //!__IOBJECTIDTRACKER__H__ #endif //!__IOBJECTIDTRACKER__H__

View File

@@ -13,7 +13,7 @@ public:
std::string description; std::string description;
std::string rejectionReason; std::string rejectionReason;
LWOOBJID id{}; LWOOBJID id{};
LWOOBJID ownerId{}; uint32_t ownerId{};
LWOCLONEID cloneId{}; LWOCLONEID cloneId{};
int32_t privacyOption{}; int32_t privacyOption{};
uint32_t modApproved{}; uint32_t modApproved{};
@@ -27,29 +27,25 @@ public:
uint32_t mapId{}; uint32_t mapId{};
std::string searchString; std::string searchString;
ePropertySortType sortChoice{}; ePropertySortType sortChoice{};
LWOOBJID playerId{}; uint32_t playerId{};
uint32_t numResults{}; uint32_t numResults{};
uint32_t startIndex{}; uint32_t startIndex{};
uint32_t playerSort{}; uint32_t playerSort{};
}; };
struct PropertyEntranceResult { struct PropertyEntranceResult {
// This is the number of entries that are in the query IF it were ran without a limit.
int32_t totalEntriesMatchingQuery{}; int32_t totalEntriesMatchingQuery{};
// The entries that match the query. This should only contain up to 12 entries. // The entries that match the query. This should only contain up to 12 entries.
std::vector<IProperty::Info> entries; std::vector<IProperty::Info> entries;
}; };
// Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) = 0;
// Get the property info for the given property id. // Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0; virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0;
// Get the properties for the given property lookup params. // Get the properties for the given property lookup params.
// This is expected to return a result set of up to 12 properties // This is expected to return a result set of up to 12 properties
// so as not to transfer too much data at once. // so as not to transfer too much data at once.
virtual IProperty::PropertyEntranceResult GetProperties(const PropertyLookup& params) = 0; virtual std::optional<IProperty::PropertyEntranceResult> GetProperties(const PropertyLookup& params) = 0;
// Update the property moderation info for the given property id. // Update the property moderation info for the given property id.
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0; virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;

View File

@@ -16,16 +16,16 @@ public:
NiQuaternion rotation = QuatUtils::IDENTITY; NiQuaternion rotation = QuatUtils::IDENTITY;
LWOOBJID id{}; LWOOBJID id{};
LOT lot{}; LOT lot{};
LWOOBJID ugcId{}; uint32_t ugcId{};
std::array<LWOOBJID, 5> behaviors{}; std::array<LWOOBJID, 5> behaviors{};
}; };
// Inserts a new UGC model into the database. // Inserts a new UGC model into the database.
virtual void InsertNewUgcModel( virtual void InsertNewUgcModel(
std::stringstream& sd0Data, std::stringstream& sd0Data,
const uint64_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const LWOOBJID characterId) = 0; const uint32_t characterId) = 0;
// Get the property models for the given property id. // Get the property models for the given property id.
virtual std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) = 0; virtual std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) = 0;
@@ -45,6 +45,6 @@ public:
virtual void RemoveModel(const LWOOBJID& modelId) = 0; virtual void RemoveModel(const LWOOBJID& modelId) = 0;
// Gets a model by ID // Gets a model by ID
virtual std::optional<Model> GetModel(const LWOOBJID modelID) = 0; virtual Model GetModel(const LWOOBJID modelID) = 0;
}; };
#endif //!__IPROPERTIESCONTENTS__H__ #endif //!__IPROPERTIESCONTENTS__H__

View File

@@ -29,7 +29,5 @@ public:
// Inserts a new UGC model into the database. // Inserts a new UGC model into the database.
virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0; virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) = 0;
virtual std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) = 0;
}; };
#endif //!__IUGC__H__ #endif //!__IUGC__H__

View File

@@ -7,7 +7,7 @@
class IUgcModularBuild { class IUgcModularBuild {
public: public:
virtual void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) = 0; virtual void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) = 0;
virtual void DeleteUgcBuild(const LWOOBJID bigId) = 0; virtual void DeleteUgcBuild(const LWOOBJID bigId) = 0;
}; };

View File

@@ -100,7 +100,7 @@ void MySQLDatabase::SetAutoCommit(bool value) {
con->setAutoCommit(value); con->setAutoCommit(value);
} }
void MySQLDatabase::DeleteCharacter(const LWOOBJID characterId) { void MySQLDatabase::DeleteCharacter(const uint32_t characterId) {
ExecuteDelete("DELETE FROM charxml WHERE id=? LIMIT 1;", characterId); ExecuteDelete("DELETE FROM charxml WHERE id=? LIMIT 1;", characterId);
ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId); ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId); ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId);

View File

@@ -9,20 +9,6 @@
typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef; typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef;
typedef std::unique_ptr<sql::ResultSet> UniqueResultSet; typedef std::unique_ptr<sql::ResultSet> UniqueResultSet;
// This struct is used to keep the PreparedStatement alive alongside the ResultSet, since the ResultSet will be invalidated if the PreparedStatement is destroyed.
// Declaring the members in reverse order of usage to ensure the PreparedStatement is destroyed after the ResultSet. This is guaranteed by the C++ standard.
struct PreparedStmtResultSet {
std::unique_ptr<sql::PreparedStatement> m_stmt;
std::unique_ptr<sql::ResultSet> m_resultSet;
PreparedStmtResultSet(sql::PreparedStatement* stmt = nullptr, sql::ResultSet* resultSet = nullptr)
: m_stmt(stmt), m_resultSet(resultSet) {}
sql::ResultSet* operator->() const {
return m_resultSet.get();
}
};
// Purposefully no definition for this to provide linker errors in the case someone tries to // Purposefully no definition for this to provide linker errors in the case someone tries to
// bind a parameter to a type that isn't defined. // bind a parameter to a type that isn't defined.
template<typename ParamType> template<typename ParamType>
@@ -54,31 +40,31 @@ public:
std::vector<std::string> GetApprovedCharacterNames() override; std::vector<std::string> GetApprovedCharacterNames() override;
std::vector<FriendData> GetFriendsList(LWOOBJID charID) override; std::vector<FriendData> GetFriendsList(uint32_t charID) override;
std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) override; std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override;
void SetBestFriendStatus(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId, const uint32_t bestFriendStatus) override; void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override;
void AddFriend(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId) override; void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void RemoveFriend(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId) override; void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const LWOOBJID characterId, const eActivityType activityType, const LWOMAPID mapId) override; void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override; void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override; void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override; std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override; void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override; bool IsMigrationRun(const std::string_view str) override;
void InsertMigration(const std::string_view str) override; void InsertMigration(const std::string_view str) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const LWOOBJID charId) override; std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override; std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override;
std::string GetCharacterXml(const LWOOBJID accountId) override; std::string GetCharacterXml(const uint32_t accountId) override;
void UpdateCharacterXml(const LWOOBJID characterId, const std::string_view lxfml) override; void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override;
std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override; std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override;
void InsertNewCharacter(const ICharInfo::Info info) override; void InsertNewCharacter(const ICharInfo::Info info) override;
void InsertCharacterXml(const LWOOBJID accountId, const std::string_view lxfml) override; void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override;
std::vector<LWOOBJID> GetAccountCharacterIds(LWOOBJID accountId) override; std::vector<uint32_t> GetAccountCharacterIds(uint32_t accountId) override;
void DeleteCharacter(const LWOOBJID characterId) override; void DeleteCharacter(const uint32_t characterId) override;
void SetCharacterName(const LWOOBJID characterId, const std::string_view name) override; void SetCharacterName(const uint32_t characterId, const std::string_view name) override;
void SetPendingCharacterName(const LWOOBJID characterId, const std::string_view name) override; void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override;
void UpdateLastLoggedInCharacter(const LWOOBJID characterId) override; void UpdateLastLoggedInCharacter(const uint32_t characterId) override;
void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override; void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override;
std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override; std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override; std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
@@ -97,68 +83,63 @@ public:
void InsertNewMail(const MailInfo& mail) override; void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel( void InsertNewUgcModel(
std::stringstream& sd0Data, std::stringstream& sd0Data,
const uint64_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const LWOOBJID characterId) override; const uint32_t characterId) override;
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override; std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
std::optional<MailInfo> GetMail(const uint64_t mailId) override; std::optional<MailInfo> GetMail(const uint64_t mailId) override;
uint32_t GetUnreadMailCount(const LWOOBJID characterId) override; uint32_t GetUnreadMailCount(const uint32_t characterId) override;
void MarkMailRead(const uint64_t mailId) override; void MarkMailRead(const uint64_t mailId) override;
void DeleteMail(const uint64_t mailId) override; void DeleteMail(const uint64_t mailId) override;
void ClaimMailItem(const uint64_t mailId) override; void ClaimMailItem(const uint64_t mailId) override;
void InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) override; void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override;
void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override; void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override;
void UpdateAccountBan(const uint32_t accountId, const bool banned) override; void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterInfo(const IServers::MasterInfo& info) override; void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint64_t> GetCurrentPersistentId() override; std::optional<uint32_t> GetCurrentPersistentId() override;
IObjectIdTracker::Range GetPersistentIdRange() override;
void InsertDefaultPersistentId() override; void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override; std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override; std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override; std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
void AddIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) override; void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
void RemoveIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) override; void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
std::vector<IIgnoreList::Info> GetIgnoreList(const LWOOBJID playerId) override; std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override; std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
void AddBehavior(const IBehaviors::Info& info) override; void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const LWOOBJID behaviorId) override; std::string GetBehavior(const LWOOBJID behaviorId) override;
void RemoveBehavior(const LWOOBJID characterId) override; void RemoveBehavior(const LWOOBJID characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
IProperty::PropertyEntranceResult GetProperties(const IProperty::PropertyLookup& params) override; std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override;
void SaveScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) override; void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
void UpdateScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) override; void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
std::optional<ILeaderboard::Score> GetPlayerScore(const LWOOBJID playerId, const uint32_t gameId) override; std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override;
void IncrementNumWins(const LWOOBJID playerId, const uint32_t gameId) override; void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override;
void IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) override; void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) override; void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override; void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override; uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override; bool IsNameInUse(const std::string_view name) override;
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override; IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query); sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
private: private:
// Generic query functions that can be used for any query. // Generic query functions that can be used for any query.
// Return type may be different depending on the query, so it is up to the caller to check the return type. // Return type may be different depending on the query, so it is up to the caller to check the return type.
// The first argument is the query string, and the rest are the parameters to bind to the query. // The first argument is the query string, and the rest are the parameters to bind to the query.
// The return type is a PreparedStmtResultSet which keeps the PreparedStatement alive alongside the ResultSet. // The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope
template<typename... Args> template<typename... Args>
inline PreparedStmtResultSet ExecuteSelect(const std::string& query, Args&&... args) { inline std::unique_ptr<sql::ResultSet> ExecuteSelect(const std::string& query, Args&&... args) {
PreparedStmtResultSet toReturn; std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
toReturn.m_stmt.reset(CreatePreppedStmt(query)); SetParams(preppedStmt, std::forward<Args>(args)...);
SetParams(toReturn.m_stmt, std::forward<Args>(args)...); DLU_SQL_TRY_CATCH_RETHROW(return std::unique_ptr<sql::ResultSet>(preppedStmt->executeQuery()));
DLU_SQL_TRY_CATCH_RETHROW(toReturn.m_resultSet.reset(toReturn.m_stmt->executeQuery()));
// Return the PreparedStmtResultSet, which now owns both the PreparedStatement and ResultSet via unique_ptr and will ensure they are properly cleaned up.
return toReturn;
} }
template<typename... Args> template<typename... Args>
@@ -187,91 +168,91 @@ private:
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) {
LOG_DEBUG("%s", param.data()); // LOG("%s", param.data());
stmt->setString(index, param.data()); stmt->setString(index, param.data());
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) {
LOG_DEBUG("%s", param); // LOG("%s", param);
stmt->setString(index, param); stmt->setString(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) {
LOG_DEBUG("%s", param.c_str()); // LOG("%s", param.c_str());
stmt->setString(index, param.c_str()); stmt->setString(index, param.c_str());
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) {
LOG_DEBUG("%u", param); // LOG("%u", param);
stmt->setByte(index, param); stmt->setByte(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) {
LOG_DEBUG("%d", param); // LOG("%d", param);
stmt->setByte(index, param); stmt->setByte(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) {
LOG_DEBUG("%u", param); // LOG("%u", param);
stmt->setShort(index, param); stmt->setShort(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) {
LOG_DEBUG("%d", param); // LOG("%d", param);
stmt->setShort(index, param); stmt->setShort(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) {
LOG_DEBUG("%u", param); // LOG("%u", param);
stmt->setUInt(index, param); stmt->setUInt(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) {
LOG_DEBUG("%d", param); // LOG("%d", param);
stmt->setInt(index, param); stmt->setInt(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) {
LOG_DEBUG("%llu", param); // LOG("%llu", param);
stmt->setInt64(index, param); stmt->setInt64(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) {
LOG_DEBUG("%llu", param); // LOG("%llu", param);
stmt->setUInt64(index, param); stmt->setUInt64(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) {
LOG_DEBUG("%f", param); // LOG("%f", param);
stmt->setFloat(index, param); stmt->setFloat(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) {
LOG_DEBUG("%f", param); // LOG("%f", param);
stmt->setDouble(index, param); stmt->setDouble(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) {
LOG_DEBUG("%s", param ? "true" : "false"); // LOG("%d", param);
stmt->setBoolean(index, param); stmt->setBoolean(index, param);
} }
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) {
LOG_DEBUG("Blob"); // LOG("Blob");
// This is the one time you will ever see me use const_cast. // This is the one time you will ever see me use const_cast.
stmt->setBlob(index, const_cast<std::istream*>(param)); stmt->setBlob(index, const_cast<std::istream*>(param));
} }
@@ -279,21 +260,10 @@ inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istr
template<> template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) { inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
if (param) { if (param) {
LOG_DEBUG("%d", param.value()); // LOG("%d", param.value());
stmt->setInt(index, param.value()); stmt->setInt(index, param.value());
} else { } else {
LOG_DEBUG("Null"); // LOG("Null");
stmt->setNull(index, sql::DataType::SQLNULL);
}
}
template<>
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
if (param) {
LOG_DEBUG("%d", param.value());
stmt->setInt64(index, param.value());
} else {
LOG_DEBUG("Null");
stmt->setNull(index, sql::DataType::SQLNULL); stmt->setNull(index, sql::DataType::SQLNULL);
} }
} }

View File

@@ -1,6 +1,6 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
void MySQLDatabase::UpdateActivityLog(const LWOOBJID characterId, const eActivityType activityType, const LWOMAPID mapId) { void MySQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) {
ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);", ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);",
characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId); characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId);
} }

View File

@@ -12,14 +12,14 @@ std::vector<std::string> MySQLDatabase::GetApprovedCharacterNames() {
return toReturn; return toReturn;
} }
std::optional<ICharInfo::Info> CharInfoFromQueryResult(PreparedStmtResultSet& stmt) { std::optional<ICharInfo::Info> CharInfoFromQueryResult(std::unique_ptr<sql::ResultSet> stmt) {
if (!stmt->next()) { if (!stmt->next()) {
return std::nullopt; return std::nullopt;
} }
ICharInfo::Info toReturn; ICharInfo::Info toReturn;
toReturn.id = stmt->getInt64("id"); toReturn.id = stmt->getUInt("id");
toReturn.name = stmt->getString("name").c_str(); toReturn.name = stmt->getString("name").c_str();
toReturn.pendingName = stmt->getString("pending_name").c_str(); toReturn.pendingName = stmt->getString("pending_name").c_str();
toReturn.needsRename = stmt->getBoolean("needs_rename"); toReturn.needsRename = stmt->getBoolean("needs_rename");
@@ -30,23 +30,25 @@ std::optional<ICharInfo::Info> CharInfoFromQueryResult(PreparedStmtResultSet& st
return toReturn; return toReturn;
} }
std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const LWOOBJID charId) { std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const uint32_t charId) {
auto result = ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId); return CharInfoFromQueryResult(
return CharInfoFromQueryResult(result); ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId)
);
} }
std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const std::string_view name) { std::optional<ICharInfo::Info> MySQLDatabase::GetCharacterInfo(const std::string_view name) {
auto result = ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name); return CharInfoFromQueryResult(
return CharInfoFromQueryResult(result); ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name)
);
} }
std::vector<LWOOBJID> MySQLDatabase::GetAccountCharacterIds(const LWOOBJID accountId) { std::vector<uint32_t> MySQLDatabase::GetAccountCharacterIds(const uint32_t accountId) {
auto result = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId); auto result = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId);
std::vector<LWOOBJID> toReturn; std::vector<uint32_t> toReturn;
toReturn.reserve(result->rowsCount()); toReturn.reserve(result->rowsCount());
while (result->next()) { while (result->next()) {
toReturn.push_back(result->getInt64("id")); toReturn.push_back(result->getUInt("id"));
} }
return toReturn; return toReturn;
@@ -63,15 +65,15 @@ void MySQLDatabase::InsertNewCharacter(const ICharInfo::Info info) {
static_cast<uint32_t>(time(NULL))); static_cast<uint32_t>(time(NULL)));
} }
void MySQLDatabase::SetCharacterName(const LWOOBJID characterId, const std::string_view name) { void MySQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1;", name, static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1;", name, static_cast<uint32_t>(time(NULL)), characterId);
} }
void MySQLDatabase::SetPendingCharacterName(const LWOOBJID characterId, const std::string_view name) { void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1", name, static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1", name, static_cast<uint32_t>(time(NULL)), characterId);
} }
void MySQLDatabase::UpdateLastLoggedInCharacter(const LWOOBJID characterId) { void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast<uint32_t>(time(NULL)), characterId);
} }

View File

@@ -1,6 +1,6 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
std::string MySQLDatabase::GetCharacterXml(const LWOOBJID charId) { std::string MySQLDatabase::GetCharacterXml(const uint32_t charId) {
auto result = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId); auto result = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId);
if (!result->next()) { if (!result->next()) {
@@ -10,10 +10,10 @@ std::string MySQLDatabase::GetCharacterXml(const LWOOBJID charId) {
return result->getString("xml_data").c_str(); return result->getString("xml_data").c_str();
} }
void MySQLDatabase::UpdateCharacterXml(const LWOOBJID charId, const std::string_view lxfml) { void MySQLDatabase::UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) {
ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId); ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId);
} }
void MySQLDatabase::InsertCharacterXml(const LWOOBJID characterId, const std::string_view lxfml) { void MySQLDatabase::InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) {
ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml); ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml);
} }

View File

@@ -1,5 +1,5 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
void MySQLDatabase::InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) { void MySQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) {
ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command); ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command);
} }

View File

@@ -1,6 +1,6 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
std::vector<FriendData> MySQLDatabase::GetFriendsList(const LWOOBJID charId) { std::vector<FriendData> MySQLDatabase::GetFriendsList(const uint32_t charId) {
auto friendsList = ExecuteSelect( auto friendsList = ExecuteSelect(
R"QUERY( R"QUERY(
SELECT fr.requested_player AS player, best_friend AS bff, ci.name AS name FROM SELECT fr.requested_player AS player, best_friend AS bff, ci.name AS name FROM
@@ -19,7 +19,7 @@ std::vector<FriendData> MySQLDatabase::GetFriendsList(const LWOOBJID charId) {
while (friendsList->next()) { while (friendsList->next()) {
FriendData fd; FriendData fd;
fd.friendID = friendsList->getUInt64("player"); fd.friendID = friendsList->getUInt("player");
fd.isBestFriend = friendsList->getInt("bff") == 3; // 0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs fd.isBestFriend = friendsList->getInt("bff") == 3; // 0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
fd.friendName = friendsList->getString("name").c_str(); fd.friendName = friendsList->getString("name").c_str();
@@ -29,7 +29,7 @@ std::vector<FriendData> MySQLDatabase::GetFriendsList(const LWOOBJID charId) {
return toReturn; return toReturn;
} }
std::optional<IFriends::BestFriendStatus> MySQLDatabase::GetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) { std::optional<IFriends::BestFriendStatus> MySQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
auto result = ExecuteSelect("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", auto result = ExecuteSelect("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;",
playerCharacterId, playerCharacterId,
friendCharacterId, friendCharacterId,
@@ -42,14 +42,14 @@ std::optional<IFriends::BestFriendStatus> MySQLDatabase::GetBestFriendStatus(con
} }
IFriends::BestFriendStatus toReturn; IFriends::BestFriendStatus toReturn;
toReturn.playerCharacterId = result->getUInt64("player_id"); toReturn.playerCharacterId = result->getUInt("player_id");
toReturn.friendCharacterId = result->getUInt64("friend_id"); toReturn.friendCharacterId = result->getUInt("friend_id");
toReturn.bestFriendStatus = result->getUInt("best_friend"); toReturn.bestFriendStatus = result->getUInt("best_friend");
return toReturn; return toReturn;
} }
void MySQLDatabase::SetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId, const uint32_t bestFriendStatus) { void MySQLDatabase::SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) {
ExecuteUpdate("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", ExecuteUpdate("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;",
bestFriendStatus, bestFriendStatus,
playerCharacterId, playerCharacterId,
@@ -59,11 +59,11 @@ void MySQLDatabase::SetBestFriendStatus(const LWOOBJID playerCharacterId, const
); );
} }
void MySQLDatabase::AddFriend(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) { void MySQLDatabase::AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
ExecuteInsert("INSERT IGNORE INTO friends (player_id, friend_id, best_friend) VALUES (?, ?, 0);", playerCharacterId, friendCharacterId); ExecuteInsert("INSERT IGNORE INTO friends (player_id, friend_id, best_friend) VALUES (?, ?, 0);", playerCharacterId, friendCharacterId);
} }
void MySQLDatabase::RemoveFriend(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) { void MySQLDatabase::RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
ExecuteDelete("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", ExecuteDelete("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;",
playerCharacterId, playerCharacterId,
friendCharacterId, friendCharacterId,

View File

@@ -1,22 +1,22 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
std::vector<IIgnoreList::Info> MySQLDatabase::GetIgnoreList(const LWOOBJID playerId) { std::vector<IIgnoreList::Info> MySQLDatabase::GetIgnoreList(const uint32_t playerId) {
auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId); auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId);
std::vector<IIgnoreList::Info> ignoreList; std::vector<IIgnoreList::Info> ignoreList;
ignoreList.reserve(result->rowsCount()); ignoreList.reserve(result->rowsCount());
while (result->next()) { while (result->next()) {
ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getInt64("ignore_id") }); ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getUInt("ignore_id") });
} }
return ignoreList; return ignoreList;
} }
void MySQLDatabase::AddIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) { void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId); ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId);
} }
void MySQLDatabase::RemoveIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) { void MySQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId); ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId);
} }

View File

@@ -14,14 +14,14 @@ std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityI
return donation_total->getUInt("donation_total"); return donation_total->getUInt("donation_total");
} }
std::vector<ILeaderboard::Entry> ProcessQuery(PreparedStmtResultSet& rows) { std::vector<ILeaderboard::Entry> ProcessQuery(UniqueResultSet& rows) {
std::vector<ILeaderboard::Entry> entries; std::vector<ILeaderboard::Entry> entries;
entries.reserve(rows->rowsCount()); entries.reserve(rows->rowsCount());
while (rows->next()) { while (rows->next()) {
auto& entry = entries.emplace_back(); auto& entry = entries.emplace_back();
entry.charId = rows->getUInt64("character_id"); entry.charId = rows->getUInt("character_id");
entry.lastPlayedTimestamp = rows->getUInt("lp_unix"); entry.lastPlayedTimestamp = rows->getUInt("lp_unix");
entry.primaryScore = rows->getFloat("primaryScore"); entry.primaryScore = rows->getFloat("primaryScore");
entry.secondaryScore = rows->getFloat("secondaryScore"); entry.secondaryScore = rows->getFloat("secondaryScore");
@@ -58,21 +58,21 @@ std::vector<ILeaderboard::Entry> MySQLDatabase::GetNsLeaderboard(const uint32_t
return ProcessQuery(leaderboard); return ProcessQuery(leaderboard);
} }
void MySQLDatabase::SaveScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) { void MySQLDatabase::SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("INSERT leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, character_id = ?, game_id = ?;", ExecuteInsert("INSERT leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, character_id = ?, game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId); score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
} }
void MySQLDatabase::UpdateScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) { void MySQLDatabase::UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("UPDATE leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;", ExecuteInsert("UPDATE leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId); score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
} }
void MySQLDatabase::IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) { void MySQLDatabase::IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId); ExecuteUpdate("UPDATE leaderboard SET timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId);
} }
std::optional<ILeaderboard::Score> MySQLDatabase::GetPlayerScore(const LWOOBJID playerId, const uint32_t gameId) { std::optional<ILeaderboard::Score> MySQLDatabase::GetPlayerScore(const uint32_t playerId, const uint32_t gameId) {
std::optional<ILeaderboard::Score> toReturn = std::nullopt; std::optional<ILeaderboard::Score> toReturn = std::nullopt;
auto res = ExecuteSelect("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;", playerId, gameId); auto res = ExecuteSelect("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;", playerId, gameId);
if (res->next()) { if (res->next()) {
@@ -86,6 +86,6 @@ std::optional<ILeaderboard::Score> MySQLDatabase::GetPlayerScore(const LWOOBJID
return toReturn; return toReturn;
} }
void MySQLDatabase::IncrementNumWins(const LWOOBJID playerId, const uint32_t gameId) { void MySQLDatabase::IncrementNumWins(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId); ExecuteUpdate("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId);
} }

View File

@@ -19,7 +19,7 @@ void MySQLDatabase::InsertNewMail(const MailInfo& mail) {
mail.itemCount); mail.itemCount);
} }
std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) { std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
auto res = ExecuteSelect( auto res = ExecuteSelect(
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent" "SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
" FROM mail WHERE receiver_id=? limit ?;", " FROM mail WHERE receiver_id=? limit ?;",
@@ -48,7 +48,7 @@ std::vector<MailInfo> MySQLDatabase::GetMailForPlayer(const LWOOBJID characterId
} }
std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) { std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
auto res = ExecuteSelect("SELECT attachment_lot, attachment_count, receiver_id FROM mail WHERE id=? LIMIT 1;", mailId); auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
if (!res->next()) { if (!res->next()) {
return std::nullopt; return std::nullopt;
@@ -57,12 +57,11 @@ std::optional<MailInfo> MySQLDatabase::GetMail(const uint64_t mailId) {
MailInfo toReturn; MailInfo toReturn;
toReturn.itemLOT = res->getInt("attachment_lot"); toReturn.itemLOT = res->getInt("attachment_lot");
toReturn.itemCount = res->getInt("attachment_count"); toReturn.itemCount = res->getInt("attachment_count");
toReturn.receiverId = res->getUInt64("receiver_id");
return toReturn; return toReturn;
} }
uint32_t MySQLDatabase::GetUnreadMailCount(const LWOOBJID characterId) { uint32_t MySQLDatabase::GetUnreadMailCount(const uint32_t characterId) {
auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId); auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId);
if (!res->next()) { if (!res->next()) {

View File

@@ -1,42 +1,17 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
std::optional<uint64_t> MySQLDatabase::GetCurrentPersistentId() { std::optional<uint32_t> MySQLDatabase::GetCurrentPersistentId() {
auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker"); auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
if (!result->next()) { if (!result->next()) {
return std::nullopt; return std::nullopt;
} }
return result->getUInt64("last_object_id"); return result->getUInt("last_object_id");
} }
void MySQLDatabase::InsertDefaultPersistentId() { void MySQLDatabase::InsertDefaultPersistentId() {
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);"); ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
} }
IObjectIdTracker::Range MySQLDatabase::GetPersistentIdRange() { void MySQLDatabase::UpdatePersistentId(const uint32_t newId) {
IObjectIdTracker::Range range; ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
auto prevCommit = GetAutoCommit();
SetAutoCommit(false);
// THIS MUST ABSOLUTELY NOT FAIL. These IDs are expected to be unique. As such a transactional select is used to safely get a range
// of IDs that will never be used again. A separate feature could track unused IDs and recycle them, but that is not implemented.
ExecuteCustomQuery("START TRANSACTION;");
// 200 seems like a good range to reserve at once. Only way this would be noticable is if a player
// added hundreds of items at once.
// Doing the update first ensures that all other connections are blocked from accessing this table until we commit.
auto result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
// If no rows were updated, it means the table is empty, so we need to insert the default row first.
if (result == 0) {
InsertDefaultPersistentId();
result = ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = last_object_id + 200;");
}
// At this point all connections are waiting on us to finish the transaction, so we can safely select the ID we just set.
auto selectRes = ExecuteSelect("SELECT last_object_id FROM object_id_tracker;");
selectRes->next();
range.maxID = selectRes->getUInt64("last_object_id");
range.minID = range.maxID - 199;
ExecuteCustomQuery("COMMIT;");
SetAutoCommit(prevCommit);
return range;
} }

View File

@@ -1,27 +1,10 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
#include "ePropertySortType.h" #include "ePropertySortType.h"
IProperty::Info ReadPropertyInfo(PreparedStmtResultSet& result) { std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
IProperty::Info info; std::optional<IProperty::PropertyEntranceResult> result;
info.id = result->getUInt64("id");
info.ownerId = result->getInt64("owner_id");
info.cloneId = result->getUInt64("clone_id");
info.name = result->getString("name").c_str();
info.description = result->getString("description").c_str();
info.privacyOption = result->getInt("privacy_option");
info.rejectionReason = result->getString("rejection_reason").c_str();
info.lastUpdatedTime = result->getUInt("last_updated");
info.claimedTime = result->getUInt("time_claimed");
info.reputation = result->getUInt("reputation");
info.modApproved = result->getUInt("mod_approved");
info.performanceCost = result->getFloat("performance_cost");
return info;
}
IProperty::PropertyEntranceResult MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
IProperty::PropertyEntranceResult result;
std::string query; std::string query;
PreparedStmtResultSet properties; std::unique_ptr<sql::ResultSet> properties;
if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) { if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) {
query = R"QUERY( query = R"QUERY(
@@ -73,7 +56,8 @@ IProperty::PropertyEntranceResult MySQLDatabase::GetProperties(const IProperty::
params.playerId params.playerId
); );
if (count->next()) { if (count->next()) {
result.totalEntriesMatchingQuery = count->getUInt("count"); if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count");
} }
} else { } else {
if (params.sortChoice == SORT_TYPE_REPUTATION) { if (params.sortChoice == SORT_TYPE_REPUTATION) {
@@ -126,12 +110,26 @@ IProperty::PropertyEntranceResult MySQLDatabase::GetProperties(const IProperty::
params.playerSort params.playerSort
); );
if (count->next()) { if (count->next()) {
result.totalEntriesMatchingQuery = count->getUInt("count"); if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count");
} }
} }
while (properties->next()) { while (properties->next()) {
result.entries.push_back(ReadPropertyInfo(properties)); if (!result) result = IProperty::PropertyEntranceResult();
auto& entry = result->entries.emplace_back();
entry.id = properties->getUInt64("id");
entry.ownerId = properties->getUInt64("owner_id");
entry.cloneId = properties->getUInt64("clone_id");
entry.name = properties->getString("name").c_str();
entry.description = properties->getString("description").c_str();
entry.privacyOption = properties->getInt("privacy_option");
entry.rejectionReason = properties->getString("rejection_reason").c_str();
entry.lastUpdatedTime = properties->getUInt("last_updated");
entry.claimedTime = properties->getUInt("time_claimed");
entry.reputation = properties->getUInt("reputation");
entry.modApproved = properties->getUInt("mod_approved");
entry.performanceCost = properties->getFloat("performance_cost");
} }
return result; return result;
@@ -146,7 +144,21 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
return std::nullopt; return std::nullopt;
} }
return ReadPropertyInfo(propertyEntry); IProperty::Info toReturn;
toReturn.id = propertyEntry->getUInt64("id");
toReturn.ownerId = propertyEntry->getUInt64("owner_id");
toReturn.cloneId = propertyEntry->getUInt64("clone_id");
toReturn.name = propertyEntry->getString("name").c_str();
toReturn.description = propertyEntry->getString("description").c_str();
toReturn.privacyOption = propertyEntry->getInt("privacy_option");
toReturn.rejectionReason = propertyEntry->getString("rejection_reason").c_str();
toReturn.lastUpdatedTime = propertyEntry->getUInt("last_updated");
toReturn.claimedTime = propertyEntry->getUInt("time_claimed");
toReturn.reputation = propertyEntry->getUInt("reputation");
toReturn.modApproved = propertyEntry->getUInt("mod_approved");
toReturn.performanceCost = propertyEntry->getFloat("performance_cost");
return toReturn;
} }
void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) { void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
@@ -183,15 +195,3 @@ void MySQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_
zoneId.GetMapID() zoneId.GetMapID()
); );
} }
std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOOBJID id) {
auto propertyEntry = ExecuteSelect(
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
"FROM properties WHERE id = ?;", id);
if (!propertyEntry->next()) {
return std::nullopt;
}
return ReadPropertyInfo(propertyEntry);
}

View File

@@ -64,27 +64,26 @@ void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId); ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
} }
std::optional<IPropertyContents::Model> MySQLDatabase::GetModel(const LWOOBJID modelID) { IPropertyContents::Model MySQLDatabase::GetModel(const LWOOBJID modelID) {
auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID); auto result = ExecuteSelect("SELECT * FROM properties_contents WHERE id = ?", modelID);
std::optional<IPropertyContents::Model> model = std::nullopt; IPropertyContents::Model model{};
while (result->next()) { while (result->next()) {
model = IPropertyContents::Model{}; model.id = result->getUInt64("id");
model->id = result->getUInt64("id"); model.lot = static_cast<LOT>(result->getUInt("lot"));
model->lot = static_cast<LOT>(result->getUInt("lot")); model.position.x = result->getFloat("x");
model->position.x = result->getFloat("x"); model.position.y = result->getFloat("y");
model->position.y = result->getFloat("y"); model.position.z = result->getFloat("z");
model->position.z = result->getFloat("z"); model.rotation.w = result->getFloat("rw");
model->rotation.w = result->getFloat("rw"); model.rotation.x = result->getFloat("rx");
model->rotation.x = result->getFloat("rx"); model.rotation.y = result->getFloat("ry");
model->rotation.y = result->getFloat("ry"); model.rotation.z = result->getFloat("rz");
model->rotation.z = result->getFloat("rz"); model.ugcId = result->getUInt64("ugc_id");
model->ugcId = result->getUInt64("ugc_id"); model.behaviors[0] = result->getUInt64("behavior_1");
model->behaviors[0] = result->getUInt64("behavior_1"); model.behaviors[1] = result->getUInt64("behavior_2");
model->behaviors[1] = result->getUInt64("behavior_2"); model.behaviors[2] = result->getUInt64("behavior_3");
model->behaviors[2] = result->getUInt64("behavior_3"); model.behaviors[3] = result->getUInt64("behavior_4");
model->behaviors[3] = result->getUInt64("behavior_4"); model.behaviors[4] = result->getUInt64("behavior_5");
model->behaviors[4] = result->getUInt64("behavior_5");
} }
return model; return model;

View File

@@ -1,17 +1,5 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
IUgc::Model ReadModel(PreparedStmtResultSet& result) {
IUgc::Model model;
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
model.id = result->getUInt64("ugcID");
model.modelID = result->getUInt64("modelID");
return model;
}
std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
auto result = ExecuteSelect( auto result = ExecuteSelect(
"SELECT lxfml, u.id as ugcID, pc.id as modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;", "SELECT lxfml, u.id as ugcID, pc.id as modelID FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;",
@@ -20,7 +8,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId)
std::vector<IUgc::Model> toReturn; std::vector<IUgc::Model> toReturn;
while (result->next()) { while (result->next()) {
toReturn.push_back(ReadModel(result)); IUgc::Model model;
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
model.id = result->getUInt64("ugcID");
model.modelID = result->getUInt64("modelID");
toReturn.push_back(std::move(model));
} }
return toReturn; return toReturn;
@@ -32,7 +27,14 @@ std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
std::vector<IUgc::Model> models; std::vector<IUgc::Model> models;
models.reserve(result->rowsCount()); models.reserve(result->rowsCount());
while (result->next()) { while (result->next()) {
models.push_back(ReadModel(result)); IUgc::Model model;
model.id = result->getInt64("ugcID");
model.modelID = result->getUInt64("modelID");
// blob is owned by the query, so we need to do a deep copy :/
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
model.lxfmlData << blob->rdbuf();
models.push_back(std::move(model));
} }
return models; return models;
@@ -43,10 +45,10 @@ void MySQLDatabase::RemoveUnreferencedUgcModels() {
} }
void MySQLDatabase::InsertNewUgcModel( void MySQLDatabase::InsertNewUgcModel(
std::stringstream& sd0Data, // cant be const sad std:: stringstream& sd0Data, // cant be const sad
const uint64_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const LWOOBJID characterId) { const uint32_t characterId) {
const std::istream stream(sd0Data.rdbuf()); const std::istream stream(sd0Data.rdbuf());
ExecuteInsert( ExecuteInsert(
"INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)", "INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)",
@@ -69,14 +71,3 @@ void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::stringstrea
const std::istream stream(lxfml.rdbuf()); const std::istream stream(lxfml.rdbuf());
ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId);
} }
std::optional<IUgc::Model> MySQLDatabase::GetUgcModel(const LWOOBJID ugcId) {
auto result = ExecuteSelect("SELECT u.id AS ugcID, lxfml, pc.id AS modelID FROM ugc AS u JOIN properties_contents AS pc ON pc.ugc_id = u.id WHERE u.id = ?", ugcId);
std::optional<IUgc::Model> toReturn = std::nullopt;
if (result->next()) {
toReturn = ReadModel(result);
}
return toReturn;
}

View File

@@ -1,6 +1,6 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
void MySQLDatabase::InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) { void MySQLDatabase::InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) {
ExecuteInsert("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)", bigId, modules, characterId); ExecuteInsert("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)", bigId, modules, characterId);
} }

View File

@@ -61,13 +61,13 @@ bool SQLiteDatabase::GetAutoCommit() {
void SQLiteDatabase::SetAutoCommit(bool value) { void SQLiteDatabase::SetAutoCommit(bool value) {
if (value) { if (value) {
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
} else {
if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML(); if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML();
} else {
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
} }
} }
void SQLiteDatabase::DeleteCharacter(const LWOOBJID characterId) { void SQLiteDatabase::DeleteCharacter(const uint32_t characterId) {
ExecuteDelete("DELETE FROM charxml WHERE id=?;", characterId); ExecuteDelete("DELETE FROM charxml WHERE id=?;", characterId);
ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId); ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId); ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId);

View File

@@ -38,31 +38,31 @@ public:
std::vector<std::string> GetApprovedCharacterNames() override; std::vector<std::string> GetApprovedCharacterNames() override;
std::vector<FriendData> GetFriendsList(LWOOBJID charID) override; std::vector<FriendData> GetFriendsList(uint32_t charID) override;
std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const LWOOBJID playerCharacterId, const LWOOBJID friendCharacterId) override; std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override;
void SetBestFriendStatus(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId, const uint32_t bestFriendStatus) override; void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override;
void AddFriend(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId) override; void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void RemoveFriend(const LWOOBJID playerAccountId, const LWOOBJID friendAccountId) override; void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const LWOOBJID characterId, const eActivityType activityType, const LWOMAPID mapId) override; void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override; void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override; void UpdateUgcModelData(const LWOOBJID& modelId, std::stringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override; std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override; void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override; bool IsMigrationRun(const std::string_view str) override;
void InsertMigration(const std::string_view str) override; void InsertMigration(const std::string_view str) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const LWOOBJID charId) override; std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override; std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override;
std::string GetCharacterXml(const LWOOBJID accountId) override; std::string GetCharacterXml(const uint32_t accountId) override;
void UpdateCharacterXml(const LWOOBJID characterId, const std::string_view lxfml) override; void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override;
std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override; std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override;
void InsertNewCharacter(const ICharInfo::Info info) override; void InsertNewCharacter(const ICharInfo::Info info) override;
void InsertCharacterXml(const LWOOBJID accountId, const std::string_view lxfml) override; void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override;
std::vector<LWOOBJID> GetAccountCharacterIds(LWOOBJID accountId) override; std::vector<uint32_t> GetAccountCharacterIds(uint32_t accountId) override;
void DeleteCharacter(const LWOOBJID characterId) override; void DeleteCharacter(const uint32_t characterId) override;
void SetCharacterName(const LWOOBJID characterId, const std::string_view name) override; void SetCharacterName(const uint32_t characterId, const std::string_view name) override;
void SetPendingCharacterName(const LWOOBJID characterId, const std::string_view name) override; void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override;
void UpdateLastLoggedInCharacter(const LWOOBJID characterId) override; void UpdateLastLoggedInCharacter(const uint32_t characterId) override;
void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override; void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override;
std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override; std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override; std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
@@ -81,53 +81,51 @@ public:
void InsertNewMail(const MailInfo& mail) override; void InsertNewMail(const MailInfo& mail) override;
void InsertNewUgcModel( void InsertNewUgcModel(
std::stringstream& sd0Data, std::stringstream& sd0Data,
const uint64_t blueprintId, const uint32_t blueprintId,
const uint32_t accountId, const uint32_t accountId,
const LWOOBJID characterId) override; const uint32_t characterId) override;
std::vector<MailInfo> GetMailForPlayer(const LWOOBJID characterId, const uint32_t numberOfMail) override; std::vector<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
std::optional<MailInfo> GetMail(const uint64_t mailId) override; std::optional<MailInfo> GetMail(const uint64_t mailId) override;
uint32_t GetUnreadMailCount(const LWOOBJID characterId) override; uint32_t GetUnreadMailCount(const uint32_t characterId) override;
void MarkMailRead(const uint64_t mailId) override; void MarkMailRead(const uint64_t mailId) override;
void DeleteMail(const uint64_t mailId) override; void DeleteMail(const uint64_t mailId) override;
void ClaimMailItem(const uint64_t mailId) override; void ClaimMailItem(const uint64_t mailId) override;
void InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) override; void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override;
void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override; void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override;
void UpdateAccountBan(const uint32_t accountId, const bool banned) override; void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterInfo(const IServers::MasterInfo& info) override; void SetMasterInfo(const IServers::MasterInfo& info) override;
std::optional<uint64_t> GetCurrentPersistentId() override; std::optional<uint32_t> GetCurrentPersistentId() override;
IObjectIdTracker::Range GetPersistentIdRange() override;
void InsertDefaultPersistentId() override; void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override; std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override; std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override; std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
void AddIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) override; void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
void RemoveIgnore(const LWOOBJID playerId, const LWOOBJID ignoredPlayerId) override; void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
std::vector<IIgnoreList::Info> GetIgnoreList(const LWOOBJID playerId) override; std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override; std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
void AddBehavior(const IBehaviors::Info& info) override; void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const LWOOBJID behaviorId) override; std::string GetBehavior(const LWOOBJID behaviorId) override;
void RemoveBehavior(const LWOOBJID characterId) override; void RemoveBehavior(const LWOOBJID characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override; void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
IProperty::PropertyEntranceResult GetProperties(const IProperty::PropertyLookup& params) override; std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override; std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override;
void SaveScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) override; void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
void UpdateScore(const LWOOBJID playerId, const uint32_t gameId, const Score& score) override; void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
std::optional<ILeaderboard::Score> GetPlayerScore(const LWOOBJID playerId, const uint32_t gameId) override; std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override;
void IncrementNumWins(const LWOOBJID playerId, const uint32_t gameId) override; void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override;
void IncrementTimesPlayed(const LWOOBJID playerId, const uint32_t gameId) override; void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<LWOOBJID> characterId) override; void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override; void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override; uint32_t GetAccountCount() override;
bool IsNameInUse(const std::string_view name) override; bool IsNameInUse(const std::string_view name) override;
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override; IPropertyContents::Model GetModel(const LWOOBJID modelID) override;
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOOBJID id) override;
private: private:
CppSQLite3Statement CreatePreppedStmt(const std::string& query); CppSQLite3Statement CreatePreppedStmt(const std::string& query);
@@ -170,91 +168,91 @@ private:
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string_view param) { inline void SetParam(PreppedStmtRef stmt, const int index, const std::string_view param) {
LOG_DEBUG("%s", param.data()); LOG("%s", param.data());
stmt.bind(index, param.data()); stmt.bind(index, param.data());
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const char* param) { inline void SetParam(PreppedStmtRef stmt, const int index, const char* param) {
LOG_DEBUG("%s", param); LOG("%s", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string param) { inline void SetParam(PreppedStmtRef stmt, const int index, const std::string param) {
LOG_DEBUG("%s", param.c_str()); LOG("%s", param.c_str());
stmt.bind(index, param.c_str()); stmt.bind(index, param.c_str());
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int8_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const int8_t param) {
LOG_DEBUG("%u", param); LOG("%u", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint8_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const uint8_t param) {
LOG_DEBUG("%d", param); LOG("%d", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int16_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const int16_t param) {
LOG_DEBUG("%u", param); LOG("%u", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint16_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const uint16_t param) {
LOG_DEBUG("%d", param); LOG("%d", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint32_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const uint32_t param) {
LOG_DEBUG("%u", param); LOG("%u", param);
stmt.bind(index, static_cast<int32_t>(param)); stmt.bind(index, static_cast<int32_t>(param));
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int32_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const int32_t param) {
LOG_DEBUG("%d", param); LOG("%d", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int64_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const int64_t param) {
LOG_DEBUG("%llu", param); LOG("%llu", param);
stmt.bind(index, static_cast<sqlite_int64>(param)); stmt.bind(index, static_cast<sqlite_int64>(param));
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint64_t param) { inline void SetParam(PreppedStmtRef stmt, const int index, const uint64_t param) {
LOG_DEBUG("%llu", param); LOG("%llu", param);
stmt.bind(index, static_cast<sqlite_int64>(param)); stmt.bind(index, static_cast<sqlite_int64>(param));
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const float param) { inline void SetParam(PreppedStmtRef stmt, const int index, const float param) {
LOG_DEBUG("%f", param); LOG("%f", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const double param) { inline void SetParam(PreppedStmtRef stmt, const int index, const double param) {
LOG_DEBUG("%f", param); LOG("%f", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const bool param) { inline void SetParam(PreppedStmtRef stmt, const int index, const bool param) {
LOG_DEBUG("%d", param); LOG("%d", param);
stmt.bind(index, param); stmt.bind(index, param);
} }
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* param) { inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* param) {
LOG_DEBUG("Blob"); LOG("Blob");
// This is the one time you will ever see me use const_cast. // This is the one time you will ever see me use const_cast.
std::stringstream stream; std::stringstream stream;
stream << param->rdbuf(); stream << param->rdbuf();
@@ -264,21 +262,10 @@ inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* p
template<> template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) { inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
if (param) { if (param) {
LOG_DEBUG("%d", param.value()); LOG("%d", param.value());
stmt.bind(index, static_cast<int>(param.value())); stmt.bind(index, static_cast<int>(param.value()));
} else { } else {
LOG_DEBUG("Null"); LOG("Null");
stmt.bindNull(index);
}
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<LWOOBJID> param) {
if (param) {
LOG_DEBUG("%d", param.value());
stmt.bind(index, static_cast<sqlite_int64>(param.value()));
} else {
LOG_DEBUG("Null");
stmt.bindNull(index); stmt.bindNull(index);
} }
} }

View File

@@ -1,6 +1,6 @@
#include "SQLiteDatabase.h" #include "SQLiteDatabase.h"
void SQLiteDatabase::UpdateActivityLog(const LWOOBJID characterId, const eActivityType activityType, const LWOMAPID mapId) { void SQLiteDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) {
ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);", ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);",
characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId); characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId);
} }

View File

@@ -20,7 +20,7 @@ std::optional<ICharInfo::Info> CharInfoFromQueryResult(CppSQLite3Query stmt) {
ICharInfo::Info toReturn; ICharInfo::Info toReturn;
toReturn.id = stmt.getInt64Field("id"); toReturn.id = stmt.getIntField("id");
toReturn.name = stmt.getStringField("name"); toReturn.name = stmt.getStringField("name");
toReturn.pendingName = stmt.getStringField("pending_name"); toReturn.pendingName = stmt.getStringField("pending_name");
toReturn.needsRename = stmt.getIntField("needs_rename"); toReturn.needsRename = stmt.getIntField("needs_rename");
@@ -31,7 +31,7 @@ std::optional<ICharInfo::Info> CharInfoFromQueryResult(CppSQLite3Query stmt) {
return toReturn; return toReturn;
} }
std::optional<ICharInfo::Info> SQLiteDatabase::GetCharacterInfo(const LWOOBJID charId) { std::optional<ICharInfo::Info> SQLiteDatabase::GetCharacterInfo(const uint32_t charId) {
return CharInfoFromQueryResult( return CharInfoFromQueryResult(
ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId).second ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId).second
); );
@@ -43,12 +43,12 @@ std::optional<ICharInfo::Info> SQLiteDatabase::GetCharacterInfo(const std::strin
); );
} }
std::vector<LWOOBJID> SQLiteDatabase::GetAccountCharacterIds(const LWOOBJID accountId) { std::vector<uint32_t> SQLiteDatabase::GetAccountCharacterIds(const uint32_t accountId) {
auto [_, result] = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId); auto [_, result] = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId);
std::vector<LWOOBJID> toReturn; std::vector<uint32_t> toReturn;
while (!result.eof()) { while (!result.eof()) {
toReturn.push_back(result.getInt64Field("id")); toReturn.push_back(result.getIntField("id"));
result.nextRow(); result.nextRow();
} }
@@ -66,15 +66,15 @@ void SQLiteDatabase::InsertNewCharacter(const ICharInfo::Info info) {
static_cast<uint32_t>(time(NULL))); static_cast<uint32_t>(time(NULL)));
} }
void SQLiteDatabase::SetCharacterName(const LWOOBJID characterId, const std::string_view name) { void SQLiteDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId);
} }
void SQLiteDatabase::SetPendingCharacterName(const LWOOBJID characterId, const std::string_view name) { void SQLiteDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId);
} }
void SQLiteDatabase::UpdateLastLoggedInCharacter(const LWOOBJID characterId) { void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast<uint32_t>(time(NULL)), characterId); ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast<uint32_t>(time(NULL)), characterId);
} }

View File

@@ -1,6 +1,6 @@
#include "SQLiteDatabase.h" #include "SQLiteDatabase.h"
std::string SQLiteDatabase::GetCharacterXml(const LWOOBJID charId) { std::string SQLiteDatabase::GetCharacterXml(const uint32_t charId) {
auto [_, result] = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId); auto [_, result] = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId);
if (result.eof()) { if (result.eof()) {
@@ -10,10 +10,10 @@ std::string SQLiteDatabase::GetCharacterXml(const LWOOBJID charId) {
return result.getStringField("xml_data"); return result.getStringField("xml_data");
} }
void SQLiteDatabase::UpdateCharacterXml(const LWOOBJID charId, const std::string_view lxfml) { void SQLiteDatabase::UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) {
ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId); ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId);
} }
void SQLiteDatabase::InsertCharacterXml(const LWOOBJID characterId, const std::string_view lxfml) { void SQLiteDatabase::InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) {
ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml); ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml);
} }

View File

@@ -1,5 +1,5 @@
#include "SQLiteDatabase.h" #include "SQLiteDatabase.h"
void SQLiteDatabase::InsertSlashCommandUsage(const LWOOBJID characterId, const std::string_view command) { void SQLiteDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) {
ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command); ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command);
} }

Some files were not shown because too many files have changed in this diff Show More