mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-17 20:14:31 -06:00
Compare commits
5 Commits
fixDestCom
...
mysql-repl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f4ebd7587 | ||
|
|
44a5d83b1d | ||
|
|
d61d3b0ce2 | ||
|
|
6fb0677bd9 | ||
|
|
8edfdd48a1 |
@@ -1,264 +0,0 @@
|
||||
components = {
|
||||
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS': 1,
|
||||
'COMPONENT_TYPE_RENDER': 2,
|
||||
'COMPONENT_TYPE_SIMPLE_PHYSICS': 3,
|
||||
'COMPONENT_TYPE_CHARACTER': 4,
|
||||
'COMPONENT_TYPE_SCRIPT': 5,
|
||||
'COMPONENT_TYPE_BOUNCER': 6,
|
||||
'COMPONENT_TYPE_DESTROYABLE': 7,
|
||||
'COMPONENT_TYPE_GHOST': 8,
|
||||
'COMPONENT_TYPE_SKILL': 9,
|
||||
'COMPONENT_TYPE_SPAWNER': 10,
|
||||
'COMPONENT_TYPE_ITEM': 11,
|
||||
'COMPONENT_TYPE_MODULAR_BUILD': 12,
|
||||
'COMPONENT_TYPE_BUILD_CONTROLLER': 13,
|
||||
'COMPONENT_TYPE_REBUILD_ACTIVATOR': 14,
|
||||
'COMPONENT_TYPE_ICON_ONLY': 15,
|
||||
'COMPONENT_TYPE_VENDOR': 16,
|
||||
'COMPONENT_TYPE_INVENTORY': 17,
|
||||
'COMPONENT_TYPE_PROJECTILE_PHYSICS': 18,
|
||||
'COMPONENT_TYPE_SHOOTING_GALLERY': 19,
|
||||
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS': 20,
|
||||
'COMPONENT_TYPE_DROP_EFFECT': 21,
|
||||
'COMPONENT_TYPE_CHEST': 22,
|
||||
'COMPONENT_TYPE_COLLECTIBLE': 23,
|
||||
'COMPONENT_TYPE_BLUEPRINT': 24,
|
||||
'COMPONENT_TYPE_MOVING_PLATFORM': 25,
|
||||
'COMPONENT_TYPE_PET': 26,
|
||||
'COMPONENT_TYPE_PLATFORM_BOUNDARY': 27,
|
||||
'COMPONENT_TYPE_MODULE': 28,
|
||||
'COMPONENT_TYPE_ARCADE': 29,
|
||||
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS': 30,
|
||||
'COMPONENT_TYPE_MOVEMENT_AI': 31,
|
||||
'COMPONENT_TYPE_EXHIBIT': 32,
|
||||
'COMPONENT_TYPE_OVERHEAD_ICON': 33,
|
||||
'COMPONENT_TYPE_PET_CONTROL': 34,
|
||||
'COMPONENT_TYPE_MINIFIG': 35,
|
||||
'COMPONENT_TYPE_PROPERTY': 36,
|
||||
'COMPONENT_TYPE_PET_CREATOR': 37,
|
||||
'COMPONENT_TYPE_MODEL_BUILDER': 38,
|
||||
'COMPONENT_TYPE_SCRIPTED_ACTIVITY': 39,
|
||||
'COMPONENT_TYPE_PHANTOM_PHYSICS': 40,
|
||||
'COMPONENT_TYPE_SPRING_PAD': 41,
|
||||
'COMPONENT_TYPE_MODEL': 42,
|
||||
'COMPONENT_TYPE_PROPERTY_ENTRANCE': 43,
|
||||
'COMPONENT_TYPE_FX': 44,
|
||||
'COMPONENT_TYPE_PROPERTY_MANAGEMENT': 45,
|
||||
'COMPONENT_TYPE_VEHICLE_PHYSICS': 46,
|
||||
'COMPONENT_TYPE_PHYSICS_SYSTEM': 47,
|
||||
'COMPONENT_TYPE_REBUILD': 48,
|
||||
'COMPONENT_TYPE_SWITCH': 49,
|
||||
'COMPONENT_TYPE_ZONE_CONTROL': 50,
|
||||
'COMPONENT_TYPE_CHANGELING': 51,
|
||||
'COMPONENT_TYPE_CHOICE_BUILD': 52,
|
||||
'COMPONENT_TYPE_PACKAGE': 53,
|
||||
'COMPONENT_TYPE_SOUND_REPEATER': 54,
|
||||
'COMPONENT_TYPE_SOUND_AMBIENT_2D': 55,
|
||||
'COMPONENT_TYPE_SOUND_AMBIENT_3D': 56,
|
||||
'COMPONENT_TYPE_PRECONDITION': 57,
|
||||
'COMPONENT_TYPE_PLAYER_FLAG': 58,
|
||||
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY': 59,
|
||||
'COMPONENT_TYPE_BASE_COMBAT_AI': 60,
|
||||
'COMPONENT_TYPE_MODULE_ASSEMBLY': 61,
|
||||
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER': 62,
|
||||
'COMPONENT_TYPE_RACING_MODULE': 63,
|
||||
'COMPONENT_TYPE_GENERIC_ACTIVATOR': 64,
|
||||
'COMPONENT_TYPE_PROPERTY_VENDOR': 65,
|
||||
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET': 66,
|
||||
'COMPONENT_TYPE_ROCKET_LAUNCH': 67,
|
||||
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT': 68,
|
||||
'COMPONENT_TYPE_TRIGGER': 69,
|
||||
'COMPONENT_TYPE_DROPPED_LOOT': 70,
|
||||
'COMPONENT_TYPE_RACING_CONTROL': 71,
|
||||
'COMPONENT_TYPE_FACTION_TRIGGER': 72,
|
||||
'COMPONENT_TYPE_MISSION_OFFER': 73,
|
||||
'COMPONENT_TYPE_RACING_STATS': 74,
|
||||
'COMPONENT_TYPE_LUP_EXHIBIT': 75,
|
||||
'COMPONENT_TYPE_BBB': 76,
|
||||
'COMPONENT_TYPE_SOUND_TRIGGER': 77,
|
||||
'COMPONENT_TYPE_PROXIMITY_MONITOR': 78,
|
||||
'COMPONENT_TYPE_RACING_SOUND_TRIGGER': 79,
|
||||
'COMPONENT_TYPE_CHAT': 80,
|
||||
'COMPONENT_TYPE_FRIENDS_LIST': 81,
|
||||
'COMPONENT_TYPE_GUILD': 82,
|
||||
'COMPONENT_TYPE_LOCAL_SYSTEM': 83,
|
||||
'COMPONENT_TYPE_MISSION': 84,
|
||||
'COMPONENT_TYPE_MUTUABLE_MODEL_BEHAVIORS': 85,
|
||||
'COMPONENT_TYPE_PATHFINDING_CONTROL': 86,
|
||||
'COMPONENT_TYPE_PLAYER_PET_TAMING': 87,
|
||||
'COMPONENT_TYPE_PROPERTY_EDITOR': 88,
|
||||
'COMPONENT_TYPE_SKINNED_RENDER': 89,
|
||||
'COMPONENT_TYPE_SLASH_COMMAND': 90,
|
||||
'COMPONENT_TYPE_STATUS_EFFECT': 91,
|
||||
'COMPONENT_TYPE_TEAMS': 92,
|
||||
'COMPONENT_TYPE_TEXT_EFFECT': 93,
|
||||
'COMPONENT_TYPE_TRADE': 94,
|
||||
'COMPONENT_TYPE_USER_CONTROL': 95,
|
||||
'COMPONENT_TYPE_IGNORE_LIST': 96,
|
||||
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP': 97,
|
||||
'COMPONENT_TYPE_BUFF': 98,
|
||||
'COMPONENT_TYPE_INTERACTION_MANAGER': 99,
|
||||
'COMPONENT_TYPE_DONATION_VENDOR': 100,
|
||||
'COMPONENT_TYPE_COMBAT_MEDIATOR': 101,
|
||||
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR': 102,
|
||||
'COMPONENT_TYPE_GATE_RUSH': 103,
|
||||
'COMPONENT_TYPE_RAIL_ACTIVATOR': 104,
|
||||
'COMPONENT_TYPE_ROLLER': 105,
|
||||
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT': 106,
|
||||
'COMPONENT_TYPE_CRAFTING': 107,
|
||||
'COMPONENT_TYPE_POSSESSABLE': 108,
|
||||
'COMPONENT_TYPE_LEVEL_PROGRESSION': 109,
|
||||
'COMPONENT_TYPE_POSSESSOR': 110,
|
||||
'COMPONENT_TYPE_MOUNT_CONTROL': 111,
|
||||
'UNKNOWN_112': 112,
|
||||
'COMPONENT_TYPE_PROPERTY_PLAQUE': 113,
|
||||
'COMPONENT_TYPE_BUILD_BORDER': 114,
|
||||
'UNKNOWN_115': 115,
|
||||
'COMPONENT_TYPE_CULLING_PLANE': 116,
|
||||
'INVALID': 4294967295
|
||||
}
|
||||
|
||||
component_initialization_order = [
|
||||
'COMPONENT_TYPE_POSSESSABLE',
|
||||
'COMPONENT_TYPE_BLUEPRINT',
|
||||
'COMPONENT_TYPE_MODULE_ASSEMBLY',
|
||||
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS',
|
||||
'COMPONENT_TYPE_PROJECTILE_PHYSICS',
|
||||
'COMPONENT_TYPE_PHYSICS_SYSTEM',
|
||||
'COMPONENT_TYPE_VEHICLE_PHYSICS',
|
||||
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS',
|
||||
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS',
|
||||
'COMPONENT_TYPE_SIMPLE_PHYSICS',
|
||||
'COMPONENT_TYPE_PHANTOM_PHYSICS',
|
||||
'COMPONENT_TYPE_DESTROYABLE',
|
||||
'COMPONENT_TYPE_MINIFIG',
|
||||
'COMPONENT_TYPE_CHARACTER',
|
||||
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT',
|
||||
'COMPONENT_TYPE_POSSESSOR',
|
||||
'COMPONENT_TYPE_MOUNT_CONTROL',
|
||||
'COMPONENT_TYPE_PET',
|
||||
'COMPONENT_TYPE_INVENTORY',
|
||||
'COMPONENT_TYPE_PROXIMITY_MONITOR',
|
||||
'COMPONENT_TYPE_MOVEMENT_AI',
|
||||
'COMPONENT_TYPE_SCRIPT',
|
||||
'COMPONENT_TYPE_SKILL',
|
||||
'COMPONENT_TYPE_BASE_COMBAT_AI',
|
||||
'COMPONENT_TYPE_SPAWNER',
|
||||
'COMPONENT_TYPE_ITEM',
|
||||
'COMPONENT_TYPE_REBUILD',
|
||||
'COMPONENT_TYPE_BUILD_BORDER',
|
||||
'COMPONENT_TYPE_MODULE',
|
||||
'COMPONENT_TYPE_BUILD_CONTROLLER',
|
||||
'COMPONENT_TYPE_REBUILD_ACTIVATOR',
|
||||
'COMPONENT_TYPE_MODULAR_BUILD',
|
||||
'COMPONENT_TYPE_MISSION_OFFER',
|
||||
'COMPONENT_TYPE_VENDOR',
|
||||
'COMPONENT_TYPE_DONATION_VENDOR',
|
||||
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR',
|
||||
'COMPONENT_TYPE_SHOOTING_GALLERY',
|
||||
'COMPONENT_TYPE_ROLLER',
|
||||
'COMPONENT_TYPE_SCRIPTED_ACTIVITY',
|
||||
'COMPONENT_TYPE_COLLECTIBLE',
|
||||
'COMPONENT_TYPE_MOVING_PLATFORM',
|
||||
'COMPONENT_TYPE_CHANGELING',
|
||||
'COMPONENT_TYPE_CHOICE_BUILD',
|
||||
'COMPONENT_TYPE_PACKAGE',
|
||||
'COMPONENT_TYPE_PLATFORM_BOUNDARY',
|
||||
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET',
|
||||
'COMPONENT_TYPE_CULLING_PLANE',
|
||||
'COMPONENT_TYPE_EXHIBIT',
|
||||
'COMPONENT_TYPE_LUP_EXHIBIT',
|
||||
'COMPONENT_TYPE_PROPERTY',
|
||||
'COMPONENT_TYPE_PROPERTY_PLAQUE',
|
||||
'COMPONENT_TYPE_MODEL',
|
||||
'COMPONENT_TYPE_PROPERTY_ENTRANCE',
|
||||
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP',
|
||||
'COMPONENT_TYPE_PROPERTY_MANAGEMENT',
|
||||
'COMPONENT_TYPE_RAIL_ACTIVATOR',
|
||||
'COMPONENT_TYPE_BOUNCER',
|
||||
'COMPONENT_TYPE_SPRING_PAD',
|
||||
'COMPONENT_TYPE_SWITCH',
|
||||
'COMPONENT_TYPE_ZONE_CONTROL',
|
||||
'COMPONENT_TYPE_MODEL_BUILDER',
|
||||
'COMPONENT_TYPE_SOUND_REPEATER',
|
||||
'COMPONENT_TYPE_CHEST',
|
||||
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER',
|
||||
'COMPONENT_TYPE_ARCADE',
|
||||
'COMPONENT_TYPE_RENDER',
|
||||
'COMPONENT_TYPE_SOUND_AMBIENT_2D',
|
||||
'COMPONENT_TYPE_SOUND_AMBIENT_3D',
|
||||
'COMPONENT_TYPE_SOUND_TRIGGER',
|
||||
'COMPONENT_TYPE_RACING_SOUND_TRIGGER',
|
||||
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY',
|
||||
'COMPONENT_TYPE_GENERIC_ACTIVATOR',
|
||||
'COMPONENT_TYPE_PROPERTY_VENDOR',
|
||||
'COMPONENT_TYPE_ROCKET_LAUNCH',
|
||||
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT',
|
||||
'COMPONENT_TYPE_FACTION_TRIGGER',
|
||||
'COMPONENT_TYPE_RACING_CONTROL',
|
||||
'COMPONENT_TYPE_GATE_RUSH',
|
||||
'COMPONENT_TYPE_BBB',
|
||||
'COMPONENT_TYPE_CRAFTING'
|
||||
]
|
||||
component_order_as_ints = {}
|
||||
|
||||
component_int_to_name = {}
|
||||
|
||||
for component in component_initialization_order:
|
||||
component_order_as_ints[components[component]] = component
|
||||
|
||||
for component in components:
|
||||
component_int_to_name[components[component]] = component
|
||||
|
||||
class GameObject:
|
||||
type = ""
|
||||
|
||||
class ObjectLoadState:
|
||||
createdGameObject = GameObject()
|
||||
components = []
|
||||
hasPhysicsComponent = False
|
||||
hasDestroyable = False
|
||||
hasPathfindingComponent = False
|
||||
hasProximityMonitorComponent = False
|
||||
hasScriptComponent = False
|
||||
hasDroppedLootComponent = False
|
||||
hasModelbehaviors = False
|
||||
somethingOrNothing = False
|
||||
physicsComponentId = -1
|
||||
|
||||
# Begin custom attributes
|
||||
serializedOrder = []
|
||||
hasMissionOffer = False
|
||||
|
||||
class NiPoint3:
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
z = 0.0
|
||||
|
||||
class NiQuaternion:
|
||||
w = 0.0
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
z = 0.0
|
||||
|
||||
class LoadObject:
|
||||
objId = 0
|
||||
owner = 0
|
||||
lotToSpawn = 0
|
||||
isLocalPlayer = False
|
||||
hasDropEffect = False
|
||||
bIsSmashable = False
|
||||
dynamicPhysics = False
|
||||
spawnPosition = NiPoint3()
|
||||
spawnRotation = NiQuaternion()
|
||||
scale = 0.0
|
||||
legoBrickID = 0
|
||||
legoColorID = 0
|
||||
configData = {}
|
||||
isHFPaletteObject = False
|
||||
objType = 0
|
||||
useSimplePhysics = False
|
||||
bDelayedLoad = False
|
||||
|
||||
# Don't know the name yet of this
|
||||
field8_0x20 = True
|
||||
@@ -1,408 +0,0 @@
|
||||
import ComponentDefinitions as cd
|
||||
|
||||
def ApplyComponentWhitelist():
|
||||
# Dont care to actually do this, just want the call for now.
|
||||
pass
|
||||
|
||||
def ControllablePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasModelbehaviors:
|
||||
if not loadObject.hasDropEffect:
|
||||
if not loadObject.useSimplePhysics:
|
||||
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
|
||||
else:
|
||||
objectLoadState.components.append('SIMPLE_PHYSICS')
|
||||
else:
|
||||
loadObject.physicsComponentId = componentId
|
||||
|
||||
def RenderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if loadObject.lotToSpawn == 176:
|
||||
return
|
||||
gmlevel = loadObject.configData.get('gmlevel', 0)
|
||||
if gmlevel > 0 and gmlevel < 6:
|
||||
componentId = 9940 # Override the componentId of the render component to be 9940, the Mythran Render Component
|
||||
renderDisabled = loadObject.configData.get('renderDisabled', False)
|
||||
if renderDisabled:
|
||||
return
|
||||
if objectLoadState.createdGameObject.type == 'BlockingVolume':
|
||||
return
|
||||
rejectedLots = [5937, 5938, 9741, 9742, 9862, 9863]
|
||||
if loadObject.lotToSpawn in rejectedLots:
|
||||
return
|
||||
if loadObject.lotToSpawn == 6368:
|
||||
FxFactory(loadObject, objectLoadState, componentId)
|
||||
return
|
||||
FxFactory(loadObject, objectLoadState, componentId)
|
||||
defaultWrapperAsset = ""
|
||||
hasNifAsset = False
|
||||
hasKfmAsset = False
|
||||
allowRenderWrapping = False
|
||||
nifName = loadObject.configData.get('nif_name', '')
|
||||
if nifName == '':
|
||||
print('TODO query cdc for this data in the Render component')
|
||||
nifName = 'mesh/something.nif' # if empty get nifname as the render_asset column of the RenderComponent table from the database
|
||||
if nifName[-1] == 'f':
|
||||
hasNifAsset = True
|
||||
elif nifName[-1] == 'm':
|
||||
hasKfmAsset = True
|
||||
if defaultWrapperAsset != '':
|
||||
if defaultWrapperAsset[-1] == 'm':
|
||||
allowRenderWrapping = not loadObject.configData.get('preventRenderWrapping', False)
|
||||
if allowRenderWrapping or hasKfmAsset or (not defaultWrapperAsset == 'PrimitiveModels' and not hasNifAsset and not loadObject.configData.get('CreateNULLRender', False)):
|
||||
objectLoadState.components.append('SKINNED_RENDER')
|
||||
else:
|
||||
objectLoadState.components.append('RENDER')
|
||||
|
||||
def FxFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('FX')
|
||||
|
||||
def SimplePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasModelbehaviors:
|
||||
objectLoadState.physicsComponentId = componentId
|
||||
return
|
||||
if not loadObject.hasDropEffect:
|
||||
return
|
||||
markedAsPhantom = loadObject.configData.get('markedAsPhantom', False)
|
||||
if markedAsPhantom:
|
||||
objectLoadState.components.append('PHANTOM_PHYSICS')
|
||||
else:
|
||||
objectLoadState.components.append('SIMPLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def CharacterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('POSSESSOR')
|
||||
objectLoadState.components.append('MOUNT_CONTROL')
|
||||
objectLoadState.components.append('PET_CREATOR')
|
||||
objectLoadState.components.append('LEVEL_PROGRESSION')
|
||||
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
|
||||
objectLoadState.components.append('CHARACTER')
|
||||
|
||||
def ScriptFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
|
||||
custom_script_client = loadObject.configData.get('custom_script_client', '')
|
||||
custom_script_server = loadObject.configData.get('custom_script_server', '')
|
||||
if componentId == 0 and custom_script_client == '' and custom_script_server == '':
|
||||
return
|
||||
objectLoadState.components.append('SCRIPT')
|
||||
|
||||
if not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
|
||||
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
|
||||
objectLoadState.components.append('PATHFINDING_CONTROL')
|
||||
objectLoadState.hasPathfindingComponent = True
|
||||
|
||||
def BouncerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('BOUNCER')
|
||||
|
||||
def DestroyableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('STATUS_EFFECT')
|
||||
objectLoadState.components.append('BUFF')
|
||||
objectLoadState.components.append('DESTROYABLE')
|
||||
objectLoadState.hasDestroyable = True
|
||||
|
||||
def SkillFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SKILL')
|
||||
|
||||
def SpawnerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SPAWNER')
|
||||
# If game state is happy flower
|
||||
spawntemplate = loadObject.configData.get('spawntemplate', '')
|
||||
|
||||
print('TODO query cdc for this data in the Spawner component')
|
||||
render_asset = loadObject.configData.get('render_asset', 'test.nif') # Get this from RenderComponent table
|
||||
render_id = loadObject.configData.get('render_id', '') # Get this from RenderComponent table
|
||||
|
||||
if render_asset[-1] == 'f':
|
||||
objectLoadState.components.append('SKINNED_RENDER')
|
||||
else:
|
||||
objectLoadState.components.append('RENDER')
|
||||
|
||||
def ItemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ITEM')
|
||||
|
||||
def ModularBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MODULAR_BUILD')
|
||||
|
||||
def BuildControllerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('BUILD_CONTROLLER')
|
||||
|
||||
def VendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
objectLoadState.components.append('VENDOR')
|
||||
|
||||
def InventoryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('INVENTORY')
|
||||
|
||||
def ProjectilePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not loadObject.hasDropEffect:
|
||||
objectLoadState.components.append('PROJECTILE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def ShootingGalleryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SHOOTING_GALLERY')
|
||||
|
||||
def RigibBodyPhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('RIGID_BODY_PHANTOM_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def ChestFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('CHEST')
|
||||
|
||||
def CollectibleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('COLLECTIBLE')
|
||||
if not objectLoadState.hasDestroyable:
|
||||
objectLoadState.components.append('DESTROYABLE')
|
||||
objectLoadState.hasDestroyable = True
|
||||
|
||||
def BlueprintFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('BLUEPRINT')
|
||||
|
||||
def MovingPlatformFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MOVING_PLATFORM')
|
||||
|
||||
def PetFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
inInventory = loadObject.configData.get('inInventory', False)
|
||||
if inInventory:
|
||||
return
|
||||
|
||||
if not objectLoadState.hasPathfindingComponent:
|
||||
objectLoadState.components.append('PATHFINDING_CONTROL')
|
||||
objectLoadState.hasPathfindingComponent = True
|
||||
objectLoadState.components.append('PET')
|
||||
|
||||
def PlatformBoundaryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PLATFORM_BOUNDARY')
|
||||
|
||||
def ModuleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MODULE')
|
||||
|
||||
def ArcadeFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ARCADE')
|
||||
|
||||
def HavokVehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
use_simple_physics = loadObject.configData.get('use_simple_physics', False)
|
||||
if use_simple_physics:
|
||||
objectLoadState.components.append('SIMPLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
else:
|
||||
objectWorldState = loadObject.configData.get('objectWorldState', '')
|
||||
if objectWorldState == 0 or objectWorldState != 2:
|
||||
objectLoadState.components.append('HAVOK_VEHICLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def MovementAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MOVEMENT_AI')
|
||||
|
||||
def ExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
# Sorry aron, doesnt do anything :(
|
||||
pass
|
||||
|
||||
def MinifigFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MINIFIG')
|
||||
|
||||
def PropertyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PROPERTY')
|
||||
|
||||
def ModelBuilderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MODEL_BUILDER')
|
||||
|
||||
def ScriptedActivityFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SCRIPTED_ACTIVITY')
|
||||
|
||||
def PhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not loadObject.hasDropEffect:
|
||||
objectLoadState.components.append('PHANTOM_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def SpringPadFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SPRING_PAD')
|
||||
|
||||
def ModelFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
modelType = loadObject.configData.get('modelType', 0)
|
||||
if not objectLoadState.hasModelbehaviors:
|
||||
if not objectLoadState.hasPhysicsComponent:
|
||||
objectLoadState.components.append('SIMPLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
elif not objectLoadState.hasPhysicsComponent:
|
||||
if modelType == 0:
|
||||
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
|
||||
else:
|
||||
objectLoadState.components.append('SIMPLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
if modelType != 0 and not objectLoadState.hasPathfindingComponent:
|
||||
objectLoadState.components.append('PATHFINDING_CONTROL')
|
||||
objectLoadState.hasPathfindingComponent = True
|
||||
|
||||
hasPropertyObjectID = loadObject.configData.get('propertyObjectID', 0) != 0
|
||||
inInventory = loadObject.configData.get('inInventory', False)
|
||||
if hasPropertyObjectID and inInventory:
|
||||
objectLoadState.components.append('MODEL_BEHAVIORS')
|
||||
else:
|
||||
objectLoadState.components.append('MUTABLE_MODEL_BEHAVIORS')
|
||||
|
||||
def PropertyEntranceFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PROPERTY_ENTRANCE')
|
||||
|
||||
def PropertyManagementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PROPERTY_MANAGEMENT')
|
||||
|
||||
def VehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('VEHICLE_PHYSICS')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def PhysicsSystemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasModelbehaviors:
|
||||
if not loadObject.hasDropEffect:
|
||||
objectLoadState.components.append('PHYSICS_SYSTEM')
|
||||
objectLoadState.hasPhysicsComponent = True
|
||||
|
||||
def RebuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('REBUILD')
|
||||
if not objectLoadState.hasDestroyable:
|
||||
objectLoadState.components.append('DESTROYABLE')
|
||||
objectLoadState.hasDestroyable = True
|
||||
loadObject.bIsSmashable = True
|
||||
|
||||
def BaseCombatAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
|
||||
if not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
|
||||
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
|
||||
objectLoadState.components.append('PATHFINDING_CONTROL')
|
||||
objectLoadState.hasPathfindingComponent = True
|
||||
|
||||
objectLoadState.components.append('BASE_COMBAT_AI')
|
||||
|
||||
def ProximityMonitorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
print('TODO query cdc for this data in prox mon')
|
||||
# There must be a row for this component in the ProximityMonitorComponent table to get this component
|
||||
LoadOnClient = loadObject.configData.get('LoadOnClient', False) # Get this from ProximityMonitorComponent table if you know there is a column for it
|
||||
if LoadOnClient:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
|
||||
def DonationVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
objectLoadState.components.append('DONATION_VENDOR')
|
||||
|
||||
def AchievmentVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
|
||||
objectLoadState.components.append('ACHIEVEMENT_VENDOR')
|
||||
|
||||
def CraftingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
# Game verson must be above 999 for this to work!
|
||||
gameVersion = 1000
|
||||
if gameVersion > 999:
|
||||
objectLoadState.components.append('CRAFTING')
|
||||
|
||||
# Technically Possessable and Possessor are the exact same thing
|
||||
def PossessableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('POSSESSABLE')
|
||||
|
||||
def PossessorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('POSSESSOR')
|
||||
|
||||
def PropertyPlaqueFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PROPERTY_PLAQUE')
|
||||
|
||||
def BuildBorderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('BUILD_BORDER')
|
||||
|
||||
def CullingPlaneFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('CULLING_PLANE')
|
||||
|
||||
def GateRushFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('GATE_RUSH')
|
||||
|
||||
def RailActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('RAIL_ACTIVATOR')
|
||||
|
||||
def RollerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ROLLER')
|
||||
|
||||
def PlayerForcedMovementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
|
||||
|
||||
def RacingSoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('RACING_SOUND_TRIGGER')
|
||||
|
||||
def RocketLaunchLupFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ROCKET_LAUNCH_LUP')
|
||||
|
||||
|
||||
def ModuleAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MODULE_ASSEMBLY')
|
||||
|
||||
def ShowcaseModelHandlerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SHOWCASE_MODEL_HANDLER')
|
||||
|
||||
def GenericActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('GENERIC_ACTIVATOR')
|
||||
|
||||
def PropertyVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('PROPERTY_VENDOR')
|
||||
|
||||
def RocketLaunchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ROCKET_LAUNCH')
|
||||
|
||||
def RocketLandingComponentFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ROCKET_LANDING_COMPONENT')
|
||||
|
||||
def RacingControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('RACING_CONTROL')
|
||||
|
||||
def FactionTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('FACTION_TRIGGER')
|
||||
|
||||
def MissionOfferFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('MISSION_OFFER')
|
||||
objectLoadState.hasMissionOffer = True
|
||||
|
||||
def RacingStatsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('RACING_STATS')
|
||||
|
||||
def LupExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('LUP_EXHIBIT')
|
||||
|
||||
def BBBFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('BBB')
|
||||
|
||||
def SoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SOUND_TRIGGER')
|
||||
|
||||
def SwitchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SWITCH')
|
||||
|
||||
def ZoneControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('ZONE_CONTROL')
|
||||
|
||||
def ChanglingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('CHANGELING')
|
||||
|
||||
def ChoiceBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('CHOICE_BUILD')
|
||||
|
||||
def PackageFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
# Literally returns nothing
|
||||
pass
|
||||
|
||||
def SoundRepeaterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SOUND_REPEATER')
|
||||
|
||||
def SoundAmbient2DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
if loadObject.isLocalPlayer:
|
||||
objectLoadState.components.append('SOUND_AMBIENT_2D')
|
||||
|
||||
def SoundAmbient3DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('SOUND_AMBIENT_3D')
|
||||
|
||||
def CustomBuildAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
|
||||
objectLoadState.components.append('CUSTOM_BUILD_ASSEMBLY')
|
||||
@@ -1,306 +0,0 @@
|
||||
import ComponentDefinitions as cd
|
||||
import ComponentFactories as cf
|
||||
|
||||
# If we have any of these components, we need pathfinding control
|
||||
def CheckIfNeedsPathfindingControl(components_to_load: dict):
|
||||
add_pathfinding_control = cd.components['COMPONENT_TYPE_CONTROLLABLE_PHYSICS'] in components_to_load
|
||||
add_pathfinding_control |= cd.components['COMPONENT_TYPE_PROJECTILE_PHYSICS'] in components_to_load
|
||||
add_pathfinding_control |= cd.components['COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS'] in components_to_load
|
||||
add_pathfinding_control |= cd.components['COMPONENT_TYPE_VEHICLE_PHYSICS'] in components_to_load
|
||||
return add_pathfinding_control
|
||||
|
||||
def DoObjectPreLoad(components_to_load: dict):
|
||||
isHfActive = False
|
||||
cf.ApplyComponentWhitelist()
|
||||
objectLoadState = cd.ObjectLoadState()
|
||||
loadObject = cd.LoadObject()
|
||||
|
||||
for componentType in cd.component_order_as_ints.keys():
|
||||
try:
|
||||
componentId = components_to_load[componentType]
|
||||
except KeyError:
|
||||
continue
|
||||
try:
|
||||
match cd.component_int_to_name[componentType]:
|
||||
case 'COMPONENT_TYPE_CONTROLLABLE_PHYSICS':
|
||||
if objectLoadState.hasDroppedLootComponent: continue
|
||||
cf.ControllablePhysFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RENDER':
|
||||
cf.RenderFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SIMPLE_PHYSICS':
|
||||
if objectLoadState.hasDroppedLootComponent: continue
|
||||
cf.SimplePhysFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CHARACTER':
|
||||
cf.CharacterFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SCRIPT':
|
||||
cf.ScriptFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
|
||||
|
||||
case 'COMPONENT_TYPE_BOUNCER':
|
||||
cf.BouncerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_DESTROYABLE':
|
||||
cf.DestroyableFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SKILL':
|
||||
cf.SkillFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SPAWNER':
|
||||
cf.SpawnerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ITEM':
|
||||
cf.ItemFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MODULAR_BUILD':
|
||||
cf.ModularBuildFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_BUILD_CONTROLLER':
|
||||
cf.BuildControllerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_VENDOR':
|
||||
cf.VendorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_INVENTORY':
|
||||
cf.InventoryFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROJECTILE_PHYSICS':
|
||||
if objectLoadState.hasDroppedLootComponent: continue
|
||||
cf.ProjectilePhysicsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SHOOTING_GALLERY':
|
||||
cf.ShootingGalleryFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS':
|
||||
cf.RigibBodyPhantomPhysicsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CHEST':
|
||||
cf.ChestFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_COLLECTIBLE':
|
||||
cf.CollectibleFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_BLUEPRINT':
|
||||
cf.BlueprintFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MOVING_PLATFORM':
|
||||
cf.MovingPlatformFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PET':
|
||||
cf.PetFactory(loadObject, objectLoadState, componentId)
|
||||
print('Deleting COMPONENT_TYPE_MODEL and COMPONENT_TYPE_ITEM due to COMPONENT_TYPE_PET being loaded')
|
||||
toDel = cd.components['COMPONENT_TYPE_MODEL']
|
||||
del components_to_load[toDel]
|
||||
toDel = cd.components['COMPONENT_TYPE_ITEM']
|
||||
del components_to_load[toDel]
|
||||
|
||||
case 'COMPONENT_TYPE_PLATFORM_BOUNDARY':
|
||||
cf.PlatformBoundaryFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MODULE':
|
||||
cf.ModuleFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ARCADE':
|
||||
cf.ArcadeFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS':
|
||||
cf.HavokVehiclePhysicsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MOVEMENT_AI':
|
||||
cf.MovementAiFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_EXHIBIT':
|
||||
cf.ExhibitFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MINIFIG':
|
||||
cf.MinifigFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROPERTY':
|
||||
cf.PropertyFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MODEL_BUILDER':
|
||||
cf.ModelBuilderFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SCRIPTED_ACTIVITY':
|
||||
cf.ScriptedActivityFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PHANTOM_PHYSICS':
|
||||
if objectLoadState.hasDroppedLootComponent: continue
|
||||
cf.PhantomPhysicsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SPRING_PAD':
|
||||
cf.SpringPadFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MODEL':
|
||||
cf.ModelFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROPERTY_ENTRANCE':
|
||||
cf.PropertyEntranceFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROPERTY_MANAGEMENT':
|
||||
cf.PropertyManagementFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_VEHICLE_PHYSICS':
|
||||
cf.VehiclePhysicsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PHYSICS_SYSTEM':
|
||||
cf.PhysicsSystemFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_REBUILD':
|
||||
cf.RebuildFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SWITCH':
|
||||
cf.SwitchFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ZONE_CONTROL':
|
||||
cf.ZoneControlFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CHANGELING':
|
||||
cf.ChanglingFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CHOICE_BUILD':
|
||||
cf.ChoiceBuildFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PACKAGE':
|
||||
cf.PackageFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SOUND_REPEATER':
|
||||
cf.SoundRepeaterFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SOUND_AMBIENT_2D':
|
||||
cf.SoundAmbient2DFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SOUND_AMBIENT_3D':
|
||||
cf.SoundAmbient3DFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY':
|
||||
cf.CustomBuildAssemblyFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_BASE_COMBAT_AI':
|
||||
cf.BaseCombatAiFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
|
||||
|
||||
case 'COMPONENT_TYPE_MODULE_ASSEMBLY':
|
||||
cf.ModuleAssemblyFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER':
|
||||
cf.ShowcaseModelHandlerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_GENERIC_ACTIVATOR':
|
||||
cf.GenericActivatorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROPERTY_VENDOR':
|
||||
cf.PropertyVendorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ROCKET_LAUNCH':
|
||||
cf.RocketLaunchFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT':
|
||||
cf.RocketLandingComponentFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RACING_CONTROL':
|
||||
cf.RacingControlFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_FACTION_TRIGGER':
|
||||
cf.FactionTriggerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_MISSION_OFFER':
|
||||
cf.MissionOfferFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RACING_STATS':
|
||||
cf.RacingStatsFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_LUP_EXHIBIT':
|
||||
cf.LupExhibitFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_BBB':
|
||||
cf.BBBFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_SOUND_TRIGGER':
|
||||
cf.SoundTriggerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROXIMITY_MONITOR':
|
||||
cf.ProximityMonitorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RACING_SOUND_TRIGGER':
|
||||
cf.RacingSoundTriggerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ROCKET_LAUNCH_LUP':
|
||||
cf.RocketLaunchLupFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_DONATION_VENDOR':
|
||||
cf.DonationVendorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ACHIEVEMENT_VENDOR':
|
||||
cf.AchievmentVendorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_GATE_RUSH':
|
||||
cf.GateRushFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_RAIL_ACTIVATOR':
|
||||
cf.RailActivatorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_ROLLER':
|
||||
cf.RollerFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT':
|
||||
cf.PlayerForcedMovementFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CRAFTING':
|
||||
cf.CraftingFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_POSSESSABLE':
|
||||
cf.PossessableFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_POSSESSOR':
|
||||
cf.PossessorFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_PROPERTY_PLAQUE':
|
||||
cf.PropertyPlaqueFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_BUILD_BORDER':
|
||||
cf.BuildBorderFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
case 'COMPONENT_TYPE_CULLING_PLANE':
|
||||
cf.CullingPlaneFactory(loadObject, objectLoadState, componentId)
|
||||
|
||||
except KeyError:
|
||||
print(f'Unknown component: {componentType}')
|
||||
|
||||
if not 'is_smashable' in loadObject.configData and loadObject.bIsSmashable:
|
||||
loadObject.configData['is_smashable'] = True
|
||||
|
||||
is_smashable = loadObject.configData['is_smashable']
|
||||
if is_smashable and not objectLoadState.hasDestroyable and loadObject.lotToSpawn != 176:
|
||||
objectLoadState.components.append('DESTROYABLE')
|
||||
|
||||
if objectLoadState.hasMissionOffer and not objectLoadState.hasProximityMonitorComponent:
|
||||
objectLoadState.components.append('PROXIMITY_MONITOR')
|
||||
objectLoadState.hasProximityMonitorComponent = True
|
||||
|
||||
if loadObject.field8_0x20 and loadObject.owner != 0:
|
||||
objectLoadState.components.append('OVERHEAD_ICON')
|
||||
|
||||
trigger_id = loadObject.configData.get('trigger_id', 0)
|
||||
if trigger_id != 0:
|
||||
objectLoadState.components.append('TRIGGER')
|
||||
|
||||
return objectLoadState
|
||||
|
||||
components_to_load = {}
|
||||
|
||||
# Testing code to load all components
|
||||
for i in range(110):
|
||||
components_to_load[i] = i
|
||||
|
||||
objectLoadState = DoObjectPreLoad(components_to_load)
|
||||
|
||||
# Convert the components to ints
|
||||
components_to_int = []
|
||||
for component in objectLoadState.components:
|
||||
components_to_int.append([cd.components[f'COMPONENT_TYPE_{component}'], component])
|
||||
|
||||
# Print out the components in a more readable format
|
||||
for comp in components_to_int:
|
||||
print(comp[0], comp[1])
|
||||
Binary file not shown.
Binary file not shown.
@@ -55,34 +55,10 @@ int main(int argc, char** argv) {
|
||||
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
Database::Connect(Game::config);
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("AuthServer");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1500;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
// Get Master server IP and port
|
||||
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
@@ -92,7 +68,7 @@ int main(int argc, char** argv) {
|
||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Auth, Game::config, &Game::shouldShutdown);
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
@@ -131,18 +107,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
Database::Connection->GetMasterServerIP();
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
@@ -151,9 +116,7 @@ int main(int argc, char** argv) {
|
||||
t += std::chrono::milliseconds(authFrameDelta); //Auth can run at a lower "fps"
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
//Delete our objects here:
|
||||
Database::Destroy("AuthServer");
|
||||
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
|
||||
@@ -31,16 +31,13 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
||||
ReadWordlistDCF("blacklist.dcf", false);
|
||||
}
|
||||
|
||||
//Read player names that are ok as well:
|
||||
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
std::string line = res->getString(1).c_str();
|
||||
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
|
||||
m_ApprovedWords.push_back(CalculateHash(line));
|
||||
// Read player names that are ok as well:
|
||||
auto names = Database::Connection->GetAllCharacterNames();
|
||||
|
||||
for (auto& name : names) {
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
m_ApprovedWords.push_back(CalculateHash(name));
|
||||
}
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
dChatFilter::~dChatFilter() {
|
||||
|
||||
@@ -77,34 +77,10 @@ int main(int argc, char** argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
Database::Connect(Game::config);
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("ChatServer");
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
// Get Master server IP and port
|
||||
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
uint32_t maxClients = 50;
|
||||
@@ -112,7 +88,7 @@ int main(int argc, char** argv) {
|
||||
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
|
||||
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
|
||||
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Chat, Game::config, &Game::shouldShutdown);
|
||||
|
||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||
|
||||
@@ -155,18 +131,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
Database::Connection->GetMasterServerIP();
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
@@ -176,8 +141,8 @@ int main(int argc, char** argv) {
|
||||
std::this_thread::sleep_until(t);
|
||||
}
|
||||
|
||||
//Delete our objects here:
|
||||
Database::Destroy("ChatServer");
|
||||
// Delete our objects here:
|
||||
Database::Destroy();
|
||||
delete Game::server;
|
||||
delete Game::logger;
|
||||
delete Game::config;
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "eConnectionType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
PlayerContainer::PlayerContainer() {
|
||||
}
|
||||
@@ -20,10 +19,6 @@ PlayerContainer::~PlayerContainer() {
|
||||
mPlayers.clear();
|
||||
}
|
||||
|
||||
TeamData::TeamData() {
|
||||
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
||||
}
|
||||
|
||||
void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
PlayerData* data = new PlayerData();
|
||||
@@ -46,14 +41,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
|
||||
mPlayers.insert(std::make_pair(data->playerID, data));
|
||||
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
insertLog->setInt(1, data->playerID);
|
||||
insertLog->setInt(2, 0);
|
||||
insertLog->setUInt64(3, time(nullptr));
|
||||
insertLog->setInt(4, data->zoneID.GetMapID());
|
||||
|
||||
insertLog->executeUpdate();
|
||||
Database::Connection->InsertIntoActivityLog(data->playerID, 0, time(nullptr), data->zoneID.GetMapID());
|
||||
}
|
||||
|
||||
void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
@@ -90,14 +78,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
|
||||
mPlayers.erase(playerID);
|
||||
|
||||
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
insertLog->setInt(1, playerID);
|
||||
insertLog->setInt(2, 1);
|
||||
insertLog->setUInt64(3, time(nullptr));
|
||||
insertLog->setInt(4, player->zoneID.GetMapID());
|
||||
|
||||
insertLog->executeUpdate();
|
||||
Database::Connection->InsertIntoActivityLog(playerID, 1, time(nullptr), player->zoneID.GetMapID());
|
||||
}
|
||||
|
||||
void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||
|
||||
@@ -18,7 +18,6 @@ struct PlayerData {
|
||||
};
|
||||
|
||||
struct TeamData {
|
||||
TeamData();
|
||||
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
|
||||
LWOOBJID leaderID = LWOOBJID_EMPTY;
|
||||
std::vector<LWOOBJID> memberIDs{};
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace BrickByBrickFix {
|
||||
/**
|
||||
* @brief Deletes all broken BrickByBrick models that have invalid XML
|
||||
*
|
||||
* @return The number of BrickByBrick models that were truncated
|
||||
*/
|
||||
uint32_t TruncateBrokenBrickByBrickXml();
|
||||
|
||||
/**
|
||||
* @brief Updates all BrickByBrick models in the database to be
|
||||
* in the sd0 format as opposed to a zlib compressed format.
|
||||
*
|
||||
* @return The number of BrickByBrick models that were updated
|
||||
*/
|
||||
uint32_t UpdateBrickByBrickModelsToSd0();
|
||||
};
|
||||
@@ -14,7 +14,6 @@ set(DCOMMON_SOURCES
|
||||
"SHA512.cpp"
|
||||
"Demangler.cpp"
|
||||
"ZCompression.cpp"
|
||||
"BrickByBrickFix.cpp"
|
||||
"BinaryPathFinder.cpp"
|
||||
"FdbToSqlite.cpp"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
set(DDATABASE_SOURCES "CDClientDatabase.cpp"
|
||||
"CDClientManager.cpp"
|
||||
"Database.cpp"
|
||||
"MigrationRunner.cpp")
|
||||
"Database.cpp")
|
||||
|
||||
add_subdirectory(Tables)
|
||||
|
||||
@@ -9,5 +8,17 @@ foreach(file ${DDATABASE_TABLES_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(Databases)
|
||||
|
||||
foreach (file ${DDATABASE_DATABASES_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/${file}")
|
||||
endforeach()
|
||||
|
||||
add_subdirectory(Databases/Migrations)
|
||||
|
||||
foreach (file ${DDATABASE_DATABASES_MIGRATIONS_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/Migrations/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
||||
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
|
||||
|
||||
@@ -1,118 +1,26 @@
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
|
||||
#include "dConfig.h"
|
||||
#include "dLogger.h"
|
||||
using namespace std;
|
||||
|
||||
#pragma warning (disable:4251) //Disables SQL warnings
|
||||
#include "Databases/MySQL.h"
|
||||
|
||||
sql::Driver* Database::driver;
|
||||
sql::Connection* Database::con;
|
||||
sql::Properties Database::props;
|
||||
std::string Database::database;
|
||||
void Database::Connect(dConfig* config) {
|
||||
bool useSqlite = true;
|
||||
if (config->GetValue("mysql_host") != "" && config->GetValue("mysql_database") != "" && config->GetValue("mysql_username") != "" && config->GetValue("mysql_password") != "") {
|
||||
useSqlite = false;
|
||||
}
|
||||
|
||||
void Database::Connect(const string& host, const string& database, const string& username, const string& password) {
|
||||
if (useSqlite) {
|
||||
|
||||
//To bypass debug issues:
|
||||
const char* szDatabase = database.c_str();
|
||||
const char* szUsername = username.c_str();
|
||||
const char* szPassword = password.c_str();
|
||||
|
||||
driver = sql::mariadb::get_driver_instance();
|
||||
|
||||
sql::Properties properties;
|
||||
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
|
||||
// 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the
|
||||
// presence of a /
|
||||
// 2) even avoiding that, the connector still assumes you're connecting with a tcp socket
|
||||
// So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost,
|
||||
// which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate
|
||||
// property manually (which the URL parsing fails to do)
|
||||
const std::string UNIX_PROTO = "unix://";
|
||||
const std::string PIPE_PROTO = "pipe://";
|
||||
if (host.find(UNIX_PROTO) == 0) {
|
||||
properties["hostName"] = "unix://localhost";
|
||||
properties["localSocket"] = host.substr(UNIX_PROTO.length()).c_str();
|
||||
} else if (host.find(PIPE_PROTO) == 0) {
|
||||
properties["hostName"] = "pipe://localhost";
|
||||
properties["pipe"] = host.substr(PIPE_PROTO.length()).c_str();
|
||||
} else {
|
||||
properties["hostName"] = host.c_str();
|
||||
}
|
||||
properties["user"] = szUsername;
|
||||
properties["password"] = szPassword;
|
||||
properties["autoReconnect"] = "true";
|
||||
|
||||
Database::props = properties;
|
||||
Database::database = database;
|
||||
|
||||
Database::Connect();
|
||||
}
|
||||
|
||||
void Database::Connect() {
|
||||
// `connect(const Properties& props)` segfaults in windows debug, but
|
||||
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
|
||||
if (Database::props.find("localSocket") != Database::props.end() || Database::props.find("pipe") != Database::props.end()) {
|
||||
con = driver->connect(Database::props);
|
||||
} else {
|
||||
con = driver->connect(Database::props["hostName"].c_str(), Database::props["user"].c_str(), Database::props["password"].c_str());
|
||||
}
|
||||
con->setSchema(Database::database.c_str());
|
||||
}
|
||||
|
||||
void Database::Destroy(std::string source, bool log) {
|
||||
if (!con) return;
|
||||
|
||||
if (log) {
|
||||
if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!", source.c_str());
|
||||
else Game::logger->Log("Database", "Destroying MySQL connection!");
|
||||
Database::Connection = new MySQLDatabase(config->GetValue("mysql_host"), config->GetValue("mysql_database"), config->GetValue("mysql_username"), config->GetValue("mysql_password"));
|
||||
Database::ConnectionType = eConnectionTypes::MYSQL;
|
||||
}
|
||||
|
||||
con->close();
|
||||
delete con;
|
||||
} //Destroy
|
||||
|
||||
sql::Statement* Database::CreateStmt() {
|
||||
sql::Statement* toReturn = con->createStatement();
|
||||
return toReturn;
|
||||
} //CreateStmt
|
||||
|
||||
sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) {
|
||||
const char* test = query.c_str();
|
||||
size_t size = query.length();
|
||||
sql::SQLString str(test, size);
|
||||
|
||||
if (!con) {
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL");
|
||||
}
|
||||
|
||||
if (!con->isValid() || con->isClosed()) {
|
||||
delete con;
|
||||
|
||||
con = nullptr;
|
||||
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
|
||||
}
|
||||
|
||||
auto* stmt = con->prepareStatement(str);
|
||||
|
||||
return stmt;
|
||||
} //CreatePreppedStmt
|
||||
|
||||
void Database::Commit() {
|
||||
Database::con->commit();
|
||||
Database::Connection->Connect();
|
||||
}
|
||||
|
||||
bool Database::GetAutoCommit() {
|
||||
// TODO This should not just access a pointer. A future PR should update this
|
||||
// to check for null and throw an error if the connection is not valid.
|
||||
return con->getAutoCommit();
|
||||
}
|
||||
|
||||
void Database::SetAutoCommit(bool value) {
|
||||
// TODO This should not just access a pointer. A future PR should update this
|
||||
// to check for null and throw an error if the connection is not valid.
|
||||
Database::con->setAutoCommit(value);
|
||||
void Database::Destroy() {
|
||||
Database::Connection->Destroy();
|
||||
delete Database::Connection;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <conncpp.hpp>
|
||||
|
||||
class MySqlException : public std::runtime_error {
|
||||
public:
|
||||
MySqlException() : std::runtime_error("MySQL error!") {}
|
||||
MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {}
|
||||
};
|
||||
#include "Databases/DatabaseBase.h"
|
||||
|
||||
class dConfig;
|
||||
|
||||
class Database {
|
||||
private:
|
||||
static sql::Driver* driver;
|
||||
static sql::Connection* con;
|
||||
static sql::Properties props;
|
||||
static std::string database;
|
||||
public:
|
||||
static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
|
||||
static void Connect();
|
||||
static void Destroy(std::string source = "", bool log = true);
|
||||
static DatabaseBase* Connection;
|
||||
static eConnectionTypes ConnectionType;
|
||||
|
||||
static sql::Statement* CreateStmt();
|
||||
static sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||
static void Commit();
|
||||
static bool GetAutoCommit();
|
||||
static void SetAutoCommit(bool value);
|
||||
|
||||
static std::string GetDatabase() { return database; }
|
||||
static sql::Properties GetProperties() { return props; }
|
||||
static void Connect(dConfig* config);
|
||||
static void Destroy();
|
||||
};
|
||||
|
||||
1
dDatabase/Databases/CMakeLists.txt
Normal file
1
dDatabase/Databases/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
set(DDATABASE_DATABASES_SOURCES "MySQL.cpp")
|
||||
100
dDatabase/Databases/DatabaseBase.h
Normal file
100
dDatabase/Databases/DatabaseBase.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "RakNetTypes.h"
|
||||
|
||||
#include "Structures.h"
|
||||
|
||||
enum eConnectionTypes {
|
||||
NONE,
|
||||
MYSQL,
|
||||
SQLITE
|
||||
};
|
||||
|
||||
class DatabaseBase {
|
||||
public:
|
||||
virtual void Connect() = 0;
|
||||
virtual void Destroy() = 0;
|
||||
|
||||
// Server Get
|
||||
virtual SocketDescriptor GetMasterServerIP() = 0;
|
||||
|
||||
// Server Set
|
||||
virtual void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) = 0;
|
||||
virtual void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) = 0;
|
||||
|
||||
// Misc
|
||||
virtual void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) = 0;
|
||||
virtual void InsertIntoCommandLog(uint32_t playerId, const std::string& command) = 0;
|
||||
|
||||
// Character Get
|
||||
virtual CharacterInfo GetCharacterInfoByID(uint32_t id) = 0;
|
||||
virtual CharacterInfo GetCharacterInfoByName(const std::string& name) = 0;
|
||||
virtual std::string GetCharacterXMLByID(uint32_t id) = 0;
|
||||
virtual std::vector<std::string> GetAllCharacterNames() = 0;
|
||||
virtual std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) = 0;
|
||||
virtual bool IsCharacterNameAvailable(const std::string& name) = 0;
|
||||
|
||||
// Charater Write
|
||||
virtual void CreateCharacterXML(uint32_t id, const std::string& xml) = 0;
|
||||
virtual void UpdateCharacterXML(uint32_t id, const std::string& xml) = 0;
|
||||
virtual void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) = 0;
|
||||
virtual void ApproveCharacterName(uint32_t id, const std::string& newName) = 0;
|
||||
virtual void SetPendingCharacterName(uint32_t id, const std::string& pendingName) = 0;
|
||||
virtual void UpdateCharacterLastLogin(uint32_t id, uint64_t time) = 0;
|
||||
|
||||
// Character Delete
|
||||
virtual void DeleteCharacter(uint32_t id) = 0;
|
||||
|
||||
// Friends Get
|
||||
virtual bool AreBestFriends(uint32_t charId1, uint32_t charId2) = 0;
|
||||
|
||||
// Account Get
|
||||
virtual AccountInfo GetAccountByName(const std::string& name) = 0;
|
||||
virtual AccountInfo GetAccountByID(uint32_t id) = 0;
|
||||
virtual uint32_t GetLatestCharacterOfAccount(uint32_t id) = 0;
|
||||
|
||||
// Account Set
|
||||
virtual void BanAccount(uint32_t id) = 0;
|
||||
virtual void MuteAccount(uint32_t id, uint64_t muteExpireDate) = 0;
|
||||
|
||||
// Pet Write
|
||||
virtual void CreatePetName(uint64_t id, const std::string& name, bool approved) = 0;
|
||||
|
||||
// Pet Delete
|
||||
virtual void DeletePetName(uint64_t id) = 0;
|
||||
|
||||
// Pet Get
|
||||
virtual PetName GetPetName(uint64_t id) = 0;
|
||||
|
||||
// Keys Get
|
||||
virtual bool IsKeyActive(uint32_t id) = 0;
|
||||
|
||||
// Object ID tracker Get
|
||||
virtual uint32_t GetObjectIDTracker() = 0;
|
||||
|
||||
// Object ID tracker Set
|
||||
virtual void SetObjectIDTracker(uint32_t id) = 0;
|
||||
|
||||
// Mail Get
|
||||
virtual MailInfo GetMailByID(uint64_t id) = 0;
|
||||
virtual std::vector<MailInfo> GetAllRecentMailOfUser(uint32_t id) = 0;
|
||||
virtual uint32_t GetUnreadMailCountForUser(uint32_t id) = 0;
|
||||
|
||||
// Mail Write
|
||||
virtual void WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId = 0, uint32_t attachmentLot = 0, uint64_t attachmentSubkey = 0, uint32_t attachmentCount = 0, bool wasRead = false) = 0;
|
||||
virtual void SetMailAsRead(uint64_t id) = 0;
|
||||
virtual void RemoveAttachmentFromMail(uint64_t id) = 0;
|
||||
|
||||
// Mail Delete
|
||||
virtual void DeleteMail(uint64_t id) = 0;
|
||||
|
||||
// Property Get
|
||||
virtual uint64_t GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) = 0;
|
||||
virtual std::vector<uint32_t> GetBBBModlesForProperty(uint32_t propertyId) = 0;
|
||||
virtual std::istream GetLXFMLFromID(uint32_t id) = 0;
|
||||
private:
|
||||
|
||||
};
|
||||
1
dDatabase/Databases/Migrations/CMakeLists.txt
Normal file
1
dDatabase/Databases/Migrations/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
set(DDATABASE_DATABASES_MIGRATIONS_SOURCES "MigrationManager.cpp" "MySQLMigration.cpp")
|
||||
90
dDatabase/Databases/Migrations/MigrationManager.cpp
Normal file
90
dDatabase/Databases/Migrations/MigrationManager.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "MigrationManager.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "dLogger.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
|
||||
Migration Migration::LoadMigration(const std::string& type, const std::string& dbType, const std::string& name) {
|
||||
Migration migration{};
|
||||
std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / type / dbType / name);
|
||||
|
||||
if (file.is_open()) {
|
||||
std::string line;
|
||||
std::string total = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
total += line;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
migration.name = ;
|
||||
migration.data = total;
|
||||
}
|
||||
|
||||
return migration;
|
||||
}
|
||||
|
||||
void MigrationManager::RunSQLiteMigrations() {
|
||||
auto cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);");
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
|
||||
auto migration = Migration::LoadMigration("cdserver", "", entry);
|
||||
|
||||
if (migration.data.empty()) continue;
|
||||
|
||||
// Check if there is an entry in the migration history table on the cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
cdstmt.bind((int32_t)1, migration.name.c_str());
|
||||
auto cdres = cdstmt.execQuery();
|
||||
bool doExit = !cdres.eof();
|
||||
cdres.finalize();
|
||||
cdstmt.finalize();
|
||||
|
||||
if (doExit) continue;
|
||||
|
||||
// Check first if there is entry in the migration history table on the main database.
|
||||
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
auto* res = stmt->executeQuery();
|
||||
doExit = res->next();
|
||||
delete res;
|
||||
delete stmt;
|
||||
if (doExit) {
|
||||
// Insert into cdclient database if there is an entry in the main database but not the cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
cdstmt.bind((int32_t)1, migration.name.c_str());
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Doing these 1 migration at a time since one takes a long time and some may think it is crashing.
|
||||
// This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated".
|
||||
Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str());
|
||||
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
||||
for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) {
|
||||
if (dml.empty()) continue;
|
||||
try {
|
||||
CDClientDatabase::ExecuteDML(dml.c_str());
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Insert into cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
cdstmt.bind((int32_t)1, migration.name.c_str());
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||
}
|
||||
|
||||
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
|
||||
}
|
||||
17
dDatabase/Databases/Migrations/MigrationManager.h
Normal file
17
dDatabase/Databases/Migrations/MigrationManager.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
class DatabaseBase;
|
||||
|
||||
struct Migration {
|
||||
std::string data;
|
||||
std::string name;
|
||||
|
||||
static Migration LoadMigration(const std::string& type, const std::string& dbType, const std::string& name);
|
||||
};
|
||||
|
||||
class MigrationManager {
|
||||
public:
|
||||
void RunMigrations(DatabaseBase* db);
|
||||
void RunSQLiteMigrations();
|
||||
private:
|
||||
};
|
||||
@@ -1,35 +1,93 @@
|
||||
#include "BrickByBrickFix.h"
|
||||
#include "MySQLMigration.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <istream>
|
||||
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Game.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dLogger.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
#include "ZCompression.h"
|
||||
#include "tinyxml2.h"
|
||||
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "ZCompression.h"
|
||||
#include "dLogger.h"
|
||||
#include "../MySQL.h"
|
||||
|
||||
//! Forward declarations
|
||||
void MigrationRunner::RunMigrations(DatabaseBase* db) {
|
||||
auto database = dynamic_cast<MySQLDatabase*>(db);
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
|
||||
void WriteSd0Magic(char* input, uint32_t chunkSize);
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck);
|
||||
auto* stmt = database->CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
/**
|
||||
* @brief Truncates all models with broken data from the database.
|
||||
*
|
||||
* @return The number of models deleted
|
||||
*/
|
||||
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
sql::SQLString finalSQL = "";
|
||||
bool runSd0Migrations = false;
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/mysql").string())) {
|
||||
auto migration = Migration::LoadMigration("dlu", "mysql", entry);
|
||||
|
||||
if (migration.data.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stmt = database->CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
auto* res = stmt->executeQuery();
|
||||
bool doExit = res->next();
|
||||
delete res;
|
||||
delete stmt;
|
||||
if (doExit) continue;
|
||||
|
||||
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
|
||||
if (migration.name == "dlu/5_brick_model_sd0.sql") {
|
||||
runSd0Migrations = true;
|
||||
} else {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
|
||||
stmt = database->CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations) {
|
||||
Game::logger->Log("MigrationRunner", "Server database is up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!finalSQL.empty()) {
|
||||
auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
|
||||
std::unique_ptr<sql::Statement> simpleStatement(Database::CreateStmt());
|
||||
for (auto& query : migration) {
|
||||
try {
|
||||
if (query.empty()) continue;
|
||||
simpleStatement->execute(query.c_str());
|
||||
} catch (sql::SQLException& e) {
|
||||
Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do this last on the off chance none of the other migrations have been run yet.
|
||||
if (runSd0Migrations) {
|
||||
uint32_t numberOfUpdatedModels = UpdateBrickByBrickModelsToSd0();
|
||||
Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels);
|
||||
|
||||
uint32_t numberOfTruncatedModels = TruncateBrokenBrickByBrickXml();
|
||||
Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t TruncateBrokenBrickByBrickXml(MySQLDatabase* database) {
|
||||
uint32_t modelsTruncated{};
|
||||
auto modelsToTruncate = GetModelsFromDatabase();
|
||||
bool previousCommitValue = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
auto modelsToTruncate = GetModelsFromDatabase(database);
|
||||
|
||||
bool previousCommitValue = database->GetAutoCommit();
|
||||
database->SetAutoCommit(false);
|
||||
|
||||
while (modelsToTruncate->next()) {
|
||||
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(database->CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(database->CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
|
||||
std::string completeUncompressedModel{};
|
||||
uint32_t chunkCount{};
|
||||
uint64_t modelId = modelsToTruncate->getInt(1);
|
||||
@@ -95,8 +153,8 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
}
|
||||
}
|
||||
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousCommitValue);
|
||||
database->Commit();
|
||||
database->SetAutoCommit(previousCommitValue);
|
||||
return modelsTruncated;
|
||||
}
|
||||
|
||||
@@ -106,12 +164,14 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
*
|
||||
* @return The number of models updated to SD0
|
||||
*/
|
||||
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
uint32_t UpdateBrickByBrickModelsToSd0(MySQLDatabase* database) {
|
||||
uint32_t updatedModels = 0;
|
||||
auto modelsToUpdate = GetModelsFromDatabase();
|
||||
auto previousAutoCommitState = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
|
||||
|
||||
auto previousAutoCommitState = database->GetAutoCommit();
|
||||
database->SetAutoCommit(false);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> insertionStatement(database->CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
|
||||
while (modelsToUpdate->next()) {
|
||||
int64_t modelId = modelsToUpdate->getInt64(1);
|
||||
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
|
||||
@@ -150,13 +210,14 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
}
|
||||
}
|
||||
}
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousAutoCommitState);
|
||||
|
||||
database->Commit();
|
||||
database->SetAutoCommit(previousAutoCommitState);
|
||||
return updatedModels;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
|
||||
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase(MySQLDatabase* database) {
|
||||
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(database->CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
|
||||
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
|
||||
}
|
||||
|
||||
13
dDatabase/Databases/Migrations/MySQLMigration.h
Normal file
13
dDatabase/Databases/Migrations/MySQLMigration.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "MigrationManager.h"
|
||||
|
||||
class MySQLMigrationManager : public MigrationManager {
|
||||
public:
|
||||
void RunMigrations(DatabaseBase* db) override;
|
||||
void RunSQLiteMigrations() override;
|
||||
private:
|
||||
|
||||
};
|
||||
651
dDatabase/Databases/MySQL.cpp
Normal file
651
dDatabase/Databases/MySQL.cpp
Normal file
@@ -0,0 +1,651 @@
|
||||
#include "MySQL.h"
|
||||
|
||||
#pragma warning (disable:4251)
|
||||
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "dLogger.h"
|
||||
|
||||
MySQLDatabase::MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password) {
|
||||
this->m_Host = host;
|
||||
this->m_Database = database;
|
||||
this->m_Username = username;
|
||||
this->m_Password = password;
|
||||
|
||||
m_Driver = sql::mariadb::get_driver_instance();
|
||||
|
||||
sql::Properties properties;
|
||||
|
||||
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
|
||||
// 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the
|
||||
// presence of a /
|
||||
// 2) even avoiding that, the connector still assumes you're connecting with a tcp socket
|
||||
// So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost,
|
||||
// which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate
|
||||
// property manually (which the URL parsing fails to do)
|
||||
const std::string UNIX_PROTO = "unix://";
|
||||
const std::string PIPE_PROTO = "pipe://";
|
||||
if (this->m_Host.find(UNIX_PROTO) == 0) {
|
||||
properties["hostName"] = "unix://localhost";
|
||||
properties["localSocket"] = this->m_Host.substr(UNIX_PROTO.length()).c_str();
|
||||
} else if (this->m_Host.find(PIPE_PROTO) == 0) {
|
||||
properties["hostName"] = "pipe://localhost";
|
||||
properties["pipe"] = this->m_Host.substr(PIPE_PROTO.length()).c_str();
|
||||
} else {
|
||||
properties["hostName"] = this->m_Host.c_str();
|
||||
}
|
||||
properties["user"] = this->m_Username.c_str();
|
||||
properties["password"] = this->m_Password.c_str();
|
||||
properties["autoReconnect"] = "true";
|
||||
|
||||
this->m_Properties = properties;
|
||||
this->m_Database = database;
|
||||
}
|
||||
|
||||
MySQLDatabase::~MySQLDatabase() {
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
void MySQLDatabase::Connect() {
|
||||
if (this->m_Properties.find("localSocket") != this->m_Properties.end() || this->m_Properties.find("pipe") != this->m_Properties.end()) {
|
||||
this->m_Connection = m_Driver->connect(this->m_Properties);
|
||||
} else {
|
||||
this->m_Connection = m_Driver->connect(
|
||||
this->m_Properties["hostName"].c_str(),
|
||||
this->m_Properties["user"].c_str(),
|
||||
this->m_Properties["password"].c_str()
|
||||
);
|
||||
}
|
||||
this->m_Connection->setSchema(this->m_Database.c_str());
|
||||
}
|
||||
|
||||
void MySQLDatabase::Destroy() {
|
||||
if (this->m_Connection != nullptr) {
|
||||
this->m_Connection->close();
|
||||
delete this->m_Connection;
|
||||
this->m_Connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
sql::Statement* MySQLDatabase::CreateStmt() {
|
||||
sql::Statement* toReturn = this->m_Connection->createStatement();
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) {
|
||||
const char* test = query.c_str();
|
||||
size_t size = query.length();
|
||||
sql::SQLString str(test, size);
|
||||
|
||||
if (!this->m_Connection) {
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL");
|
||||
}
|
||||
|
||||
if (!this->m_Connection->isValid() || this->m_Connection->isClosed()) {
|
||||
delete this->m_Connection;
|
||||
|
||||
this->m_Connection = nullptr;
|
||||
|
||||
Connect();
|
||||
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
|
||||
}
|
||||
|
||||
auto* stmt = this->m_Connection->prepareStatement(str);
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> MySQLDatabase::CreatePreppedStmtUnique(const std::string& query) {
|
||||
std::unique_ptr<sql::PreparedStatement> stmt(CreatePreppedStmt(query));
|
||||
return stmt;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::ResultSet> MySQLDatabase::GetResultsOfStatement(sql::Statement* stmt) {
|
||||
auto* res = stmt->executeQuery();
|
||||
std::unique_ptr<sql::ResultSet> result(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void MySQLDatabase::Commit() {
|
||||
this->m_Connection->commit();
|
||||
}
|
||||
|
||||
bool MySQLDatabase::GetAutoCommit() {
|
||||
// TODO This should not just access a pointer. A future PR should update this
|
||||
// to check for null and throw an error if the connection is not valid.
|
||||
return m_Connection->getAutoCommit();
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetAutoCommit(bool value) {
|
||||
// TODO This should not just access a pointer. A future PR should update this
|
||||
// to check for null and throw an error if the connection is not valid.
|
||||
m_Connection->setAutoCommit(value);
|
||||
}
|
||||
|
||||
SocketDescriptor MySQLDatabase::GetMasterServerIP() {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT ip, port FROM servers WHERE name = 'master';");
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return SocketDescriptor(res->getInt("port"), res->getString("ip"));
|
||||
}
|
||||
|
||||
return SocketDescriptor(0, "");
|
||||
}
|
||||
|
||||
void MySQLDatabase::CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO servers (name, ip, port, state, version) VALUES (?, ?, ?, ?, ?);");
|
||||
stmt->setString(1, name);
|
||||
stmt->setString(2, ip);
|
||||
stmt->setInt(3, port);
|
||||
stmt->setInt(4, state);
|
||||
stmt->setInt(5, version);
|
||||
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE servers SET ip = ?, port = ? WHERE name = ?;");
|
||||
stmt->setString(1, ip);
|
||||
stmt->setInt(2, port);
|
||||
stmt->setString(3, name);
|
||||
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
std::vector<std::string> MySQLDatabase::GetAllCharacterNames() {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo;");
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
std::vector<std::string> names;
|
||||
|
||||
while (res->next()) {
|
||||
names.push_back(res->getString("name").c_str());
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
bool MySQLDatabase::IsCharacterNameAvailable(const std::string& name) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo WHERE name = ? OR pending_name = ?;");
|
||||
stmt->setString(1, name);
|
||||
stmt->setString(2, name);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
return !res->next();
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
||||
|
||||
stmt->setUInt(1, playerId);
|
||||
stmt->setUInt(2, activityId);
|
||||
stmt->setUInt(3, timestamp);
|
||||
stmt->setUInt(4, zoneId);
|
||||
|
||||
stmt->executeUpdate();
|
||||
}
|
||||
|
||||
void MySQLDatabase::InsertIntoCommandLog(uint32_t playerId, const std::string& command) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO command_log (character_id, command) VALUES (?, ?);");
|
||||
|
||||
stmt->setUInt(1, playerId);
|
||||
stmt->setString(2, command);
|
||||
|
||||
stmt->executeUpdate();
|
||||
}
|
||||
|
||||
CharacterInfo MySQLDatabase::GetCharacterInfoByID(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT account_id, name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
CharacterInfo info{};
|
||||
info.AccountID = res->getUInt("account_id");
|
||||
info.ID = id;
|
||||
info.Name = res->getString("name");
|
||||
info.PendingName = res->getString("pending_name");
|
||||
info.NameRejected = res->getBoolean("needs_rename");
|
||||
info.PropertyCloneID = res->getUInt("prop_clone_id");
|
||||
info.PermissionMap = (ePermissionMap)res->getUInt("permission_map");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return CharacterInfo{};
|
||||
}
|
||||
|
||||
CharacterInfo MySQLDatabase::GetCharacterInfoByName(const std::string& name) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE name = ?");
|
||||
stmt->setString(1, name);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return GetCharacterInfoByID(res->getUInt("id"));
|
||||
}
|
||||
|
||||
return CharacterInfo{};
|
||||
}
|
||||
|
||||
uint32_t MySQLDatabase::GetLatestCharacterOfAccount(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return res->getUInt("id");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string MySQLDatabase::GetCharacterXMLByID(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return res->getString("xml_data").c_str();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void MySQLDatabase::CreateCharacterXML(uint32_t id, const std::string& xml) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO charxml (id, xml_data) VALUES (?, ?);");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->setString(2, xml);
|
||||
|
||||
stmt->executeUpdate();
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateCharacterXML(uint32_t id, const std::string& xml) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE charxml SET xml_data = ? WHERE id = ?;");
|
||||
stmt->setString(1, xml);
|
||||
stmt->setUInt(2, id);
|
||||
|
||||
stmt->executeUpdate();
|
||||
}
|
||||
|
||||
void MySQLDatabase::CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO charinfo (id, account_id, name, pending_name, needs_rename, last_login) VALUES (?, ?, ?, ?, ?, ?);");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->setUInt(2, account_id);
|
||||
stmt->setString(3, name);
|
||||
stmt->setString(4, pending_name);
|
||||
stmt->setBoolean(5, needs_rename);
|
||||
stmt->setUInt64(6, last_login);
|
||||
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::ApproveCharacterName(uint32_t id, const std::string& newName) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1");
|
||||
stmt->setString(1, newName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetPendingCharacterName(uint32_t id, const std::string& pendingName) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1;");
|
||||
stmt->setString(1, pendingName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateCharacterLastLogin(uint32_t id, uint64_t time) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt64(1, time);
|
||||
stmt->setUInt(2, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::DeleteCharacter(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("DELETE FROM charxml WHERE id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM command_log WHERE character_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM friends WHERE player_id = ? OR friend_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->setUInt(2, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM leaderboard WHERE character_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id = ?)");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM properties WHERE owner_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM ugc WHERE character_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM activity_log WHERE character_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM mail WHERE receiver_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
|
||||
stmt = CreatePreppedStmtUnique("DELETE FROM charinfo WHERE id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
AccountInfo MySQLDatabase::GetAccountByName(const std::string& name) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT id FROM accounts WHERE name = ? LIMIT 1;");
|
||||
stmt->setString(1, name);
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return GetAccountByID(res->getUInt("id"));
|
||||
}
|
||||
|
||||
return AccountInfo{};
|
||||
}
|
||||
|
||||
bool MySQLDatabase::AreBestFriends(uint32_t goon1, uint32_t goon2) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
|
||||
stmt->setUInt(1, goon1);
|
||||
stmt->setUInt(2, goon2);
|
||||
stmt->setUInt(3, goon2);
|
||||
stmt->setUInt(4, goon1);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return res->getInt("best_friend") == 3;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AccountInfo MySQLDatabase::GetAccountByID(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT name, password, gm_level, locked, banned, play_key_id, created_at, mute_expire FROM accounts WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
AccountInfo info{};
|
||||
info.ID = id;
|
||||
info.Name = res->getString("name");
|
||||
info.Password = res->getString("password");
|
||||
info.MaxGMLevel = res->getUInt("gm_level");
|
||||
info.Locked = res->getBoolean("locked");
|
||||
info.Banned = res->getBoolean("banned");
|
||||
info.PlayKeyID = res->getUInt("play_key_id");
|
||||
info.CreatedAt = res->getUInt64("created_at");
|
||||
info.MuteExpire = res->getUInt64("mute_expire");
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return AccountInfo{};
|
||||
}
|
||||
|
||||
void MySQLDatabase::BanAccount(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE accounts SET banned = 1 WHERE id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::MuteAccount(uint32_t id, uint64_t muteExpireDate) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE accounts SET mute_expire = ? WHERE id = ?;");
|
||||
stmt->setUInt64(1, muteExpireDate);
|
||||
stmt->setUInt(2, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
std::vector<CharacterInfo> MySQLDatabase::GetAllCharactersByAccountID(uint32_t accountId) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? LIMIT 4;");
|
||||
stmt->setUInt(1, accountId);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
std::vector<CharacterInfo> characters;
|
||||
|
||||
while (res->next()) {
|
||||
characters.push_back(GetCharacterInfoByID(res->getUInt("id")));
|
||||
}
|
||||
|
||||
return characters;
|
||||
}
|
||||
|
||||
void MySQLDatabase::CreatePetName(uint64_t id, const std::string& name, bool approved) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO pet_names (id, name, approved) VALUES (?, ?, ?);");
|
||||
stmt->setUInt64(1, id);
|
||||
stmt->setString(2, name);
|
||||
stmt->setBoolean(3, approved);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::DeletePetName(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("DELETE FROM pet_names WHERE id = ?;");
|
||||
stmt->setUInt64(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
PetName MySQLDatabase::GetPetName(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT name, approved FROM pet_names WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt64(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
PetName name{};
|
||||
name.ID = id;
|
||||
name.Name = res->getString("name");
|
||||
name.Approved = res->getBoolean("approved");
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
return PetName{};
|
||||
}
|
||||
|
||||
bool MySQLDatabase::IsKeyActive(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT * FROM play_keys WHERE id = ? AND active = 1 LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t MySQLDatabase::GetObjectIDTracker() {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT last_object_id FROM object_id_tracker;");
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
uint32_t id = res->getUInt("last_object_id");
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO object_id_tracker (last_object_id) VALUES (1);");
|
||||
stmt->execute();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetObjectIDTracker(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE object_id_tracker SET last_object_id = ?;");
|
||||
stmt->setUInt(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
MailInfo MySQLDatabase::GetMailByID(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT * FROM mail WHERE id = ?;");
|
||||
stmt->setUInt64(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
if (res->next()) {
|
||||
MailInfo mail{};
|
||||
mail.ID = id;
|
||||
mail.SenderID = res->getUInt("sender_id");
|
||||
mail.SenderName = res->getString("sender_name");
|
||||
mail.ReceiverID = res->getUInt("receiver_id");
|
||||
mail.ReceiverName = res->getString("receiver_name");
|
||||
mail.TimeSent = res->getUInt64("time_sent");
|
||||
mail.Subject = res->getString("subject");
|
||||
mail.Body = res->getString("body");
|
||||
mail.AttachmentID = res->getUInt("attachment_id");
|
||||
mail.AttachmentLOT = res->getUInt("attachment_lot");
|
||||
mail.AttachmentSubkey = res->getUInt64("attachment_subkey");
|
||||
mail.AttachmentCount = res->getUInt("attachment_count");
|
||||
mail.WasRead = res->getBoolean("was_read");
|
||||
|
||||
return mail;
|
||||
}
|
||||
|
||||
return MailInfo{};
|
||||
}
|
||||
|
||||
std::vector<MailInfo> MySQLDatabase::GetAllRecentMailOfUser(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT * FROM mail WHERE receiver_id = ? LIMIT 20;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
std::vector<MailInfo> mail;
|
||||
|
||||
while (res->next()) {
|
||||
MailInfo mailInfo{};
|
||||
mailInfo.ID = res->getUInt64("id");
|
||||
mailInfo.SenderID = res->getUInt("sender_id");
|
||||
mailInfo.SenderName = res->getString("sender_name");
|
||||
mailInfo.ReceiverID = res->getUInt("receiver_id");
|
||||
mailInfo.ReceiverName = res->getString("receiver_name");
|
||||
mailInfo.TimeSent = res->getUInt64("time_sent");
|
||||
mailInfo.Subject = res->getString("subject");
|
||||
mailInfo.Body = res->getString("body");
|
||||
mailInfo.AttachmentID = res->getUInt("attachment_id");
|
||||
mailInfo.AttachmentLOT = res->getUInt("attachment_lot");
|
||||
mailInfo.AttachmentSubkey = res->getUInt64("attachment_subkey");
|
||||
mailInfo.AttachmentCount = res->getUInt("attachment_count");
|
||||
mailInfo.WasRead = res->getBoolean("was_read");
|
||||
|
||||
mail.push_back(mailInfo);
|
||||
}
|
||||
|
||||
return mail;
|
||||
}
|
||||
|
||||
uint32_t MySQLDatabase::GetUnreadMailCountForUser(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT COUNT(*) FROM mail WHERE receiver_id = ? AND was_read = 0;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return res->getUInt(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MySQLDatabase::WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId, uint32_t attachmentLot, uint64_t attachmentSubkey, uint32_t attachmentCount, bool wasRead) {
|
||||
auto stmt = CreatePreppedStmtUnique("INSERT INTO mail (sender_id, sender_name, receiver_id, receiver_name, time_sent, subject, body, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
|
||||
stmt->setUInt(1, senderId);
|
||||
stmt->setString(2, senderName);
|
||||
stmt->setUInt(3, receiverId);
|
||||
stmt->setString(4, receiverName);
|
||||
stmt->setUInt64(5, sendTime);
|
||||
stmt->setString(6, subject);
|
||||
stmt->setString(7, body);
|
||||
stmt->setUInt(8, attachmentId);
|
||||
stmt->setUInt(9, attachmentLot);
|
||||
stmt->setUInt64(10, attachmentSubkey);
|
||||
stmt->setUInt(11, attachmentCount);
|
||||
stmt->setBoolean(12, wasRead);
|
||||
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::DeleteMail(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("DELETE FROM mail WHERE id = ?;");
|
||||
stmt->setUInt64(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::SetMailAsRead(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE mail SET was_read = 1 WHERE id = ?;");
|
||||
stmt->setUInt64(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveAttachmentFromMail(uint64_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("UPDATE mail SET attachment_lot = 0 WHERE id = ?;");
|
||||
stmt->setUInt64(1, id);
|
||||
stmt->execute();
|
||||
}
|
||||
|
||||
uint64_t MySQLDatabase::GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT id FROM properties WHERE template_id = ? AND clone_id = ? LIMIT 1;");
|
||||
stmt->setUInt(1, templateId);
|
||||
stmt->setUInt(2, cloneId);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
return res->getUInt64("id");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint32_t> MySQLDatabase::GetBBBModlesForProperty(uint32_t propertyId) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT ugc_id FROM properties_contents WHERE lot = 14 AND property_id = ?;");
|
||||
stmt->setUInt(1, propertyId);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
std::vector<uint32_t> models;
|
||||
|
||||
while (res->next()) {
|
||||
models.push_back(res->getUInt("ugc_id"));
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
|
||||
std::istream* MySQLDatabase::GetLXFMLFromID(uint32_t id) {
|
||||
auto stmt = CreatePreppedStmtUnique("SELECT lxfml FROM ugc WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt(1, id);
|
||||
|
||||
auto res = GetResultsOfStatement(stmt.get());
|
||||
|
||||
while (res->next()) {
|
||||
std::istream* blob = res->getBlob("lxfml");
|
||||
return blob;
|
||||
}
|
||||
|
||||
return new std::istream();
|
||||
}
|
||||
99
dDatabase/Databases/MySQL.h
Normal file
99
dDatabase/Databases/MySQL.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include "DatabaseBase.h"
|
||||
|
||||
#include <string>
|
||||
#include <conncpp.hpp>
|
||||
|
||||
class MySqlException : public std::runtime_error {
|
||||
public:
|
||||
MySqlException() : std::runtime_error("MySQL error!") {}
|
||||
MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {}
|
||||
};
|
||||
|
||||
class MySQLDatabase : public DatabaseBase {
|
||||
public:
|
||||
MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
|
||||
~MySQLDatabase();
|
||||
|
||||
void Connect() override;
|
||||
void Destroy() override;
|
||||
|
||||
sql::Statement* CreateStmt();
|
||||
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
|
||||
|
||||
std::unique_ptr<sql::PreparedStatement> CreatePreppedStmtUnique(const std::string& query);
|
||||
std::unique_ptr<sql::ResultSet> GetResultsOfStatement(sql::Statement* stmt);
|
||||
|
||||
void Commit();
|
||||
bool GetAutoCommit();
|
||||
void SetAutoCommit(bool value);
|
||||
|
||||
SocketDescriptor GetMasterServerIP() override;
|
||||
|
||||
void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) override;
|
||||
void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) override;
|
||||
|
||||
void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) override;
|
||||
void InsertIntoCommandLog(uint32_t playerId, const std::string& command) override;
|
||||
|
||||
CharacterInfo GetCharacterInfoByID(uint32_t id) override;
|
||||
CharacterInfo GetCharacterInfoByName(const std::string& name) override;
|
||||
std::string GetCharacterXMLByID(uint32_t id) override;
|
||||
std::vector<std::string> GetAllCharacterNames() override;
|
||||
std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) override;
|
||||
bool IsCharacterNameAvailable(const std::string& name) override;
|
||||
|
||||
void CreateCharacterXML(uint32_t id, const std::string& xml) override;
|
||||
void UpdateCharacterXML(uint32_t id, const std::string& xml) override;
|
||||
void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) override;
|
||||
void ApproveCharacterName(uint32_t id, const std::string& newName) override;
|
||||
void SetPendingCharacterName(uint32_t id, const std::string& pendingName) override;
|
||||
void UpdateCharacterLastLogin(uint32_t id, uint64_t time) override;
|
||||
|
||||
void DeleteCharacter(uint32_t id) override;
|
||||
|
||||
bool AreBestFriends(uint32_t charId1, uint32_t charId2) override;
|
||||
|
||||
AccountInfo GetAccountByName(const std::string& name) override;
|
||||
AccountInfo GetAccountByID(uint32_t id) override;
|
||||
uint32_t GetLatestCharacterOfAccount(uint32_t id) override;
|
||||
|
||||
void BanAccount(uint32_t id) override;
|
||||
void MuteAccount(uint32_t id, uint64_t muteExpireDate) override;
|
||||
|
||||
void CreatePetName(uint64_t id, const std::string& name, bool approved) override;
|
||||
|
||||
void DeletePetName(uint64_t id) override;
|
||||
|
||||
PetName GetPetName(uint64_t id) override;
|
||||
|
||||
bool IsKeyActive(uint32_t id) override;
|
||||
|
||||
uint32_t GetObjectIDTracker() override;
|
||||
|
||||
void SetObjectIDTracker(uint32_t id) override;
|
||||
|
||||
MailInfo GetMailByID(uint64_t id) override;
|
||||
std::vector<MailInfo> GetAllRecentMailOfUser(uint32_t id) override;
|
||||
uint32_t GetUnreadMailCountForUser(uint32_t id) override;
|
||||
|
||||
void WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId = 0, uint32_t attachmentLot = 0, uint64_t attachmentSubkey = 0, uint32_t attachmentCount = 0, bool wasRead = false) override;
|
||||
void SetMailAsRead(uint64_t id) override;
|
||||
void RemoveAttachmentFromMail(uint64_t id) override;
|
||||
|
||||
void DeleteMail(uint64_t id) override;
|
||||
|
||||
uint64_t GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) override;
|
||||
std::vector<uint32_t> GetBBBModlesForProperty(uint32_t propertyId) override;
|
||||
std::istream GetLXFMLFromID(uint32_t id) override;
|
||||
private:
|
||||
std::string m_Host;
|
||||
std::string m_Database;
|
||||
std::string m_Username;
|
||||
std::string m_Password;
|
||||
|
||||
sql::Connection* m_Connection;
|
||||
sql::Driver* m_Driver;
|
||||
sql::Properties m_Properties;
|
||||
};
|
||||
49
dDatabase/Databases/Structures.h
Normal file
49
dDatabase/Databases/Structures.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ePermissionMap.h"
|
||||
|
||||
struct CharacterInfo {
|
||||
uint32_t AccountID;
|
||||
uint32_t ID;
|
||||
std::string Name;
|
||||
std::string PendingName;
|
||||
bool NameRejected;
|
||||
uint32_t PropertyCloneID;
|
||||
ePermissionMap PermissionMap;
|
||||
};
|
||||
|
||||
struct AccountInfo {
|
||||
uint32_t ID;
|
||||
std::string Name;
|
||||
std::string Password;
|
||||
uint32_t MaxGMLevel;
|
||||
bool Locked;
|
||||
bool Banned;
|
||||
uint32_t PlayKeyID;
|
||||
uint64_t CreatedAt;
|
||||
uint64_t MuteExpire;
|
||||
};
|
||||
|
||||
struct PetName {
|
||||
uint64_t ID;
|
||||
std::string Name;
|
||||
bool Approved;
|
||||
};
|
||||
|
||||
struct MailInfo {
|
||||
uint64_t ID;
|
||||
uint32_t SenderID;
|
||||
std::string SenderName;
|
||||
uint32_t ReceiverID;
|
||||
std::string ReceiverName;
|
||||
uint64_t TimeSent;
|
||||
std::string Subject;
|
||||
std::string Body;
|
||||
uint32_t AttachmentID;
|
||||
uint32_t AttachmentLOT;
|
||||
uint64_t AttachmentSubkey;
|
||||
uint32_t AttachmentCount;
|
||||
bool WasRead;
|
||||
};
|
||||
@@ -1,158 +0,0 @@
|
||||
#include "MigrationRunner.h"
|
||||
|
||||
#include "BrickByBrickFix.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Database.h"
|
||||
#include "Game.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "dLogger.h"
|
||||
#include "BinaryPathFinder.h"
|
||||
|
||||
#include <istream>
|
||||
|
||||
Migration LoadMigration(std::string path) {
|
||||
Migration migration{};
|
||||
std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / path);
|
||||
|
||||
if (file.is_open()) {
|
||||
std::string line;
|
||||
std::string total = "";
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
total += line;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
migration.name = path;
|
||||
migration.data = total;
|
||||
}
|
||||
|
||||
return migration;
|
||||
}
|
||||
|
||||
void MigrationRunner::RunMigrations() {
|
||||
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
sql::SQLString finalSQL = "";
|
||||
bool runSd0Migrations = false;
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) {
|
||||
auto migration = LoadMigration("dlu/" + entry);
|
||||
|
||||
if (migration.data.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
auto* res = stmt->executeQuery();
|
||||
bool doExit = res->next();
|
||||
delete res;
|
||||
delete stmt;
|
||||
if (doExit) continue;
|
||||
|
||||
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
|
||||
if (migration.name == "dlu/5_brick_model_sd0.sql") {
|
||||
runSd0Migrations = true;
|
||||
} else {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
|
||||
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations) {
|
||||
Game::logger->Log("MigrationRunner", "Server database is up to date.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!finalSQL.empty()) {
|
||||
auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
|
||||
std::unique_ptr<sql::Statement> simpleStatement(Database::CreateStmt());
|
||||
for (auto& query : migration) {
|
||||
try {
|
||||
if (query.empty()) continue;
|
||||
simpleStatement->execute(query.c_str());
|
||||
} catch (sql::SQLException& e) {
|
||||
Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do this last on the off chance none of the other migrations have been run yet.
|
||||
if (runSd0Migrations) {
|
||||
uint32_t numberOfUpdatedModels = BrickByBrickFix::UpdateBrickByBrickModelsToSd0();
|
||||
Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels);
|
||||
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
|
||||
Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels);
|
||||
}
|
||||
}
|
||||
|
||||
void MigrationRunner::RunSQLiteMigrations() {
|
||||
auto cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);");
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
|
||||
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
|
||||
auto migration = LoadMigration("cdserver/" + entry);
|
||||
|
||||
if (migration.data.empty()) continue;
|
||||
|
||||
// Check if there is an entry in the migration history table on the cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
cdstmt.bind((int32_t) 1, migration.name.c_str());
|
||||
auto cdres = cdstmt.execQuery();
|
||||
bool doExit = !cdres.eof();
|
||||
cdres.finalize();
|
||||
cdstmt.finalize();
|
||||
|
||||
if (doExit) continue;
|
||||
|
||||
// Check first if there is entry in the migration history table on the main database.
|
||||
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
auto* res = stmt->executeQuery();
|
||||
doExit = res->next();
|
||||
delete res;
|
||||
delete stmt;
|
||||
if (doExit) {
|
||||
// Insert into cdclient database if there is an entry in the main database but not the cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
cdstmt.bind((int32_t) 1, migration.name.c_str());
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Doing these 1 migration at a time since one takes a long time and some may think it is crashing.
|
||||
// This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated".
|
||||
Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str());
|
||||
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
|
||||
for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) {
|
||||
if (dml.empty()) continue;
|
||||
try {
|
||||
CDClientDatabase::ExecuteDML(dml.c_str());
|
||||
} catch (CppSQLite3Exception& e) {
|
||||
Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// Insert into cdclient database.
|
||||
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
cdstmt.bind((int32_t) 1, migration.name.c_str());
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||
}
|
||||
|
||||
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Migration {
|
||||
std::string data;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
namespace MigrationRunner {
|
||||
void RunMigrations();
|
||||
void RunSQLiteMigrations();
|
||||
};
|
||||
@@ -1,33 +1,4 @@
|
||||
#include "CDLootTableTable.h"
|
||||
#include "CDClientManager.h"
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
// Sort the tables by their rarity so the highest rarity items are first.
|
||||
void SortTable(LootTableEntries& table) {
|
||||
auto* componentsRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
|
||||
auto* itemComponentTable = CDClientManager::Instance().GetTable<CDItemComponentTable>();
|
||||
// We modify the table in place so the outer loop keeps track of what is sorted
|
||||
// and the inner loop finds the highest rarity item and swaps it with the current position
|
||||
// of the outer loop.
|
||||
for (auto oldItrOuter = table.begin(); oldItrOuter != table.end(); oldItrOuter++) {
|
||||
auto lootToInsert = oldItrOuter;
|
||||
// Its fine if this starts at 0, even if this doesnt match lootToInsert as the actual highest will
|
||||
// either be found and overwrite these values, or the original is somehow zero and is still the highest rarity.
|
||||
uint32_t highestLootRarity = 0;
|
||||
for (auto oldItrInner = oldItrOuter; oldItrInner != table.end(); oldItrInner++) {
|
||||
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(oldItrInner->itemid, eReplicaComponentType::ITEM);
|
||||
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
|
||||
if (rarity > highestLootRarity) {
|
||||
highestLootRarity = rarity;
|
||||
lootToInsert = oldItrInner;
|
||||
}
|
||||
}
|
||||
Game::logger->LogDebug("CDLootTableTable", "highest rarity %i item id %i", highestLootRarity, lootToInsert->itemid);
|
||||
std::swap(*oldItrOuter, *lootToInsert);
|
||||
}
|
||||
}
|
||||
|
||||
CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const {
|
||||
CDLootTable entry{};
|
||||
@@ -61,9 +32,6 @@ void CDLootTableTable::LoadValuesFromDatabase() {
|
||||
this->entries[lootTableIndex].push_back(ReadRow(tableData));
|
||||
tableData.nextRow();
|
||||
}
|
||||
for (auto& [id, table] : this->entries) {
|
||||
SortTable(table);
|
||||
}
|
||||
}
|
||||
|
||||
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
|
||||
@@ -81,7 +49,6 @@ const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
|
||||
this->entries[tableId].push_back(ReadRow(tableData));
|
||||
tableData.nextRow();
|
||||
}
|
||||
SortTable(this->entries[tableId]);
|
||||
|
||||
return this->entries[tableId];
|
||||
}
|
||||
|
||||
@@ -26,39 +26,16 @@ Character::Character(uint32_t id, User* parentUser) {
|
||||
//First load the name, etc:
|
||||
m_ID = id;
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
||||
);
|
||||
// Load the character
|
||||
auto character = Database::Connection->GetCharacterInfoByID(m_ID);
|
||||
m_Name = character.Name;
|
||||
m_UnapprovedName = character.PendingName;
|
||||
m_NameRejected = character.NameRejected;
|
||||
m_PropertyCloneID = character.PropertyCloneID;
|
||||
m_PermissionMap = character.PermissionMap;
|
||||
|
||||
stmt->setInt64(1, id);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
while (res->next()) {
|
||||
m_Name = res->getString(1).c_str();
|
||||
m_UnapprovedName = res->getString(2).c_str();
|
||||
m_NameRejected = res->getBoolean(3);
|
||||
m_PropertyCloneID = res->getUInt(4);
|
||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//Load the xmlData now:
|
||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
||||
);
|
||||
|
||||
xmlStmt->setInt64(1, id);
|
||||
|
||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
||||
while (xmlRes->next()) {
|
||||
m_XMLData = xmlRes->getString(1).c_str();
|
||||
}
|
||||
|
||||
delete xmlRes;
|
||||
delete xmlStmt;
|
||||
// Load the xmlData now
|
||||
m_XMLData = Database::Connection->GetCharacterXMLByID(m_ID);
|
||||
|
||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||
@@ -85,38 +62,16 @@ Character::~Character() {
|
||||
}
|
||||
|
||||
void Character::UpdateFromDatabase() {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;"
|
||||
);
|
||||
// Load the character
|
||||
auto character = Database::Connection->GetCharacterInfoByID(m_ID);
|
||||
m_Name = character.Name;
|
||||
m_UnapprovedName = character.PendingName;
|
||||
m_NameRejected = character.NameRejected;
|
||||
m_PropertyCloneID = character.PropertyCloneID;
|
||||
m_PermissionMap = character.PermissionMap;
|
||||
|
||||
stmt->setInt64(1, m_ID);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
while (res->next()) {
|
||||
m_Name = res->getString(1).c_str();
|
||||
m_UnapprovedName = res->getString(2).c_str();
|
||||
m_NameRejected = res->getBoolean(3);
|
||||
m_PropertyCloneID = res->getUInt(4);
|
||||
m_PermissionMap = static_cast<ePermissionMap>(res->getUInt64(5));
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
//Load the xmlData now:
|
||||
sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt(
|
||||
"SELECT xml_data FROM charxml WHERE id=? LIMIT 1;"
|
||||
);
|
||||
xmlStmt->setInt64(1, m_ID);
|
||||
|
||||
sql::ResultSet* xmlRes = xmlStmt->executeQuery();
|
||||
while (xmlRes->next()) {
|
||||
m_XMLData = xmlRes->getString(1).c_str();
|
||||
}
|
||||
|
||||
delete xmlRes;
|
||||
delete xmlStmt;
|
||||
// Load the xmlData now
|
||||
m_XMLData = Database::Connection->GetCharacterXMLByID(m_ID);
|
||||
|
||||
m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing.
|
||||
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
|
||||
@@ -404,18 +359,13 @@ void Character::SetIsNewLogin() {
|
||||
}
|
||||
|
||||
void Character::WriteToDatabase() {
|
||||
//Dump our xml into m_XMLData:
|
||||
// Dump our xml into m_XMLData:
|
||||
auto* printer = new tinyxml2::XMLPrinter(0, true, 0);
|
||||
m_Doc->Print(printer);
|
||||
m_XMLData = printer->CStr();
|
||||
|
||||
//Finally, save to db:
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charxml SET xml_data=? WHERE id=?");
|
||||
stmt->setString(1, m_XMLData.c_str());
|
||||
stmt->setUInt(2, m_ID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
delete printer;
|
||||
// Save to DB
|
||||
Database::Connection->UpdateCharacterXML(m_ID, m_XMLData);
|
||||
}
|
||||
|
||||
void Character::SetPlayerFlag(const uint32_t flagId, const bool value) {
|
||||
|
||||
@@ -345,7 +345,7 @@ void Entity::Initialize() {
|
||||
int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
|
||||
int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
|
||||
|
||||
int componentID = -1;
|
||||
int componentID = 0;
|
||||
if (collectibleComponentID > 0) componentID = collectibleComponentID;
|
||||
if (rebuildComponentID > 0) componentID = rebuildComponentID;
|
||||
if (buffComponentID > 0) componentID = buffComponentID;
|
||||
@@ -353,8 +353,7 @@ void Entity::Initialize() {
|
||||
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
|
||||
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
|
||||
|
||||
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
|
||||
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
|
||||
if (buffComponentID > 0 || collectibleComponentID > 0) {
|
||||
DestroyableComponent* comp = new DestroyableComponent(this);
|
||||
if (m_Character) {
|
||||
comp->LoadFromXml(m_Character->GetXMLDoc());
|
||||
@@ -374,7 +373,6 @@ void Entity::Initialize() {
|
||||
comp->SetMaxHealth(destCompData[0].life);
|
||||
comp->SetMaxImagination(destCompData[0].imagination);
|
||||
comp->SetMaxArmor(destCompData[0].armor);
|
||||
comp->SetDeathBehavior(destCompData[0].death_behavior);
|
||||
|
||||
comp->SetIsSmashable(destCompData[0].isSmashable);
|
||||
|
||||
@@ -395,7 +393,7 @@ void Entity::Initialize() {
|
||||
}
|
||||
|
||||
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
|
||||
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
|
||||
comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs<int32_t>(u"is_smashable") != 0));
|
||||
}
|
||||
} else {
|
||||
comp->SetHealth(1);
|
||||
@@ -428,19 +426,6 @@ void Entity::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
// override the factions if needed.
|
||||
auto setFaction = GetVarAsString(u"set_faction");
|
||||
if (!setFaction.empty()) {
|
||||
// TODO also split on space here however we do not have a general util for splitting on multiple characters yet.
|
||||
std::vector<std::string> factionsToAdd = GeneralUtils::SplitString(setFaction, ';');
|
||||
int32_t factionToAdd;
|
||||
for (const auto faction : factionsToAdd) {
|
||||
if (GeneralUtils::TryParse(faction, factionToAdd)) {
|
||||
comp->AddFaction(factionToAdd, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp));
|
||||
}
|
||||
|
||||
@@ -719,12 +704,12 @@ void Entity::Initialize() {
|
||||
const Path* path = Game::zoneManager->GetZone()->GetPath(pathName);
|
||||
|
||||
//Check to see if we have an attached path and add the appropiate component to handle it:
|
||||
if (path) {
|
||||
if (path){
|
||||
// if we have a moving platform path, then we need a moving platform component
|
||||
if (path->pathType == PathType::MovingPlatform) {
|
||||
MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName);
|
||||
m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat));
|
||||
// else if we are a movement path
|
||||
// else if we are a movement path
|
||||
} /*else if (path->pathType == PathType::Movement) {
|
||||
auto movementAIcomp = GetComponent<MovementAIComponent>();
|
||||
if (movementAIcomp){
|
||||
@@ -939,20 +924,10 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
|
||||
outBitStream->Write1(); //ldf data
|
||||
|
||||
RakNet::BitStream settingStream;
|
||||
int32_t numberOfValidKeys = m_Settings.size();
|
||||
|
||||
// Writing keys value pairs the client does not expect to receive or interpret will result in undefined behavior,
|
||||
// so we need to filter out any keys that are not valid and fix the number of valid keys to be correct.
|
||||
// TODO should make this more efficient so that we dont waste loops evaluating the same condition twice
|
||||
for (LDFBaseData* data : m_Settings) {
|
||||
if (!data || data->GetValueType() == eLDFType::LDF_TYPE_UNKNOWN) {
|
||||
numberOfValidKeys--;
|
||||
}
|
||||
}
|
||||
settingStream.Write<uint32_t>(numberOfValidKeys);
|
||||
settingStream.Write<uint32_t>(m_Settings.size());
|
||||
|
||||
for (LDFBaseData* data : m_Settings) {
|
||||
if (data && data->GetValueType() != eLDFType::LDF_TYPE_UNKNOWN) {
|
||||
if (data) {
|
||||
data->WriteToPacket(&settingStream);
|
||||
}
|
||||
}
|
||||
@@ -971,6 +946,7 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
|
||||
|
||||
RakNet::BitStream settingStream;
|
||||
settingStream.Write<uint32_t>(ldfData.size());
|
||||
|
||||
for (LDFBaseData* data : ldfData) {
|
||||
if (data) {
|
||||
data->WriteToPacket(&settingStream);
|
||||
@@ -1237,7 +1213,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
|
||||
renderComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
}
|
||||
|
||||
if (modelComponent || !destroyableSerialized) {
|
||||
if (modelComponent) {
|
||||
DestroyableComponent* destroyableComponent;
|
||||
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
|
||||
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
|
||||
@@ -1576,17 +1552,7 @@ void Entity::Kill(Entity* murderer) {
|
||||
}
|
||||
|
||||
if (!IsPlayer()) {
|
||||
auto* destroyableComponent = GetComponent<DestroyableComponent>();
|
||||
bool waitForDeathAnimation = false;
|
||||
|
||||
if (destroyableComponent) {
|
||||
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0;
|
||||
}
|
||||
|
||||
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
|
||||
constexpr float DelayDeathTime = 12.0f;
|
||||
if (waitForDeathAnimation) AddCallbackTimer(DelayDeathTime, [this]() { Game::entityManager->DestroyEntity(this); });
|
||||
else Game::entityManager->DestroyEntity(this);
|
||||
Game::entityManager->DestroyEntity(this);
|
||||
}
|
||||
|
||||
const auto& grpNameQBShowBricks = GetVar<std::string>(u"grpNameQBShowBricks");
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
#include "TeamManager.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
TeamManager* TeamManager::m_Address = nullptr; //For singleton method
|
||||
|
||||
Team::Team() {
|
||||
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
|
||||
}
|
||||
|
||||
TeamManager::TeamManager() {
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
struct Team {
|
||||
Team();
|
||||
struct Team
|
||||
{
|
||||
LWOOBJID teamID = LWOOBJID_EMPTY;
|
||||
char lootOption = 0;
|
||||
std::vector<LWOOBJID> members{};
|
||||
char lootRound = 0;
|
||||
};
|
||||
|
||||
class TeamManager {
|
||||
class TeamManager
|
||||
{
|
||||
public:
|
||||
static TeamManager* Instance() {
|
||||
if (!m_Address) {
|
||||
|
||||
@@ -27,37 +27,21 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
|
||||
//This needs to be re-enabled / updated whenever the mute stuff is moved to another table.
|
||||
//This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress.
|
||||
|
||||
//sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gmlevel, mute_expire FROM accounts WHERE name=? LIMIT 1;");
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gm_level FROM accounts WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, username.c_str());
|
||||
auto account = Database::Connection->GetAccountByName(username);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
m_AccountID = res->getUInt(1);
|
||||
m_MaxGMLevel = static_cast<eGameMasterLevel>(res->getInt(2));
|
||||
m_MuteExpire = 0; //res->getUInt64(3);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
m_AccountID = account.ID;
|
||||
m_MaxGMLevel = static_cast<eGameMasterLevel>(account.MaxGMLevel);
|
||||
m_MuteExpire = 0;
|
||||
|
||||
//If we're loading a zone, we'll load the last used (aka current) character:
|
||||
if (Game::server->GetZoneID() != 0) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 1;");
|
||||
stmt->setUInt(1, m_AccountID);
|
||||
uint32_t characterId = Database::Connection->GetLatestCharacterOfAccount(m_AccountID);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
if (res->rowsCount() > 0) {
|
||||
while (res->next()) {
|
||||
LWOOBJID objID = res->getUInt64(1);
|
||||
Character* character = new Character(uint32_t(objID), this);
|
||||
m_Characters.push_back(character);
|
||||
Game::logger->Log("User", "Loaded %llu as it is the last used char", objID);
|
||||
}
|
||||
if (characterId != 0) {
|
||||
Character* character = new Character(characterId, this);
|
||||
m_Characters.push_back(character);
|
||||
Game::logger->Log("User", "Loaded %u as it is the last used char", characterId);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -159,17 +159,7 @@ void UserManager::DeletePendingRemovals() {
|
||||
}
|
||||
|
||||
bool UserManager::IsNameAvailable(const std::string& requestedName) {
|
||||
bool toReturn = false; //To allow for a clean exit
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;");
|
||||
stmt->setString(1, requestedName.c_str());
|
||||
stmt->setString(2, requestedName.c_str());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
if (res->rowsCount() == 0) toReturn = true;
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
return toReturn;
|
||||
return Database::Connection->IsCharacterNameAvailable(requestedName);
|
||||
}
|
||||
|
||||
std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) {
|
||||
@@ -201,10 +191,8 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
||||
User* u = GetUser(sysAddr);
|
||||
if (!u) return;
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;");
|
||||
stmt->setUInt(1, u->GetAccountID());
|
||||
auto charInfos = Database::Connection->GetAllCharactersByAccountID(u->GetAccountID());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
std::vector<Character*>& chars = u->GetCharacters();
|
||||
|
||||
for (size_t i = 0; i < chars.size(); ++i) {
|
||||
@@ -232,16 +220,12 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
|
||||
|
||||
chars.clear();
|
||||
|
||||
while (res->next()) {
|
||||
LWOOBJID objID = res->getUInt64(1);
|
||||
Character* character = new Character(uint32_t(objID), u);
|
||||
for (const auto& info : charInfos) {
|
||||
Character* character = new Character(info.ID, u);
|
||||
character->SetIsNewLogin();
|
||||
chars.push_back(character);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
|
||||
WorldPackets::SendCharacterList(sysAddr, u);
|
||||
}
|
||||
|
||||
@@ -290,12 +274,8 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
//Now that the name is ok, we can get an objectID from Master:
|
||||
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) {
|
||||
sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?");
|
||||
overlapStmt->setUInt(1, objectID);
|
||||
|
||||
auto* overlapResult = overlapStmt->executeQuery();
|
||||
|
||||
if (overlapResult->next()) {
|
||||
auto character = Database::Connection->GetCharacterInfoByID(objectID);
|
||||
if (character.AccountID != 0) {
|
||||
Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!");
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE);
|
||||
return;
|
||||
@@ -333,45 +313,32 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
xml3 << "</in></items></inv><lvl l=\"1\" cv=\"1\" sb=\"500\"/><flag></flag></obj>";
|
||||
|
||||
//Check to see if our name was pre-approved:
|
||||
// Check to see if our name was pre-approved
|
||||
bool nameOk = IsNamePreapproved(name);
|
||||
if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
if (u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true;
|
||||
|
||||
if (name != "") {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setUInt(2, u->GetAccountID());
|
||||
stmt->setString(3, predefinedName.c_str());
|
||||
stmt->setString(4, name.c_str());
|
||||
stmt->setBoolean(5, false);
|
||||
stmt->setUInt64(6, time(NULL));
|
||||
|
||||
if (nameOk) {
|
||||
stmt->setString(3, name.c_str());
|
||||
stmt->setString(4, "");
|
||||
}
|
||||
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->CreateCharacter(
|
||||
objectID,
|
||||
u->GetAccountID(),
|
||||
nameOk ? name : predefinedName,
|
||||
nameOk ? "" : name,
|
||||
false,
|
||||
time(NULL)
|
||||
);
|
||||
} else {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setUInt(2, u->GetAccountID());
|
||||
stmt->setString(3, predefinedName.c_str());
|
||||
stmt->setString(4, "");
|
||||
stmt->setBoolean(5, false);
|
||||
stmt->setUInt64(6, time(NULL));
|
||||
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->CreateCharacter(
|
||||
objectID,
|
||||
u->GetAccountID(),
|
||||
predefinedName,
|
||||
"",
|
||||
false,
|
||||
time(NULL)
|
||||
);
|
||||
}
|
||||
|
||||
//Now finally insert our character xml:
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)");
|
||||
stmt->setUInt(1, objectID);
|
||||
stmt->setString(2, xml3.str().c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
// Now finally insert our character xml
|
||||
Database::Connection->CreateCharacterXML(objectID, xml3.str());
|
||||
|
||||
WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
@@ -403,73 +370,13 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
|
||||
} else {
|
||||
Game::logger->Log("UserManager", "Deleting character %i", charID);
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;");
|
||||
stmt->setUInt(1, charID);
|
||||
stmt->setUInt(2, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
||||
bitStream.Write(objectID);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);"
|
||||
);
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, charID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
Database::Connection->DeleteCharacter(charID);
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
|
||||
bitStream.Write(objectID);
|
||||
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
|
||||
|
||||
WorldPackets::SendCharacterDeleteResponse(sysAddr, true);
|
||||
}
|
||||
@@ -519,23 +426,13 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
|
||||
|
||||
if (IsNameAvailable(newName)) {
|
||||
if (IsNamePreapproved(newName)) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setString(1, newName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, character->GetID());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->ApproveCharacterName(character->GetID(), newName);
|
||||
|
||||
Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
UserManager::RequestCharacterList(sysAddr);
|
||||
} else {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setString(1, newName);
|
||||
stmt->setUInt64(2, time(NULL));
|
||||
stmt->setUInt(3, character->GetID());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->SetPendingCharacterName(character->GetID(), newName);
|
||||
|
||||
Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str());
|
||||
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS);
|
||||
@@ -566,11 +463,7 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
||||
}
|
||||
|
||||
if (hasCharacter && character) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET last_login=? WHERE id=? LIMIT 1");
|
||||
stmt->setUInt64(1, time(NULL));
|
||||
stmt->setUInt(2, playerID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->UpdateCharacterLastLogin(playerID, time(NULL));
|
||||
|
||||
uint32_t zoneID = character->GetZoneID();
|
||||
if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE
|
||||
|
||||
@@ -72,7 +72,6 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
|
||||
m_ImmuneToImaginationLossCount = 0;
|
||||
m_ImmuneToQuickbuildInterruptCount = 0;
|
||||
m_ImmuneToPullToPointCount = 0;
|
||||
m_DeathBehavior = -1;
|
||||
}
|
||||
|
||||
DestroyableComponent::~DestroyableComponent() {
|
||||
|
||||
@@ -416,9 +416,6 @@ public:
|
||||
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
|
||||
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
|
||||
|
||||
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
|
||||
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
|
||||
|
||||
/**
|
||||
* Utility to reset all stats to the default stats based on items and completed missions
|
||||
*/
|
||||
@@ -600,11 +597,6 @@ private:
|
||||
uint32_t m_ImmuneToImaginationLossCount;
|
||||
uint32_t m_ImmuneToQuickbuildInterruptCount;
|
||||
uint32_t m_ImmuneToPullToPointCount;
|
||||
|
||||
/**
|
||||
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
|
||||
*/
|
||||
int32_t m_DeathBehavior;
|
||||
};
|
||||
|
||||
#endif // DESTROYABLECOMPONENT_H
|
||||
|
||||
@@ -1083,44 +1083,23 @@ PetComponent::~PetComponent() {
|
||||
}
|
||||
|
||||
void PetComponent::SetPetNameForModeration(const std::string& petName) {
|
||||
int approved = 1; //default, in mod
|
||||
int approved = 1; // default, in mod
|
||||
|
||||
//Make sure that the name isn't already auto-approved:
|
||||
// Make sure that the name isn't already auto-approved
|
||||
if (Game::chatFilter->IsSentenceOkay(petName, eGameMasterLevel::CIVILIAN).empty()) {
|
||||
approved = 2; //approved
|
||||
approved = 2;
|
||||
}
|
||||
|
||||
auto deleteStmt = Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;");
|
||||
deleteStmt->setUInt64(1, m_DatabaseId);
|
||||
Database::Connection->DeletePetName(m_DatabaseId);
|
||||
|
||||
deleteStmt->execute();
|
||||
|
||||
delete deleteStmt;
|
||||
|
||||
//Save to db:
|
||||
auto stmt = Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);");
|
||||
stmt->setUInt64(1, m_DatabaseId);
|
||||
stmt->setString(2, petName);
|
||||
stmt->setInt(3, approved);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
// Save to db
|
||||
Database::Connection->CreatePetName(m_DatabaseId, petName, approved);
|
||||
}
|
||||
|
||||
void PetComponent::LoadPetNameFromModeration() {
|
||||
auto stmt = Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;");
|
||||
stmt->setUInt64(1, m_DatabaseId);
|
||||
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
m_ModerationStatus = res->getInt(2);
|
||||
|
||||
if (m_ModerationStatus == 2) {
|
||||
m_Name = res->getString(1);
|
||||
}
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
auto petNameInfo = Database::Connection->GetPetName(m_DatabaseId);
|
||||
m_ModerationStatus = petNameInfo.Approved;
|
||||
m_Name = petNameInfo.Name;
|
||||
}
|
||||
|
||||
void PetComponent::SetPreconditions(std::string& preconditions) {
|
||||
|
||||
@@ -215,7 +215,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon
|
||||
m_dpEntity->SetRotation(m_Rotation);
|
||||
m_dpEntity->SetPosition(m_Position);
|
||||
dpWorld::Instance().AddEntity(m_dpEntity);
|
||||
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") {
|
||||
} else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx"){
|
||||
m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true);
|
||||
m_dpEntity->SetScale(m_Scale);
|
||||
m_dpEntity->SetRotation(m_Rotation);
|
||||
@@ -336,37 +336,11 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
|
||||
}
|
||||
}
|
||||
|
||||
// Even if we were to implement Friction server side,
|
||||
// it also defaults to 1.0f in the last argument, so we dont need two functions to do the same thing.
|
||||
void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effectType, const float effectScale) {
|
||||
switch (effectType) {
|
||||
case ePhysicsEffectType::GRAVITY_SCALE: {
|
||||
auto* targetEntity = Game::entityManager->GetEntity(target);
|
||||
if (targetEntity) {
|
||||
auto* controllablePhysicsComponent = targetEntity->GetComponent<ControllablePhysicsComponent>();
|
||||
// dont want to apply an effect to nothing.
|
||||
if (!controllablePhysicsComponent) return;
|
||||
controllablePhysicsComponent->SetGravityScale(effectScale);
|
||||
GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress());
|
||||
}
|
||||
}
|
||||
// The other types are not handled by the server
|
||||
case ePhysicsEffectType::ATTRACT:
|
||||
case ePhysicsEffectType::FRICTION:
|
||||
case ePhysicsEffectType::PUSH:
|
||||
case ePhysicsEffectType::REPULSE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::Update(float deltaTime) {
|
||||
if (!m_dpEntity) return;
|
||||
|
||||
//Process enter events
|
||||
for (auto en : m_dpEntity->GetNewObjects()) {
|
||||
if (!en) continue;
|
||||
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, m_DirectionalMultiplier);
|
||||
m_Parent->OnCollisionPhantom(en->GetObjectID());
|
||||
|
||||
//If we are a respawn volume, inform the client:
|
||||
@@ -383,8 +357,6 @@ void PhantomPhysicsComponent::Update(float deltaTime) {
|
||||
|
||||
//Process exit events
|
||||
for (auto en : m_dpEntity->GetRemovedObjects()) {
|
||||
if (!en) continue;
|
||||
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, 1.0f);
|
||||
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -943,7 +943,7 @@ void GameMessages::SendResurrect(Entity* entity) {
|
||||
destroyableComponent->SetImagination(imaginationToRestore);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
auto cont = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS));
|
||||
@@ -2964,14 +2964,14 @@ void GameMessages::SendSetStunned(LWOOBJID objectId, eStateChangeType stateChang
|
||||
}
|
||||
|
||||
void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr,
|
||||
LWOOBJID originator,
|
||||
bool bImmuneToStunAttack,
|
||||
bool bImmuneToStunEquip,
|
||||
bool bImmuneToStunInteract,
|
||||
bool bImmuneToStunJump,
|
||||
bool bImmuneToStunMove,
|
||||
bool bImmuneToStunTurn,
|
||||
bool bImmuneToStunUseItem) {
|
||||
LWOOBJID originator,
|
||||
bool bImmuneToStunAttack,
|
||||
bool bImmuneToStunEquip,
|
||||
bool bImmuneToStunInteract,
|
||||
bool bImmuneToStunJump,
|
||||
bool bImmuneToStunMove,
|
||||
bool bImmuneToStunTurn,
|
||||
bool bImmuneToStunUseItem) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
@@ -2996,15 +2996,15 @@ void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state,
|
||||
}
|
||||
|
||||
void GameMessages::SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr,
|
||||
bool bImmuneToBasicAttack,
|
||||
bool bImmuneToDamageOverTime,
|
||||
bool bImmuneToKnockback,
|
||||
bool bImmuneToInterrupt,
|
||||
bool bImmuneToSpeed,
|
||||
bool bImmuneToImaginationGain,
|
||||
bool bImmuneToImaginationLoss,
|
||||
bool bImmuneToQuickbuildInterrupt,
|
||||
bool bImmuneToPullToPoint) {
|
||||
bool bImmuneToBasicAttack,
|
||||
bool bImmuneToDamageOverTime,
|
||||
bool bImmuneToKnockback,
|
||||
bool bImmuneToInterrupt,
|
||||
bool bImmuneToSpeed,
|
||||
bool bImmuneToImaginationGain,
|
||||
bool bImmuneToImaginationLoss,
|
||||
bool bImmuneToQuickbuildInterrupt,
|
||||
bool bImmuneToPullToPoint) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
@@ -5473,18 +5473,6 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En
|
||||
}
|
||||
}
|
||||
|
||||
void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) {
|
||||
CBITSTREAM;
|
||||
CMSGHEADER;
|
||||
|
||||
bitStream.Write(target);
|
||||
bitStream.Write(eGameMessageType::SET_GRAVITY_SCALE);
|
||||
|
||||
bitStream.Write(effectScale);
|
||||
|
||||
SEND_PACKET;
|
||||
}
|
||||
|
||||
void GameMessages::HandleMoveItemInInventory(RakNet::BitStream* inStream, Entity* entity) {
|
||||
bool destInvTypeIsDefault = false;
|
||||
int32_t destInvType = eInventoryType::INVALID;
|
||||
@@ -6281,7 +6269,7 @@ void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, En
|
||||
auto* donationEntity = Game::entityManager->GetEntity(characterComponent->GetCurrentInteracting());
|
||||
if (!donationEntity) return;
|
||||
auto* donationVendorComponent = donationEntity->GetComponent<DonationVendorComponent>();
|
||||
if (!donationVendorComponent) return;
|
||||
if(!donationVendorComponent) return;
|
||||
if (donationVendorComponent->GetActivityID() == 0) {
|
||||
Game::logger->Log("GameMessages", "WARNING: Trying to dontate to a vendor with no activity");
|
||||
return;
|
||||
@@ -6291,7 +6279,7 @@ void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, En
|
||||
auto items = inventory->GetItems();
|
||||
if (!items.empty()) {
|
||||
uint32_t count = 0;
|
||||
for (auto& [itemID, item] : items) {
|
||||
for (auto& [itemID, item] : items){
|
||||
count += item->GetCount();
|
||||
item->RemoveFromInventory();
|
||||
}
|
||||
@@ -6309,7 +6297,7 @@ void GameMessages::HandleCancelDonationOnPlayer(RakNet::BitStream* inStream, Ent
|
||||
auto* inventory = inventoryComponent->GetInventory(eInventoryType::DONATION);
|
||||
if (!inventory) return;
|
||||
auto items = inventory->GetItems();
|
||||
for (auto& [itemID, item] : items) {
|
||||
for (auto& [itemID, item] : items){
|
||||
inventoryComponent->MoveItemToInventory(item, eInventoryType::BRICKS, item->GetCount(), false, false, true);
|
||||
}
|
||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||
|
||||
@@ -109,8 +109,6 @@ namespace GameMessages {
|
||||
|
||||
void SendSetInventorySize(Entity* entity, int invType, int size);
|
||||
|
||||
void SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr);
|
||||
|
||||
void SendSetEmoteLockState(Entity* entity, bool bLock, int emoteID);
|
||||
void SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks = false, bool doHover = false, int effectID = -1, float airspeed = 10, float maxAirspeed = 15, float verticalVelocity = 1, int warningEffectID = -1);
|
||||
void SendResurrect(Entity* entity);
|
||||
|
||||
@@ -76,22 +76,7 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, const
|
||||
void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJID recipient,
|
||||
const std::string& recipientName, const std::string& subject, const std::string& body, const LOT attachment,
|
||||
const uint16_t attachmentCount, const SystemAddress& sysAddr) {
|
||||
auto* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)");
|
||||
|
||||
ins->setUInt(1, sender);
|
||||
ins->setString(2, senderName.c_str());
|
||||
ins->setUInt(3, recipient);
|
||||
ins->setString(4, recipientName.c_str());
|
||||
ins->setUInt64(5, time(nullptr));
|
||||
ins->setString(6, subject.c_str());
|
||||
ins->setString(7, body.c_str());
|
||||
ins->setUInt(8, 0);
|
||||
ins->setInt(9, attachment);
|
||||
ins->setInt(10, 0);
|
||||
ins->setInt(11, attachmentCount);
|
||||
ins->execute();
|
||||
|
||||
delete ins;
|
||||
Database::Connection->WriteMail(sender, senderName, recipient, recipientName, time(nullptr), subject, body, 0, attachment, 0, attachmentCount, false);
|
||||
|
||||
if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server
|
||||
|
||||
@@ -220,43 +205,20 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd
|
||||
}
|
||||
|
||||
//Get the receiver's id:
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id from charinfo WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, recipient);
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
uint32_t receiverID = 0;
|
||||
auto recipientInfo = Database::Connection->GetCharacterInfoByName(recipient);
|
||||
uint32_t receiverID = recipientInfo.ID;
|
||||
|
||||
if (res->rowsCount() > 0) {
|
||||
while (res->next()) receiverID = res->getUInt(1);
|
||||
} else {
|
||||
if (receiverID == 0) {
|
||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::RecipientNotFound);
|
||||
delete stmt;
|
||||
delete res;
|
||||
return;
|
||||
}
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
|
||||
//Check if we have a valid receiver:
|
||||
if (GeneralUtils::CaseInsensitiveStringCompare(recipient, character->GetName()) || receiverID == character->GetObjectID()) {
|
||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::CannotMailSelf);
|
||||
return;
|
||||
} else {
|
||||
uint64_t currentTime = time(NULL);
|
||||
sql::PreparedStatement* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)");
|
||||
ins->setUInt(1, character->GetObjectID());
|
||||
ins->setString(2, character->GetName());
|
||||
ins->setUInt(3, receiverID);
|
||||
ins->setString(4, recipient);
|
||||
ins->setUInt64(5, currentTime);
|
||||
ins->setString(6, subject);
|
||||
ins->setString(7, body);
|
||||
ins->setUInt(8, itemID);
|
||||
ins->setInt(9, itemLOT);
|
||||
ins->setInt(10, 0);
|
||||
ins->setInt(11, attachmentCount);
|
||||
ins->execute();
|
||||
delete ins;
|
||||
Database::Connection->WriteMail(character->GetObjectID(), character->GetName(), receiverID, recipient, time(nullptr), subject, body, itemID, itemLOT, 0, attachmentCount, false);
|
||||
}
|
||||
|
||||
Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success);
|
||||
@@ -279,57 +241,45 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd
|
||||
}
|
||||
|
||||
void Mail::HandleDataRequest(RakNet::BitStream* packet, const SystemAddress& sysAddr, Entity* player) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM mail WHERE receiver_id=? limit 20;");
|
||||
stmt->setUInt(1, player->GetCharacter()->GetObjectID());
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
auto mail = Database::Connection->GetAllRecentMailOfUser(player->GetCharacter()->GetObjectID());
|
||||
|
||||
RakNet::BitStream bitStream;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL);
|
||||
bitStream.Write(int(MailMessageID::MailData));
|
||||
bitStream.Write(int(0));
|
||||
|
||||
bitStream.Write(uint16_t(res->rowsCount()));
|
||||
bitStream.Write(uint16_t(mail.size()));
|
||||
bitStream.Write(uint16_t(0));
|
||||
|
||||
if (res->rowsCount() > 0) {
|
||||
while (res->next()) {
|
||||
bitStream.Write(res->getUInt64(1)); //MailID
|
||||
for (const auto& mailInfo : mail) {
|
||||
bitStream.Write(mailInfo.ID); //MailID
|
||||
|
||||
/*std::u16string subject = GeneralUtils::UTF8ToUTF16(res->getString(7));
|
||||
std::u16string body = GeneralUtils::UTF8ToUTF16(res->getString(8));
|
||||
std::u16string sender = GeneralUtils::UTF8ToUTF16(res->getString(3));
|
||||
WriteStringAsWString(&bitStream, mailInfo.Subject, 50); //subject
|
||||
WriteStringAsWString(&bitStream, mailInfo.Body, 400); //body
|
||||
WriteStringAsWString(&bitStream, mailInfo.SenderName, 32); //sender
|
||||
|
||||
WriteToPacket(&bitStream, subject, 50);
|
||||
WriteToPacket(&bitStream, body, 400);
|
||||
WriteToPacket(&bitStream, sender, 32);*/
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint64_t(0));
|
||||
|
||||
WriteStringAsWString(&bitStream, res->getString(7).c_str(), 50); //subject
|
||||
WriteStringAsWString(&bitStream, res->getString(8).c_str(), 400); //body
|
||||
WriteStringAsWString(&bitStream, res->getString(3).c_str(), 32); //sender
|
||||
bitStream.Write(mailInfo.AttachmentID); //Attachment ID
|
||||
LOT lot = mailInfo.AttachmentLOT;
|
||||
if (lot <= 0) bitStream.Write(LOT(-1));
|
||||
else bitStream.Write(lot);
|
||||
bitStream.Write(uint32_t(0));
|
||||
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint64_t(0));
|
||||
bitStream.Write(mailInfo.AttachmentSubkey); //Attachment subKey
|
||||
bitStream.Write(uint16_t(mailInfo.AttachmentCount)); //Attachment count
|
||||
|
||||
bitStream.Write(res->getUInt64(9)); //Attachment ID
|
||||
LOT lot = res->getInt(10);
|
||||
if (lot <= 0) bitStream.Write(LOT(-1));
|
||||
else bitStream.Write(lot);
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
|
||||
bitStream.Write(res->getInt64(11)); //Attachment subKey
|
||||
bitStream.Write(uint16_t(res->getInt(12))); //Attachment count
|
||||
bitStream.Write(uint64_t(mailInfo.TimeSent)); //time sent (twice?)
|
||||
bitStream.Write(uint64_t(mailInfo.TimeSent));
|
||||
bitStream.Write(uint8_t(mailInfo.WasRead)); //was read
|
||||
|
||||
bitStream.Write(uint32_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
|
||||
bitStream.Write(uint64_t(res->getUInt64(6))); //time sent (twice?)
|
||||
bitStream.Write(uint64_t(res->getUInt64(6)));
|
||||
bitStream.Write(uint8_t(res->getBoolean(13))); //was read
|
||||
|
||||
bitStream.Write(uint8_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
bitStream.Write(uint32_t(0));
|
||||
}
|
||||
bitStream.Write(uint8_t(0));
|
||||
bitStream.Write(uint16_t(0));
|
||||
bitStream.Write(uint32_t(0));
|
||||
}
|
||||
|
||||
Game::server->Send(&bitStream, sysAddr, false);
|
||||
@@ -345,31 +295,17 @@ void Mail::HandleAttachmentCollect(RakNet::BitStream* packet, const SystemAddres
|
||||
packet->Read(playerID);
|
||||
|
||||
if (mailID > 0 && playerID == player->GetObjectID()) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, mailID);
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
LOT attachmentLOT = 0;
|
||||
uint32_t attachmentCount = 0;
|
||||
|
||||
while (res->next()) {
|
||||
attachmentLOT = res->getInt(1);
|
||||
attachmentCount = res->getInt(2);
|
||||
}
|
||||
auto mailInfo = Database::Connection->GetMailByID(mailID);
|
||||
if (mailInfo.ID == 0) return;
|
||||
|
||||
auto inv = static_cast<InventoryComponent*>(player->GetComponent(eReplicaComponentType::INVENTORY));
|
||||
if (!inv) return;
|
||||
|
||||
inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL);
|
||||
inv->AddItem(mailInfo.AttachmentLOT, mailInfo.AttachmentCount, eLootSourceType::MAIL);
|
||||
|
||||
Mail::SendAttachmentRemoveConfirm(sysAddr, mailID);
|
||||
|
||||
sql::PreparedStatement* up = Database::CreatePreppedStmt("UPDATE mail SET attachment_lot=0 WHERE id=?;");
|
||||
up->setUInt64(1, mailID);
|
||||
up->execute();
|
||||
delete up;
|
||||
delete res;
|
||||
delete stmt;
|
||||
Database::Connection->RemoveAttachmentFromMail(mailID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,14 +331,10 @@ void Mail::HandleMailRead(RakNet::BitStream* packet, const SystemAddress& sysAdd
|
||||
|
||||
void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID) {
|
||||
auto returnVal = std::async(std::launch::async, [&]() {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM mail WHERE receiver_id=? AND was_read=0");
|
||||
stmt->setUInt(1, objectID);
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
auto unreadCount = Database::Connection->GetUnreadMailCountForUser(objectID);
|
||||
|
||||
if (res->rowsCount() > 0) Mail::SendNotification(sysAddr, res->rowsCount());
|
||||
delete res;
|
||||
delete stmt;
|
||||
});
|
||||
if (unreadCount > 0) Mail::SendNotification(sysAddr, unreadCount);
|
||||
});
|
||||
}
|
||||
|
||||
void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) {
|
||||
@@ -449,10 +381,7 @@ void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOO
|
||||
bitStream.Write(mailID);
|
||||
Game::server->Send(&bitStream, sysAddr, false);
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE id=? LIMIT 1;");
|
||||
stmt->setUInt64(1, mailID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->DeleteMail(mailID);
|
||||
}
|
||||
|
||||
void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
|
||||
@@ -463,8 +392,5 @@ void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) {
|
||||
bitStream.Write(mailID);
|
||||
Game::server->Send(&bitStream, sysAddr, false);
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE mail SET was_read=1 WHERE id=?");
|
||||
stmt->setUInt64(1, mailID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->SetMailAsRead(mailID);
|
||||
}
|
||||
|
||||
@@ -360,11 +360,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
}
|
||||
|
||||
// Log command to database
|
||||
auto stmt = Database::CreatePreppedStmt("INSERT INTO command_log (character_id, command) VALUES (?, ?);");
|
||||
stmt->setInt(1, entity->GetCharacter()->GetID());
|
||||
stmt->setString(2, GeneralUtils::UTF16ToWTF8(command).c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->InsertIntoCommandLog(entity->GetCharacter()->GetID(), GeneralUtils::UTF16ToWTF8(command));
|
||||
|
||||
if (chatCommand == "setminifig" && args.size() == 2 && entity->GetGMLevel() >= eGameMasterLevel::FORUM_MODERATOR) { // could break characters so only allow if GM > 0
|
||||
int32_t minifigItemId;
|
||||
@@ -816,18 +812,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
|
||||
if (chatCommand == "mailitem" && entity->GetGMLevel() >= eGameMasterLevel::MODERATOR && args.size() >= 2) {
|
||||
const auto& playerName = args[0];
|
||||
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id from charinfo WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, playerName);
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
uint32_t receiverID = 0;
|
||||
|
||||
if (res->rowsCount() > 0) {
|
||||
while (res->next()) receiverID = res->getUInt(1);
|
||||
}
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
|
||||
auto character = Database::Connection->GetCharacterInfoByName(playerName);
|
||||
uint32_t receiverID = character.AccountID;
|
||||
|
||||
if (receiverID == 0) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player");
|
||||
@@ -842,21 +829,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t currentTime = time(NULL);
|
||||
sql::PreparedStatement* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)");
|
||||
ins->setUInt(1, entity->GetObjectID());
|
||||
ins->setString(2, "Darkflame Universe");
|
||||
ins->setUInt(3, receiverID);
|
||||
ins->setString(4, playerName);
|
||||
ins->setUInt64(5, currentTime);
|
||||
ins->setString(6, "Lost item");
|
||||
ins->setString(7, "This is a replacement item for one you lost.");
|
||||
ins->setUInt(8, 0);
|
||||
ins->setInt(9, lot);
|
||||
ins->setInt(10, 0);
|
||||
ins->setInt(11, 1);
|
||||
ins->execute();
|
||||
delete ins;
|
||||
Database::Connection->WriteMail(entity->GetObjectID(), "Darkflame Universe", receiverID, playerName, time(nullptr), "Lost item", "This is a replacement item for one you lost.", 0, lot, 0, 1);
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Mail sent");
|
||||
|
||||
@@ -1016,26 +989,15 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
LWOOBJID characterId = 0;
|
||||
|
||||
if (player == nullptr) {
|
||||
auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id, id FROM charinfo WHERE name=? LIMIT 1;");
|
||||
auto character = Database::Connection->GetCharacterInfoByName(args[0]);
|
||||
|
||||
accountQuery->setString(1, args[0]);
|
||||
|
||||
auto result = accountQuery->executeQuery();
|
||||
|
||||
if (result->rowsCount() > 0) {
|
||||
while (result->next()) {
|
||||
accountId = result->getUInt(1);
|
||||
characterId = result->getUInt64(2);
|
||||
|
||||
GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT);
|
||||
}
|
||||
}
|
||||
|
||||
delete accountQuery;
|
||||
delete result;
|
||||
|
||||
if (accountId == 0) {
|
||||
if (accountId != 0) {
|
||||
accountId = character.AccountID;
|
||||
characterId = character.ID;
|
||||
|
||||
GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT);
|
||||
} else {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
||||
|
||||
return;
|
||||
@@ -1045,8 +1007,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
characterId = player->GetCharacter()->GetID();
|
||||
}
|
||||
|
||||
auto* userUpdate = Database::CreatePreppedStmt("UPDATE accounts SET mute_expire = ? WHERE id = ?;");
|
||||
|
||||
time_t expire = 1; // Default to indefinate mute
|
||||
|
||||
if (args.size() >= 2) {
|
||||
@@ -1071,12 +1031,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
expire += 60 * 60 * hours;
|
||||
}
|
||||
|
||||
userUpdate->setUInt64(1, expire);
|
||||
userUpdate->setInt(2, accountId);
|
||||
|
||||
userUpdate->executeUpdate();
|
||||
|
||||
delete userUpdate;
|
||||
Database::Connection->MuteAccount(accountId, expire);
|
||||
|
||||
char buffer[32] = "brought up for review.\0";
|
||||
|
||||
@@ -1128,20 +1083,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
uint32_t accountId = 0;
|
||||
|
||||
if (player == nullptr) {
|
||||
auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id FROM charinfo WHERE name=? LIMIT 1;");
|
||||
auto character = Database::Connection->GetCharacterInfoByName(args[0]);
|
||||
accountId = character.AccountID;
|
||||
|
||||
accountQuery->setString(1, args[0]);
|
||||
|
||||
auto result = accountQuery->executeQuery();
|
||||
|
||||
if (result->rowsCount() > 0) {
|
||||
while (result->next()) accountId = result->getUInt(1);
|
||||
}
|
||||
|
||||
delete accountQuery;
|
||||
delete result;
|
||||
|
||||
if (accountId == 0) {
|
||||
if (character.AccountID == 0) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0]));
|
||||
|
||||
return;
|
||||
@@ -1150,13 +1095,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
|
||||
accountId = player->GetParentUser()->GetAccountID();
|
||||
}
|
||||
|
||||
auto* userUpdate = Database::CreatePreppedStmt("UPDATE accounts SET banned = true WHERE id = ?;");
|
||||
|
||||
userUpdate->setInt(1, accountId);
|
||||
|
||||
userUpdate->executeUpdate();
|
||||
|
||||
delete userUpdate;
|
||||
Database::Connection->BanAccount(accountId);
|
||||
|
||||
if (player != nullptr) {
|
||||
Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED);
|
||||
|
||||
@@ -129,19 +129,7 @@ int main(int argc, char** argv) {
|
||||
Game::logger->Log("MasterServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
|
||||
Game::logger->Log("MasterServer", "Compiled on: %s", __TIMESTAMP__);
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("MasterServer", "Got an error while connecting to the database: %s", ex.what());
|
||||
Game::logger->Log("MigrationRunner", "Migrations not run");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Database::Connect(Game::config);
|
||||
|
||||
try {
|
||||
std::string clientPathStr = Game::config->GetValue("client_location");
|
||||
@@ -313,8 +301,7 @@ int main(int argc, char** argv) {
|
||||
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown);
|
||||
|
||||
//Query for the database for a server labeled "master"
|
||||
auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'");
|
||||
auto* result = masterLookupStatement->executeQuery();
|
||||
auto masterServerSock = Database::Connection->GetMasterServerIP();
|
||||
|
||||
auto master_server_ip = Game::config->GetValue("master_ip");
|
||||
|
||||
@@ -323,20 +310,11 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
//If we found a server, update it's IP and port to the current one.
|
||||
if (result->next()) {
|
||||
auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?");
|
||||
updateStatement->setString(1, master_server_ip.c_str());
|
||||
updateStatement->setInt(2, Game::server->GetPort());
|
||||
updateStatement->setInt(3, result->getInt("id"));
|
||||
updateStatement->execute();
|
||||
delete updateStatement;
|
||||
if (masterServerSock.port != 0) {
|
||||
Database::Connection->SetServerIpAndPortByName("master", master_server_ip, Game::server->GetPort());
|
||||
} else {
|
||||
//If we didn't find a server, create one.
|
||||
auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)");
|
||||
insertStatement->setString(1, master_server_ip.c_str());
|
||||
insertStatement->setInt(2, Game::server->GetPort());
|
||||
insertStatement->execute();
|
||||
delete insertStatement;
|
||||
// If we didn't find a server, create one.
|
||||
Database::Connection->CreateServer("master", master_server_ip, Game::server->GetPort(), 0, 171023);
|
||||
}
|
||||
|
||||
//Create additional objects here:
|
||||
@@ -382,18 +360,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
Database::Connection->GetMasterServerIP();
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else
|
||||
@@ -973,8 +940,8 @@ void ShutdownSequence(int32_t signal) {
|
||||
}
|
||||
|
||||
int32_t FinalizeShutdown(int32_t signal) {
|
||||
//Delete our objects here:
|
||||
Database::Destroy("MasterServer");
|
||||
// Delete our objects here
|
||||
Database::Destroy();
|
||||
if (Game::config) delete Game::config;
|
||||
if (Game::im) delete Game::im;
|
||||
if (Game::server) delete Game::server;
|
||||
|
||||
@@ -10,62 +10,18 @@ ObjectIDManager* ObjectIDManager::m_Address = nullptr;
|
||||
//! Initializes the manager
|
||||
void ObjectIDManager::Initialize(dLogger* logger) {
|
||||
this->mLogger = logger;
|
||||
this->currentPersistentID = 1;
|
||||
|
||||
try {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"SELECT last_object_id FROM object_id_tracker");
|
||||
|
||||
sql::ResultSet* result = stmt->executeQuery();
|
||||
auto next = result->next();
|
||||
|
||||
if (!next) {
|
||||
sql::PreparedStatement* insertStmt = Database::CreatePreppedStmt(
|
||||
"INSERT INTO object_id_tracker VALUES (1)");
|
||||
|
||||
insertStmt->execute();
|
||||
|
||||
delete insertStmt;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (next) {
|
||||
this->currentPersistentID =
|
||||
result->getInt(1) > 0 ? result->getInt(1) : 1;
|
||||
next = result->next();
|
||||
}
|
||||
|
||||
delete result;
|
||||
delete stmt;
|
||||
} catch (sql::SQLException& e) {
|
||||
mLogger->Log("ObjectIDManager", "Unable to fetch max persistent object "
|
||||
"ID in use. Defaulting to 1.");
|
||||
mLogger->Log("ObjectIDManager", "SQL error: %s", e.what());
|
||||
this->currentPersistentID = 1;
|
||||
}
|
||||
this->currentPersistentID = Database::Connection->GetObjectIDTracker();
|
||||
}
|
||||
|
||||
//! Generates a new persistent ID
|
||||
uint32_t ObjectIDManager::GeneratePersistentID(void) {
|
||||
uint32_t toReturn = ++this->currentPersistentID;
|
||||
|
||||
// So we peroidically save our ObjID to the database:
|
||||
// if (toReturn % 25 == 0) { // TEMP: DISABLED FOR DEBUG / DEVELOPMENT!
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"UPDATE object_id_tracker SET last_object_id=?");
|
||||
stmt->setUInt(1, toReturn);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
// }
|
||||
Database::Connection->SetObjectIDTracker(toReturn);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
void ObjectIDManager::SaveToDatabase() {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt(
|
||||
"UPDATE object_id_tracker SET last_object_id=?");
|
||||
stmt->setUInt(1, currentPersistentID);
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
Database::Connection->SetObjectIDTracker(this->currentPersistentID);
|
||||
}
|
||||
|
||||
@@ -64,122 +64,57 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) {
|
||||
const char* szUsername = username.c_str();
|
||||
|
||||
// Fetch account details
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT password, banned, locked, play_key_id, gm_level FROM accounts WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, szUsername);
|
||||
auto account = Database::Connection->GetAccountByName(username);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
if (res->rowsCount() == 0) {
|
||||
if (account.ID == 0) {
|
||||
server->GetLogger()->Log("AuthPackets", "No user found!");
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string sqlPass = "";
|
||||
bool sqlBanned = false;
|
||||
bool sqlLocked = false;
|
||||
uint32_t sqlPlayKey = 0;
|
||||
uint32_t sqlGmLevel = 0;
|
||||
|
||||
while (res->next()) {
|
||||
sqlPass = res->getString(1).c_str();
|
||||
sqlBanned = res->getBoolean(2);
|
||||
sqlLocked = res->getBoolean(3);
|
||||
sqlPlayKey = res->getInt(4);
|
||||
sqlGmLevel = res->getInt(5);
|
||||
}
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
|
||||
|
||||
//If we aren't running in live mode, then only GMs are allowed to enter:
|
||||
const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs");
|
||||
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && sqlGmLevel == 0) {
|
||||
if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && account.MaxGMLevel == 0) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Game::config->GetValue("dont_use_keys") != "1") {
|
||||
//Check to see if we have a play key:
|
||||
if (sqlPlayKey == 0 && sqlGmLevel == 0) {
|
||||
// Check to see if we have a play key
|
||||
if (account.PlayKeyID == 0 && account.MaxGMLevel == 0) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
||||
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but they don't have a play key.", username.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if the play key is _valid_:
|
||||
auto keyCheckStmt = Database::CreatePreppedStmt("SELECT active FROM `play_keys` WHERE id=?");
|
||||
keyCheckStmt->setInt(1, sqlPlayKey);
|
||||
auto keyRes = keyCheckStmt->executeQuery();
|
||||
bool isKeyActive = false;
|
||||
// Check if the play key is _valid_
|
||||
bool isKeyActive = Database::Connection->IsKeyActive(account.PlayKeyID);
|
||||
|
||||
if (keyRes->rowsCount() == 0 && sqlGmLevel == 0) {
|
||||
if (!isKeyActive && account.MaxGMLevel == 0) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username);
|
||||
return;
|
||||
}
|
||||
|
||||
while (keyRes->next()) {
|
||||
isKeyActive = (bool)keyRes->getInt(1);
|
||||
}
|
||||
|
||||
if (!isKeyActive && sqlGmLevel == 0) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username);
|
||||
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but their play key was disabled", username.c_str());
|
||||
server->GetLogger()->Log("AuthPackets", "User %s tried to log in, but they lacked a play key", username.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sqlBanned) {
|
||||
if (account.Banned) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return;
|
||||
}
|
||||
|
||||
if (sqlLocked) {
|
||||
if (account.Locked) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updated hashing method:
|
||||
* First attempt bcrypt.
|
||||
* If that fails, fallback to old method and setup bcrypt for new login.
|
||||
*/
|
||||
|
||||
bool loginSuccess = true;
|
||||
|
||||
int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), sqlPass.c_str());
|
||||
|
||||
if (bcryptState != 0) {
|
||||
// Fallback on old method
|
||||
|
||||
std::string oldPassword = sha512(password + username);
|
||||
|
||||
if (sqlPass != oldPassword) {
|
||||
loginSuccess = false;
|
||||
} else {
|
||||
// Generate new hash for bcrypt
|
||||
|
||||
char salt[BCRYPT_HASHSIZE];
|
||||
char hash[BCRYPT_HASHSIZE];
|
||||
|
||||
bcryptState = ::bcrypt_gensalt(12, salt);
|
||||
|
||||
assert(bcryptState == 0);
|
||||
|
||||
bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash);
|
||||
|
||||
assert(bcryptState == 0);
|
||||
|
||||
sql::PreparedStatement* accountUpdate = Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE name = ? LIMIT 1;");
|
||||
|
||||
accountUpdate->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str());
|
||||
accountUpdate->setString(2, szUsername);
|
||||
|
||||
accountUpdate->executeUpdate();
|
||||
if (!account.Password.empty()) {
|
||||
if (account.Password[0] != '$') {
|
||||
Game::logger->Log("AuthPackets", "Invalid password being parsed for user %s", username.c_str());
|
||||
}
|
||||
} else {
|
||||
// Login success with bcrypt
|
||||
}
|
||||
|
||||
if (!loginSuccess) {
|
||||
int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), account.Password.c_str());
|
||||
|
||||
if (bcryptState != 0) {
|
||||
AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username);
|
||||
server->GetLogger()->Log("AuthPackets", "Wrong password used");
|
||||
} else {
|
||||
|
||||
@@ -351,41 +351,17 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa
|
||||
// Private chat
|
||||
LWOOBJID idOfReceiver = LWOOBJID_EMPTY;
|
||||
|
||||
{
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE name = ?");
|
||||
stmt->setString(1, receiver);
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
if (res->next()) {
|
||||
idOfReceiver = res->getInt("id");
|
||||
}
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
}
|
||||
|
||||
auto receiverInfo = Database::Connection->GetCharacterInfoByName(receiver);
|
||||
idOfReceiver = receiverInfo.ID;
|
||||
|
||||
if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) {
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
|
||||
stmt->setInt(1, entity->GetObjectID());
|
||||
stmt->setInt(2, idOfReceiver);
|
||||
stmt->setInt(3, idOfReceiver);
|
||||
stmt->setInt(4, entity->GetObjectID());
|
||||
|
||||
sql::ResultSet* res = stmt->executeQuery();
|
||||
|
||||
if (res->next()) {
|
||||
isBestFriend = res->getInt("best_friend") == 3;
|
||||
}
|
||||
isBestFriend = Database::Connection->AreBestFriends(entity->GetObjectID(), idOfReceiver);
|
||||
|
||||
if (isBestFriend) {
|
||||
auto tmpBestFriendMap = user->GetIsBestFriendMap();
|
||||
tmpBestFriendMap[receiver] = true;
|
||||
user->SetIsBestFriendMap(tmpBestFriendMap);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
} else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) {
|
||||
isBestFriend = true;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ void AgPropguards::OnMissionDialogueOK(Entity* self, Entity* target, int mission
|
||||
&& !character->GetPlayerFlag(flag)) {
|
||||
// If the player just started the mission, play a cinematic highlighting the target
|
||||
GameMessages::SendPlayCinematic(target->GetObjectID(), u"MissionCam", target->GetSystemAddress());
|
||||
} else if (missionState == eMissionState::READY_TO_COMPLETE) {
|
||||
} else if (missionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
|
||||
// Makes the guard disappear once the mission has been completed
|
||||
const auto zoneControlID = Game::entityManager->GetZoneControlEntity()->GetObjectID();
|
||||
GameMessages::SendNotifyClientObject(zoneControlID, u"GuardChat", 0, 0, self->GetObjectID(),
|
||||
|
||||
@@ -180,43 +180,21 @@ int main(int argc, char** argv) {
|
||||
|
||||
CDClientManager::Instance();
|
||||
|
||||
//Connect to the MySQL Database
|
||||
std::string mysql_host = Game::config->GetValue("mysql_host");
|
||||
std::string mysql_database = Game::config->GetValue("mysql_database");
|
||||
std::string mysql_username = Game::config->GetValue("mysql_username");
|
||||
std::string mysql_password = Game::config->GetValue("mysql_password");
|
||||
|
||||
Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1");
|
||||
|
||||
if (!Game::config->GetValue("dump_folder").empty()) {
|
||||
Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder"));
|
||||
}
|
||||
|
||||
try {
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
Game::logger->Log("WorldServer", "Got an error while connecting to the database: %s", ex.what());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Database::Connect(Game::config);
|
||||
|
||||
//Find out the master's IP:
|
||||
std::string masterIP = "localhost";
|
||||
uint32_t masterPort = 1000;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
auto masterSock = Database::Connection->GetMasterServerIP();
|
||||
|
||||
ObjectIDManager::Instance()->Initialize();
|
||||
UserManager::Instance()->Initialize();
|
||||
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
|
||||
|
||||
Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
|
||||
Game::server = new dServer(masterSock.hostAddress, ourPort, instanceID, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::World, Game::config, &Game::shouldShutdown, zoneID);
|
||||
|
||||
//Connect to the chat server:
|
||||
uint32_t chatPort = 1501;
|
||||
@@ -225,7 +203,7 @@ int main(int argc, char** argv) {
|
||||
auto chatSock = SocketDescriptor(uint16_t(ourPort + 2), 0);
|
||||
Game::chatServer = RakNetworkFactory::GetRakPeerInterface();
|
||||
Game::chatServer->Startup(1, 30, &chatSock, 1);
|
||||
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
|
||||
Game::chatServer->Connect(masterSock.hostAddress, chatPort, "3.25 ND1", 8);
|
||||
|
||||
//Set up other things:
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
@@ -380,7 +358,7 @@ int main(int argc, char** argv) {
|
||||
if (framesSinceChatDisconnect >= chatReconnectionTime) {
|
||||
framesSinceChatDisconnect = 0;
|
||||
|
||||
Game::chatServer->Connect(masterIP.c_str(), chatPort, "3.25 ND1", 8);
|
||||
Game::chatServer->Connect(masterSock.hostAddress, chatPort, "3.25 ND1", 8);
|
||||
}
|
||||
} else framesSinceChatDisconnect = 0;
|
||||
|
||||
@@ -482,18 +460,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
//Every 10 min we ping our sql server to keep it alive hopefully:
|
||||
if (framesSinceLastSQLPing >= sqlPingTime) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
masterIP = res->getString(1).c_str();
|
||||
masterPort = res->getInt(2);
|
||||
}
|
||||
|
||||
delete res;
|
||||
delete stmt;
|
||||
Database::Connection->GetMasterServerIP();
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
@@ -886,20 +853,10 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
// If the check is turned on, validate the client's database checksum.
|
||||
if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) {
|
||||
uint32_t gmLevel = 0;
|
||||
auto* stmt = Database::CreatePreppedStmt("SELECT gm_level FROM accounts WHERE name=? LIMIT 1;");
|
||||
stmt->setString(1, username.c_str());
|
||||
auto account = Database::Connection->GetAccountByName(username);
|
||||
|
||||
auto* res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
gmLevel = res->getInt(1);
|
||||
}
|
||||
|
||||
delete stmt;
|
||||
delete res;
|
||||
|
||||
// Developers may skip this check
|
||||
if (gmLevel < 8 && clientDatabaseChecksum != databaseChecksum) {
|
||||
// Operators may skip this check
|
||||
if (account.MaxGMLevel < 8 && clientDatabaseChecksum != databaseChecksum) {
|
||||
Game::logger->Log("WorldServer", "Client's database checksum does not match the server's, aborting connection.");
|
||||
Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION);
|
||||
return;
|
||||
@@ -1104,35 +1061,19 @@ void HandlePacket(Packet* packet) {
|
||||
goto noBBB;
|
||||
}
|
||||
|
||||
//Check for BBB models:
|
||||
auto stmt = Database::CreatePreppedStmt("SELECT ugc_id FROM properties_contents WHERE lot=14 AND property_id=?");
|
||||
|
||||
int32_t templateId = result.getIntField(0);
|
||||
uint64_t propertyId = Database::Connection->GetPropertyFromTemplateAndClone(result.getIntField(0), g_CloneID);
|
||||
|
||||
result.finalize();
|
||||
|
||||
auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;");
|
||||
// Check for BBB models
|
||||
auto models = Database::Connection->GetBBBModlesForProperty(propertyId);
|
||||
|
||||
propertyLookup->setInt(1, templateId);
|
||||
propertyLookup->setInt64(2, g_CloneID);
|
||||
for (const auto& modelId : models) {
|
||||
Game::logger->Log("UGC", "Getting lxfml ugcID: %u", modelId);
|
||||
|
||||
auto* propertyEntry = propertyLookup->executeQuery();
|
||||
uint64_t propertyId = 0;
|
||||
|
||||
if (propertyEntry->next()) {
|
||||
propertyId = propertyEntry->getUInt64(1);
|
||||
}
|
||||
|
||||
delete propertyLookup;
|
||||
|
||||
stmt->setUInt64(1, propertyId);
|
||||
auto res = stmt->executeQuery();
|
||||
while (res->next()) {
|
||||
Game::logger->Log("UGC", "Getting lxfml ugcID: %u", res->getUInt(1));
|
||||
|
||||
//Get lxfml:
|
||||
// Get lxfml data
|
||||
auto stmtL = Database::CreatePreppedStmt("SELECT lxfml from ugc where id=?");
|
||||
stmtL->setUInt(1, res->getUInt(1));
|
||||
stmtL->setUInt(1, modelId);
|
||||
|
||||
auto lxres = stmtL->executeQuery();
|
||||
|
||||
@@ -1143,15 +1084,15 @@ void HandlePacket(Packet* packet) {
|
||||
size_t lxfmlSize = lxfml->tellg();
|
||||
lxfml->seekg(0);
|
||||
|
||||
//Send message:
|
||||
// Send message
|
||||
{
|
||||
LWOOBJID blueprintID = res->getUInt(1);
|
||||
LWOOBJID blueprintID = modelId;
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
|
||||
|
||||
CBITSTREAM;
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE);
|
||||
bitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); //always zero so that a check on the client passes
|
||||
bitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); // always zero so that a check on the client passes
|
||||
bitStream.Write(eBlueprintSaveResponseType::EverythingWorked);
|
||||
bitStream.Write<uint32_t>(1);
|
||||
bitStream.Write(blueprintID);
|
||||
@@ -1163,7 +1104,7 @@ void HandlePacket(Packet* packet) {
|
||||
|
||||
SystemAddress sysAddr = packet->systemAddress;
|
||||
SEND_PACKET;
|
||||
PacketUtils::SavePacket("lxfml packet " + std::to_string(res->getUInt(1)) + ".bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed());
|
||||
PacketUtils::SavePacket("lxfml packet " + std::to_string(modelId) + ".bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1354,7 +1295,8 @@ void FinalizeShutdown() {
|
||||
|
||||
//Delete our objects here:
|
||||
Metrics::Clear();
|
||||
Database::Destroy("WorldServer");
|
||||
Database::Destroy();
|
||||
|
||||
if (Game::chatFilter) delete Game::chatFilter;
|
||||
if (Game::zoneManager) delete Game::zoneManager;
|
||||
if (Game::server) delete Game::server;
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
# MySQL connection info:
|
||||
mysql_host=
|
||||
mysql_database=
|
||||
mysql_username=
|
||||
mysql_password=
|
||||
database_file=darkflame.sqlite
|
||||
|
||||
# 0 or 1, should log to console
|
||||
log_to_console=1
|
||||
@@ -43,7 +39,3 @@ maximum_mtu_size=1228
|
||||
# This cannot just be any arbitrary number. This has to match the same value that is in your client.
|
||||
# If you do not know what this value is, default it to 171022.
|
||||
client_net_version=171022
|
||||
|
||||
# Turn to 0 to default teams to use the live accurate Shared Loot (0) by default as opposed to Free for All (1)
|
||||
# This is used in both Chat and World servers.
|
||||
default_team_loot=1
|
||||
|
||||
Reference in New Issue
Block a user