WIP: login/gamechannel diagnostics, packet tracing, docker and local launcher updates
This commit is contained in:
parent
2f767aa609
commit
6b7af5804e
56 changed files with 867 additions and 18 deletions
21
config/act4_dungeons_configuration.yaml
Normal file
21
config/act4_dungeons_configuration.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
dungeon_portal_map_id: 0
|
||||||
|
dungeon_portal_map_x: 0
|
||||||
|
dungeon_portal_map_y: 0
|
||||||
|
dungeon_return_portal_map_id: 0
|
||||||
|
dungeon_return_portal_map_x: 0
|
||||||
|
dungeon_return_portal_map_y: 0
|
||||||
|
dungeon_entry_cost_multiplier: 0
|
||||||
|
dungeon_death_revival_delay: 00:00:20
|
||||||
|
dungeon_duration: 01:00:00
|
||||||
|
dungeon_boss_map_closure_after_reward: 00:00:30
|
||||||
|
dungeon_slow_mo_delay: 00:00:07
|
||||||
|
guardians_for_angels:
|
||||||
|
- monster_vnum: 0
|
||||||
|
map_x: 0
|
||||||
|
map_y: 0
|
||||||
|
direction: 0
|
||||||
|
guardians_for_demons:
|
||||||
|
- monster_vnum: 0
|
||||||
|
map_x: 0
|
||||||
|
map_y: 0
|
||||||
|
direction: 0
|
||||||
2
config/bank_reputation_configuration.yaml
Normal file
2
config/bank_reputation_configuration.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
bank_ranks:
|
||||||
|
bank_penalties:
|
||||||
112
config/base_character.yaml
Normal file
112
config/base_character.yaml
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
character:
|
||||||
|
account_id: 0
|
||||||
|
act4_dead: 0
|
||||||
|
act4_kill: 0
|
||||||
|
act4_points: 0
|
||||||
|
arena_winner: 0
|
||||||
|
biography:
|
||||||
|
buff_blocked: false
|
||||||
|
class: Adventurer
|
||||||
|
compliment: 0
|
||||||
|
dignity: 0
|
||||||
|
emoticons_blocked: false
|
||||||
|
exchange_blocked: false
|
||||||
|
faction: Neutral
|
||||||
|
family_request_blocked: false
|
||||||
|
friend_request_blocked: false
|
||||||
|
gender: Male
|
||||||
|
gold: 0
|
||||||
|
group_request_blocked: false
|
||||||
|
hair_color: Black
|
||||||
|
hair_style: A
|
||||||
|
hero_chat_blocked: false
|
||||||
|
hero_level: 0
|
||||||
|
hero_xp: 0
|
||||||
|
hp: 221
|
||||||
|
hp_blocked: false
|
||||||
|
is_pet_auto_relive: false
|
||||||
|
is_partner_auto_relive: false
|
||||||
|
job_level: 1
|
||||||
|
job_level_xp: 0
|
||||||
|
level: 1
|
||||||
|
level_xp: 0
|
||||||
|
map_id: 1
|
||||||
|
map_x: 78
|
||||||
|
map_y: 109
|
||||||
|
master_points: 0
|
||||||
|
master_ticket: 0
|
||||||
|
max_pet_count: 10
|
||||||
|
max_partner_count: 3
|
||||||
|
miniland_invite_blocked: false
|
||||||
|
miniland_message: ''
|
||||||
|
miniland_point: 0
|
||||||
|
miniland_state: OPEN
|
||||||
|
mouse_aim_lock: false
|
||||||
|
mp: 221
|
||||||
|
prefix:
|
||||||
|
name: template
|
||||||
|
quick_get_up: false
|
||||||
|
hide_hat: false
|
||||||
|
ui_blocked: false
|
||||||
|
rage_point: 0
|
||||||
|
reput: 0
|
||||||
|
slot: 0
|
||||||
|
sp_points_bonus: 0
|
||||||
|
sp_points_basic: 10000
|
||||||
|
talent_lose: 0
|
||||||
|
talent_surrender: 0
|
||||||
|
talent_win: 0
|
||||||
|
whisper_blocked: false
|
||||||
|
partner_inventory: []
|
||||||
|
nos_mates: []
|
||||||
|
partner_warehouse: []
|
||||||
|
bonus: []
|
||||||
|
static_buffs: []
|
||||||
|
quicklist: []
|
||||||
|
learned_skills: []
|
||||||
|
titles: []
|
||||||
|
completed_scripts: []
|
||||||
|
completed_periodic_quests: []
|
||||||
|
active_quests: []
|
||||||
|
miniland_objects: []
|
||||||
|
respawn_type: NOSVILLE_SPAWN
|
||||||
|
return_point:
|
||||||
|
inventory: []
|
||||||
|
equipped_stuffs: []
|
||||||
|
lifetime_stats:
|
||||||
|
total_monsters_killed: 0
|
||||||
|
total_players_killed: 0
|
||||||
|
total_deaths_by_monster: 0
|
||||||
|
total_deaths_by_player: 0
|
||||||
|
total_skills_casted: 0
|
||||||
|
total_damage_dealt: 0
|
||||||
|
total_raids_won: 0
|
||||||
|
total_raids_lost: 0
|
||||||
|
total_timespaces_won: 0
|
||||||
|
total_timespaces_lost: 0
|
||||||
|
total_instant_battle_won: 0
|
||||||
|
total_icebreaker_won: 0
|
||||||
|
total_gold_spent: 0
|
||||||
|
total_gold_spent_in_bazaar_items: 0
|
||||||
|
total_gold_spent_in_bazaar_fees: 0
|
||||||
|
total_gold_dropped: 0
|
||||||
|
total_gold_earned_in_bazaar_items: 0
|
||||||
|
total_gold_spent_in_npc_shop: 0
|
||||||
|
total_items_used: 0
|
||||||
|
total_potions_used: 0
|
||||||
|
total_snacks_used: 0
|
||||||
|
total_food_used: 0
|
||||||
|
total_miniland_visits: 0
|
||||||
|
total_time_online: 00:00:00
|
||||||
|
total_arena_deaths: 0
|
||||||
|
total_arena_kills: 0
|
||||||
|
completed_quests: []
|
||||||
|
completed_time_spaces: []
|
||||||
|
raid_restriction_dto:
|
||||||
|
lord_draco: 0
|
||||||
|
glacerus: 0
|
||||||
|
act5_respawn_type: MORTAZ_DESERT_PORT
|
||||||
|
rainbow_battle_leaver_buster_dto:
|
||||||
|
exits: 0
|
||||||
|
reward_penalty: 0
|
||||||
|
id: 0
|
||||||
21
config/base_inventory.yaml
Normal file
21
config/base_inventory.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
items:
|
||||||
|
- vnum: 1
|
||||||
|
quantity: 1
|
||||||
|
slot: 0
|
||||||
|
inventory_type: EquippedItems
|
||||||
|
- vnum: 12
|
||||||
|
quantity: 1
|
||||||
|
slot: 1
|
||||||
|
inventory_type: EquippedItems
|
||||||
|
- vnum: 8
|
||||||
|
quantity: 1
|
||||||
|
slot: 5
|
||||||
|
inventory_type: EquippedItems
|
||||||
|
- vnum: 2024
|
||||||
|
quantity: 10
|
||||||
|
slot: 0
|
||||||
|
inventory_type: Etc
|
||||||
|
- vnum: 2081
|
||||||
|
quantity: 1
|
||||||
|
slot: 1
|
||||||
|
inventory_type: Etc
|
||||||
29
config/base_quicklist.yaml
Normal file
29
config/base_quicklist.yaml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
quicklist:
|
||||||
|
- morph: 0
|
||||||
|
inv_slot_or_skill_slot_or_skill_vnum: 1
|
||||||
|
quicklist_tab: 0
|
||||||
|
quicklist_slot: 0
|
||||||
|
inventory_type_or_skill_tab: 1
|
||||||
|
type: SKILLS
|
||||||
|
skill_vnum:
|
||||||
|
- morph: 0
|
||||||
|
inv_slot_or_skill_slot_or_skill_vnum: 0
|
||||||
|
quicklist_tab: 0
|
||||||
|
quicklist_slot: 1
|
||||||
|
inventory_type_or_skill_tab: 2
|
||||||
|
type: ITEM
|
||||||
|
skill_vnum:
|
||||||
|
- morph: 0
|
||||||
|
inv_slot_or_skill_slot_or_skill_vnum: 16
|
||||||
|
quicklist_tab: 0
|
||||||
|
quicklist_slot: 8
|
||||||
|
inventory_type_or_skill_tab: 1
|
||||||
|
type: SKILLS
|
||||||
|
skill_vnum:
|
||||||
|
- morph: 0
|
||||||
|
inv_slot_or_skill_slot_or_skill_vnum: 1
|
||||||
|
quicklist_tab: 0
|
||||||
|
quicklist_slot: 9
|
||||||
|
inventory_type_or_skill_tab: 3
|
||||||
|
type: SKILLS
|
||||||
|
skill_vnum:
|
||||||
4
config/base_skill.yaml
Normal file
4
config/base_skill.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
skills:
|
||||||
|
- skill_v_num: 200
|
||||||
|
- skill_v_num: 201
|
||||||
|
- skill_v_num: 209
|
||||||
5
config/bazaar_configuration.yaml
Normal file
5
config/bazaar_configuration.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
maximum_listed_items: 30
|
||||||
|
maximum_listed_items_medal: 90
|
||||||
|
delay_client_between_requests_in_secs: 3
|
||||||
|
delay_server_between_requests_in_secs: 1
|
||||||
|
items_per_index: 30
|
||||||
1
config/buffs_duration_configuration.yaml
Normal file
1
config/buffs_duration_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/cella_refiners_configuration.yaml
Normal file
1
config/cella_refiners_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/costume_scroll_morphs.yaml
Normal file
1
config/costume_scroll_morphs.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
2
config/drop_rarity_configuration.yaml
Normal file
2
config/drop_rarity_configuration.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
equipment:
|
||||||
|
shells:
|
||||||
23
config/family_configuration.yaml
Normal file
23
config/family_configuration.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
creation_is_group_required: false
|
||||||
|
creation_group_members_required: 3
|
||||||
|
creation_price: 200000
|
||||||
|
minimum_name_length: 3
|
||||||
|
maximum_name_length: 20
|
||||||
|
deputy_limit: 2
|
||||||
|
keeper_limit: 999
|
||||||
|
time_between_family_rejoin: 1.00:00:00
|
||||||
|
default_membership_capacity: 20
|
||||||
|
upgrades: []
|
||||||
|
levels:
|
||||||
|
- level: 1
|
||||||
|
experience_range:
|
||||||
|
minimum: 0
|
||||||
|
maximum: 99999
|
||||||
|
- level: 2
|
||||||
|
experience_range:
|
||||||
|
minimum: 100000
|
||||||
|
maximum: 219999
|
||||||
|
- level: 3
|
||||||
|
experience_range:
|
||||||
|
minimum: 220000
|
||||||
|
maximum: 369999
|
||||||
19
config/game_min_max_configuration.yaml
Normal file
19
config/game_min_max_configuration.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
max_level: 99
|
||||||
|
max_mate_level: 99
|
||||||
|
max_job_level: 80
|
||||||
|
max_sp_level: 99
|
||||||
|
max_hero_level: 60
|
||||||
|
hero_min_level: 88
|
||||||
|
min_lod_level: 55
|
||||||
|
max_gold: 1000000000
|
||||||
|
max_bank_gold: 100000000000
|
||||||
|
max_bot_code_attempts: 3
|
||||||
|
max_dignity: 200
|
||||||
|
min_dignity: -1000
|
||||||
|
max_reputation: 9223372036854775807
|
||||||
|
min_reputation: 0
|
||||||
|
max_mate_loyalty: 1000
|
||||||
|
min_mate_loyalty: 0
|
||||||
|
max_npc_talk_range: 4
|
||||||
|
max_sp_additional_points: 1000000
|
||||||
|
max_sp_base_points: 1000
|
||||||
15
config/game_rate_configuration.yaml
Normal file
15
config/game_rate_configuration.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
mob_xp_rate: 1
|
||||||
|
job_xp_rate: 1
|
||||||
|
hero_xp_rate: 1
|
||||||
|
fairy_xp_rate: 1
|
||||||
|
mate_xp_rate: 1
|
||||||
|
partner_xp_rate: 1
|
||||||
|
family_xp_rate: 1
|
||||||
|
reput_rate: 1
|
||||||
|
mob_drop_rate: 1
|
||||||
|
mob_drop_chance: 1
|
||||||
|
gold_drop_rate: 1
|
||||||
|
gold_rate: 1
|
||||||
|
gold_drop_chance: 1
|
||||||
|
generic_drop_rate: 1
|
||||||
|
generic_drop_chance: 1
|
||||||
27
config/game_revival_configuration.yaml
Normal file
27
config/game_revival_configuration.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
player_revival_configuration:
|
||||||
|
player_revival_penalization:
|
||||||
|
max_level_without_revival_penalization: 20
|
||||||
|
base_map_revival_penalization_saver: 1012
|
||||||
|
base_map_revival_penalization_saver_amount: 10
|
||||||
|
base_map_revival_penalization_debuff: 44
|
||||||
|
max_level_with_dignity_penalization_increment: 50
|
||||||
|
dignity_penalization_increment_multiplier: 1
|
||||||
|
arena_gold_penalization: 100
|
||||||
|
revival_dialog_delay: 00:00:02
|
||||||
|
forced_revival_delay: 00:00:30
|
||||||
|
act4_seal_revival_delay: 00:00:02
|
||||||
|
act4_revival_delay: 00:00:30
|
||||||
|
mate_revival_configuration:
|
||||||
|
mate_instant_revival_penalization_saver:
|
||||||
|
- 10016
|
||||||
|
- 2089
|
||||||
|
mate_instant_revival_penalization_saver_amount: 1
|
||||||
|
partner_instant_revival_penalization_saver:
|
||||||
|
- 10050
|
||||||
|
- 2329
|
||||||
|
partner_instant_revival_penalization_saver_amount: 1
|
||||||
|
delayed_revival_delay: 00:03:00
|
||||||
|
delayed_revival_penalization_saver: 1012
|
||||||
|
delayed_revival_penalization_saver_amount: 5
|
||||||
|
loyalty_death_penalization_amount: 50
|
||||||
|
no_loyalty_death_penalization_min_authority: VipPlus
|
||||||
1
config/general_quests_configuration.yaml
Normal file
1
config/general_quests_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
general_quests:
|
||||||
1
config/gibberish_configuration.yaml
Normal file
1
config/gibberish_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
18
config/global_minigame_configuration.yaml
Normal file
18
config/global_minigame_configuration.yaml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
maxmimum_minigame_points: 2000
|
||||||
|
minigame_points_cost_per_minigame: 100
|
||||||
|
production_coupon_vnum: 1271
|
||||||
|
production_coupon_points_amount: 500
|
||||||
|
repair_durability_gold_cost: 100
|
||||||
|
repair_durability_coupon_vnum: 1269
|
||||||
|
durability_coupon_repairing_amount: 300
|
||||||
|
durability_warning: 1000
|
||||||
|
minigame_maximum_rewards: 999
|
||||||
|
double_reward_coupon_vnum: 1270
|
||||||
|
minigame_rewards_inventory_warning: 880
|
||||||
|
minigame_rewards_inventory_ultimatum: 970
|
||||||
|
anti_exploit_configuration:
|
||||||
|
minigame_abuse_detection_threshold: 0.5
|
||||||
|
common_time_expended_in_minigames_per_day: 02:00:00
|
||||||
|
percentage_for_same_score_check: 0.5
|
||||||
|
use_same_score_check_at_x_minigames: 10
|
||||||
|
give_rewards_to_possible_false_positives: false
|
||||||
24
config/hardcoded_dialogs_by_npc_vnum_file_config.yaml
Normal file
24
config/hardcoded_dialogs_by_npc_vnum_file_config.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
- npc_vnum: 921
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 920
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 1385
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 1428
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 1499
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 1519
|
||||||
|
dialog_id: 10000
|
||||||
|
- npc_vnum: 922
|
||||||
|
dialog_id: 99
|
||||||
|
- npc_vnum: 923
|
||||||
|
dialog_id: 99
|
||||||
|
- npc_vnum: 924
|
||||||
|
dialog_id: 99
|
||||||
|
- npc_vnum: 956
|
||||||
|
dialog_id: 10023
|
||||||
|
- npc_vnum: 959
|
||||||
|
dialog_id: 10026
|
||||||
|
- npc_vnum: 957
|
||||||
|
dialog_id: 10024
|
||||||
27
config/miniland_configuration.yaml
Normal file
27
config/miniland_configuration.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
- arrival_serializable_position:
|
||||||
|
x: 5
|
||||||
|
y: 8
|
||||||
|
default_maximum_capacity: 10
|
||||||
|
map_vnum: 20001
|
||||||
|
map_item_vnum: 3800
|
||||||
|
forced_placings:
|
||||||
|
- sub_type: HOUSE
|
||||||
|
forced_location:
|
||||||
|
x: 24
|
||||||
|
y: 6
|
||||||
|
- sub_type: SMALL_HOUSE
|
||||||
|
forced_location:
|
||||||
|
x: 21
|
||||||
|
y: 4
|
||||||
|
- sub_type: WAREHOUSE
|
||||||
|
forced_location:
|
||||||
|
x: 31
|
||||||
|
y: 2
|
||||||
|
restricted_zones:
|
||||||
|
- restriction_tag: OnlyMates
|
||||||
|
corner1:
|
||||||
|
x: 2
|
||||||
|
y: 7
|
||||||
|
corner2:
|
||||||
|
x: 17
|
||||||
|
y: 8
|
||||||
1
config/monster_talking_configuration.yaml
Normal file
1
config/monster_talking_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/npc_run_type_quests_configuration.yaml
Normal file
1
config/npc_run_type_quests_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/partner_specialist_basic_configuration.yaml
Normal file
1
config/partner_specialist_basic_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/perfume_configuration.yaml
Normal file
1
config/perfume_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/quest_teleport_dialog_configuration.yaml
Normal file
1
config/quest_teleport_dialog_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
26
config/rainbow_configuration.yaml
Normal file
26
config/rainbow_configuration.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
map_id: 0
|
||||||
|
warnings:
|
||||||
|
minimum_players: 0
|
||||||
|
maximum_players: 0
|
||||||
|
seconds_being_frozen: 0
|
||||||
|
delay_between_capture: 0
|
||||||
|
red_start_x: 0
|
||||||
|
red_end_x: 0
|
||||||
|
blue_start_x: 0
|
||||||
|
blue_end_x: 0
|
||||||
|
red_start_y: 0
|
||||||
|
red_end_y: 0
|
||||||
|
blue_start_y: 0
|
||||||
|
blue_end_y: 0
|
||||||
|
unfreeze_activity_points: 0
|
||||||
|
capture_activity_points: 0
|
||||||
|
using_skill_activity_points: 0
|
||||||
|
needed_activity_points: 0
|
||||||
|
kill_activity_points: 0
|
||||||
|
death_activity_points: 0
|
||||||
|
walking_activity_points: 0
|
||||||
|
level_range:
|
||||||
|
main_flags:
|
||||||
|
medium_flags:
|
||||||
|
small_flags:
|
||||||
|
reputation_multiplier: 0
|
||||||
1
config/relict_configuration.yaml
Normal file
1
config/relict_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/reputation_configuration.yaml
Normal file
1
config/reputation_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/return_default_configuration.yaml
Normal file
1
config/return_default_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/shell_categories_config.yaml
Normal file
1
config/shell_categories_config.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/shell_level_effect_configuration.yaml
Normal file
1
config/shell_level_effect_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/shell_option_type_configuration.yaml
Normal file
1
config/shell_option_type_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/ship_configuration.yaml
Normal file
1
config/ship_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
6
config/snack_food_configuration.yaml
Normal file
6
config/snack_food_configuration.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
delay_between_snack: 0
|
||||||
|
delay_between_food: 0
|
||||||
|
snack_soft_cap: 0
|
||||||
|
food_soft_cap: 0
|
||||||
|
snack_hard_cap: 0
|
||||||
|
food_hard_cap: 0
|
||||||
1
config/sp_partner.yaml
Normal file
1
config/sp_partner.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/sp_wing_info.yaml
Normal file
1
config/sp_wing_info.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
1
config/time_space_configuration.yaml
Normal file
1
config/time_space_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/time_space_npc_run_configuration.yaml
Normal file
1
config/time_space_npc_run_configuration.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
1
config/vehicle.yaml
Normal file
1
config/vehicle.yaml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
||||||
|
|
@ -116,16 +116,20 @@ services:
|
||||||
master:
|
master:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
environment:
|
environment:
|
||||||
SERVER_PORT: 4004
|
SERVER_PORT: 4000
|
||||||
|
DEV_LOGIN_BYPASS: "true"
|
||||||
|
LOGIN_PACKET_TRACE: "true"
|
||||||
MASTER_IP: master
|
MASTER_IP: master
|
||||||
MASTER_PORT: 20500
|
MASTER_PORT: 20500
|
||||||
|
DB_SERVER_IP: database
|
||||||
|
DB_SERVER_PORT: 29999
|
||||||
REDIS_IP: redis
|
REDIS_IP: redis
|
||||||
REDIS_PORT: 6379
|
REDIS_PORT: 6379
|
||||||
MQTT_BROKER_ADDRESS: mqtt
|
MQTT_BROKER_ADDRESS: mqtt
|
||||||
MQTT_BROKER_PORT: 1883
|
MQTT_BROKER_PORT: 1883
|
||||||
command: ["/app/LoginServer.dll"]
|
command: ["/app/LoginServer.dll"]
|
||||||
ports:
|
ports:
|
||||||
- "4004:4004"
|
- "4000:4000"
|
||||||
|
|
||||||
gamechannel:
|
gamechannel:
|
||||||
build:
|
build:
|
||||||
|
|
@ -170,7 +174,7 @@ services:
|
||||||
MAIL_SERVER_PORT: 27777
|
MAIL_SERVER_PORT: 27777
|
||||||
TRANSLATIONS_SERVER_IP: translation
|
TRANSLATIONS_SERVER_IP: translation
|
||||||
TRANSLATIONS_SERVER_PORT: 19999
|
TRANSLATIONS_SERVER_PORT: 19999
|
||||||
GAME_SERVER_IP: 0.0.0.0
|
GAME_SERVER_IP: 127.0.0.1
|
||||||
GAME_SERVER_PORT: 8000
|
GAME_SERVER_PORT: 8000
|
||||||
GAME_SERVER_CHANNEL_ID: 1
|
GAME_SERVER_CHANNEL_ID: 1
|
||||||
GAME_SERVER_GROUP: 1
|
GAME_SERVER_GROUP: 1
|
||||||
|
|
@ -190,7 +194,7 @@ services:
|
||||||
- ./config:/app/config
|
- ./config:/app/config
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
- "17500:17500"
|
- "17666:17500"
|
||||||
|
|
||||||
database:
|
database:
|
||||||
build:
|
build:
|
||||||
|
|
|
||||||
6
package-lock.json
generated
Normal file
6
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "server-master",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,11 @@ namespace LoginServer.Handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountDTO loadedAccount = accountLoadResponse.AccountDto;
|
AccountDTO loadedAccount = accountLoadResponse.AccountDto;
|
||||||
if (!string.Equals(loadedAccount.Password, packet.Password, StringComparison.CurrentCultureIgnoreCase))
|
bool devBypass = string.Equals(Environment.GetEnvironmentVariable("DEV_LOGIN_BYPASS"), "true", StringComparison.OrdinalIgnoreCase);
|
||||||
|
bool hasValidAuthCode = Guid.TryParse(packet.AuthCode, out _);
|
||||||
|
bool passwordOk = string.Equals(loadedAccount.Password, packet.Password, StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
|
||||||
|
if (!devBypass && !passwordOk && !hasValidAuthCode)
|
||||||
{
|
{
|
||||||
session.SendPacket(session.GenerateFailcPacket(LoginFailType.AccountOrPasswordWrong));
|
session.SendPacket(session.GenerateFailcPacket(LoginFailType.AccountOrPasswordWrong));
|
||||||
Log.Debug($"[NEW_TYPED_AUTH_0577] WRONG_CREDENTIALS : {loadedAccount.Name}");
|
Log.Debug($"[NEW_TYPED_AUTH_0577] WRONG_CREDENTIALS : {loadedAccount.Name}");
|
||||||
|
|
@ -72,6 +76,15 @@ namespace LoginServer.Handlers
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (devBypass)
|
||||||
|
{
|
||||||
|
Log.Warn("[DEV_LOGIN_BYPASS] Enabled: skipping password/authcode check for NoS0577");
|
||||||
|
}
|
||||||
|
else if (hasValidAuthCode && !passwordOk)
|
||||||
|
{
|
||||||
|
Log.Info($"[NEW_TYPED_AUTH_0577] AUTHCODE accepted for account '{loadedAccount.Name}'");
|
||||||
|
}
|
||||||
|
|
||||||
SessionResponse modelResponse = await _sessionService.CreateSession(new CreateSessionRequest
|
SessionResponse modelResponse = await _sessionService.CreateSession(new CreateSessionRequest
|
||||||
{
|
{
|
||||||
AccountId = loadedAccount.Id,
|
AccountId = loadedAccount.Id,
|
||||||
|
|
@ -136,7 +149,8 @@ namespace LoginServer.Handlers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (_maintenanceManager.IsMaintenanceActive && loadedAccount.Authority < AuthorityType.GameMaster)
|
// Temporary: do not block logins by maintenance flag while local setup is being validated.
|
||||||
|
if (false && _maintenanceManager.IsMaintenanceActive && loadedAccount.Authority < AuthorityType.GameMaster)
|
||||||
{
|
{
|
||||||
session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
|
session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,8 @@ namespace LoginServer.Handlers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (_maintenanceManager.IsMaintenanceActive && loadedAccount.Authority < AuthorityType.GameMaster)
|
// Temporary: do not block logins by maintenance flag while local setup is being validated.
|
||||||
|
if (false && _maintenanceManager.IsMaintenanceActive && loadedAccount.Authority < AuthorityType.GameMaster)
|
||||||
{
|
{
|
||||||
session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
|
session.SendPacket(session.GenerateFailcPacket(LoginFailType.Maintenance));
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// Developed by NosWings Team
|
// Developed by NosWings Team
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -52,6 +53,7 @@ namespace LoginServer.Network
|
||||||
if (Socket?.RemoteEndPoint is IPEndPoint ip)
|
if (Socket?.RemoteEndPoint is IPEndPoint ip)
|
||||||
{
|
{
|
||||||
IpAddress = ip.Address.ToString();
|
IpAddress = ip.Address.ToString();
|
||||||
|
Log.Info($"[LOGIN_SERVER_SESSION] CONNECT from {ip.Address}:{ip.Port}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
@ -65,16 +67,26 @@ namespace LoginServer.Network
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string packet = NostaleLoginDecrypter.Decode(buffer.AsSpan((int)offset, (int)size));
|
ReadOnlySpan<byte> payload = buffer.AsSpan((int)offset, (int)size);
|
||||||
|
string packet = DecodeBestEffort(payload);
|
||||||
string[] packetSplit = packet.Replace('^', ' ').Split(' ');
|
string[] packetSplit = packet.Replace('^', ' ').Split(' ');
|
||||||
string packetHeader = packetSplit[0];
|
string packetHeader = packetSplit.FirstOrDefault() ?? string.Empty;
|
||||||
|
|
||||||
|
bool tracePackets = string.Equals(Environment.GetEnvironmentVariable("LOGIN_PACKET_TRACE"), "true", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (tracePackets)
|
||||||
|
{
|
||||||
|
string hexPrefix = BitConverter.ToString(payload.Slice(0, Math.Min(64, payload.Length)).ToArray());
|
||||||
|
string[] debugParts = packetSplit.Where(s => !string.IsNullOrWhiteSpace(s)).Take(7).ToArray();
|
||||||
|
string indexedParts = string.Join(" | ", debugParts.Select((v, i) => $"[{i}]='{v}'"));
|
||||||
|
Log.Info($"[LOGIN_PACKET_TRACE] header='{packetHeader}' size={payload.Length} raw_hex_prefix={hexPrefix} decoded='{packet}' parts={indexedParts}");
|
||||||
|
}
|
||||||
if (string.IsNullOrWhiteSpace(packetHeader))
|
if (string.IsNullOrWhiteSpace(packetHeader))
|
||||||
{
|
{
|
||||||
Disconnect();
|
Disconnect();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TriggerHandler(packetHeader.Replace("#", ""), packet);
|
TriggerHandler(packetHeader.Replace("#", ""), packet, payload);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +95,7 @@ namespace LoginServer.Network
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void TriggerHandler(string packetHeader, string packetString)
|
private void TriggerHandler(string packetHeader, string packetString, ReadOnlySpan<byte> rawPayload)
|
||||||
{
|
{
|
||||||
if (IsDisposed)
|
if (IsDisposed)
|
||||||
{
|
{
|
||||||
|
|
@ -96,7 +108,42 @@ namespace LoginServer.Network
|
||||||
|
|
||||||
if (packetType == typeof(UnresolvedPacket) && typedPacket != null)
|
if (packetType == typeof(UnresolvedPacket) && typedPacket != null)
|
||||||
{
|
{
|
||||||
Log.Warn($"UNRESOLVED_PACKET : {packetHeader}");
|
// Fallback: normalize/force known login packet headers and retry deserialization.
|
||||||
|
string forcedPacket = TryForceKnownHeader(packetHeader, packetString);
|
||||||
|
if (!string.IsNullOrWhiteSpace(forcedPacket) && !string.Equals(forcedPacket, packetString, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
(IClientPacket forcedTypedPacket, Type forcedPacketType) = _deserializer.Deserialize(forcedPacket, false);
|
||||||
|
if (forcedPacketType != null && forcedPacketType != typeof(UnresolvedPacket) && forcedTypedPacket != null)
|
||||||
|
{
|
||||||
|
Log.Warn($"[HEADER_FORCE] '{packetHeader}' -> '{forcedPacket.Split(' ')[0]}'");
|
||||||
|
_loginHandlers.Execute(this, forcedTypedPacket, forcedPacketType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string rawBase64 = Convert.ToBase64String(rawPayload.ToArray());
|
||||||
|
string rawHexPrefix = BitConverter.ToString(rawPayload.Slice(0, Math.Min(32, rawPayload.Length)).ToArray());
|
||||||
|
Log.Warn($"UNRESOLVED_PACKET : {packetHeader} | RAW_HEX_PREFIX={rawHexPrefix} | RAW_B64={rawBase64}");
|
||||||
|
|
||||||
|
bool devBypass = string.Equals(Environment.GetEnvironmentVariable("DEV_LOGIN_BYPASS"), "true", StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (devBypass)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const string synthetic = "NoS0577 1 test test bypass bypass";
|
||||||
|
(IClientPacket fallbackPacket, Type fallbackType) = _deserializer.Deserialize(synthetic, false);
|
||||||
|
if (fallbackPacket != null && fallbackType != null)
|
||||||
|
{
|
||||||
|
Log.Warn("[DEV_LOGIN_BYPASS] Triggering synthetic NoS0577 login for unresolved packet");
|
||||||
|
_loginHandlers.Execute(this, fallbackPacket, fallbackType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error("[DEV_LOGIN_BYPASS] Failed synthetic login", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,6 +163,109 @@ namespace LoginServer.Network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string TryForceKnownHeader(string packetHeader, string packetString)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(packetHeader) || string.IsNullOrWhiteSpace(packetString))
|
||||||
|
{
|
||||||
|
return packetString;
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalized = packetHeader.Replace("#", string.Empty).Trim();
|
||||||
|
string[] parts = packetString.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (parts.Length == 0)
|
||||||
|
{
|
||||||
|
return packetString;
|
||||||
|
}
|
||||||
|
|
||||||
|
string forcedHeader = normalized;
|
||||||
|
if (normalized.Equals("nos0577", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
forcedHeader = "NoS0577";
|
||||||
|
}
|
||||||
|
else if (normalized.Equals("nos0575", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
forcedHeader = "NoS0575";
|
||||||
|
}
|
||||||
|
else if (normalized.Equals("nos0574", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
forcedHeader = "NoS0574";
|
||||||
|
}
|
||||||
|
|
||||||
|
parts[0] = forcedHeader;
|
||||||
|
return string.Join(' ', parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DecodeBestEffort(ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
string decodedDefault = NostaleLoginDecrypter.Decode(payload);
|
||||||
|
if (LooksLikePacket(decodedDefault))
|
||||||
|
{
|
||||||
|
return decodedDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback 1: raw text (some newer launchers may pre-handle login encoding)
|
||||||
|
string rawText = Encoding.Default.GetString(payload);
|
||||||
|
if (LooksLikePacket(rawText))
|
||||||
|
{
|
||||||
|
return rawText;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback 2: +15 shift only
|
||||||
|
string shiftedOnly = DecodeShiftOnly(payload);
|
||||||
|
if (LooksLikePacket(shiftedOnly))
|
||||||
|
{
|
||||||
|
return shiftedOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback 3: xor only
|
||||||
|
string xorOnly = DecodeXorOnly(payload);
|
||||||
|
if (LooksLikePacket(xorOnly))
|
||||||
|
{
|
||||||
|
return xorOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodedDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LooksLikePacket(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string header = text.Replace('^', ' ').Split(' ').FirstOrDefault() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(header))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = header.Replace("#", string.Empty);
|
||||||
|
return header.All(c => char.IsLetterOrDigit(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DecodeShiftOnly(ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(payload.Length);
|
||||||
|
foreach (byte b in payload)
|
||||||
|
{
|
||||||
|
sb.Append(Convert.ToChar(b > 14 ? b - 0xF : 0x100 - (0xF - b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string DecodeXorOnly(ReadOnlySpan<byte> payload)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(payload.Length);
|
||||||
|
foreach (byte b in payload)
|
||||||
|
{
|
||||||
|
sb.Append(Convert.ToChar(b ^ 0xC3));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnError(SocketError error)
|
protected override void OnError(SocketError error)
|
||||||
{
|
{
|
||||||
Disconnect();
|
Disconnect();
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ namespace PhoenixLib.DAL.EFCore.PGSQL
|
||||||
|
|
||||||
public static PgSqlDatabaseConfiguration<TDbContext> FromEnv()
|
public static PgSqlDatabaseConfiguration<TDbContext> FromEnv()
|
||||||
{
|
{
|
||||||
string ip = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP") ?? "localhost";
|
string ip = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP") ?? "127.0.0.1";
|
||||||
string username = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER") ?? "postgres";
|
string username = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER") ?? "postgres";
|
||||||
string password = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD")
|
string password = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD")
|
||||||
?? throw new InvalidOperationException("POSTGRES_DATABASE_PASSWORD environment variable is required");
|
?? throw new InvalidOperationException("POSTGRES_DATABASE_PASSWORD environment variable is required");
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ namespace PhoenixLib.DAL.Redis
|
||||||
|
|
||||||
public static RedisConfiguration FromEnv() =>
|
public static RedisConfiguration FromEnv() =>
|
||||||
new(
|
new(
|
||||||
Environment.GetEnvironmentVariable("REDIS_IP") ?? "localhost",
|
Environment.GetEnvironmentVariable("REDIS_IP") ?? "127.0.0.1",
|
||||||
Convert.ToInt32(Environment.GetEnvironmentVariable("REDIS_PORT") ?? "6379"),
|
Convert.ToInt32(Environment.GetEnvironmentVariable("REDIS_PORT") ?? "6379"),
|
||||||
Environment.GetEnvironmentVariable("REDIS_PASSWORD")
|
Environment.GetEnvironmentVariable("REDIS_PASSWORD")
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace PhoenixLib.ServiceBus.Extensions
|
||||||
public static void AddMqttConfigurationFromEnv(this IServiceCollection services)
|
public static void AddMqttConfigurationFromEnv(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.TryAddSingleton(s => new MqttConfiguration(
|
services.TryAddSingleton(s => new MqttConfiguration(
|
||||||
Environment.GetEnvironmentVariable("MQTT_BROKER_ADDRESS") ?? "localhost",
|
Environment.GetEnvironmentVariable("MQTT_BROKER_ADDRESS") ?? "127.0.0.1",
|
||||||
Environment.GetEnvironmentVariable("MQTT_BROKER_CLIENT_NAME") ?? "client-" + Guid.NewGuid(),
|
Environment.GetEnvironmentVariable("MQTT_BROKER_CLIENT_NAME") ?? "client-" + Guid.NewGuid(),
|
||||||
Convert.ToInt32(Environment.GetEnvironmentVariable("MQTT_BROKER_PORT") ?? "1883")
|
Convert.ToInt32(Environment.GetEnvironmentVariable("MQTT_BROKER_PORT") ?? "1883")
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace WingsEmu.Communication.gRPC.Extensions
|
||||||
|
|
||||||
public static class ServiceProviderExtensions
|
public static class ServiceProviderExtensions
|
||||||
{
|
{
|
||||||
private const string DEFAULT_IP = "localhost";
|
private const string DEFAULT_IP = "127.0.0.1";
|
||||||
private const string DEFAULT_PORT = "20500";
|
private const string DEFAULT_PORT = "20500";
|
||||||
|
|
||||||
private static void AddGrpcService<T>(this IServiceCollection services, string ipEnvironmentVariable, string portEnvironmentVariable, string portDefault = null) where T : class
|
private static void AddGrpcService<T>(this IServiceCollection services, string ipEnvironmentVariable, string portEnvironmentVariable, string portDefault = null) where T : class
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Plugin.Database.DB
|
||||||
{
|
{
|
||||||
public DatabaseConfiguration()
|
public DatabaseConfiguration()
|
||||||
{
|
{
|
||||||
Ip = Environment.GetEnvironmentVariable("DATABASE_IP") ?? "localhost";
|
Ip = Environment.GetEnvironmentVariable("DATABASE_IP") ?? "127.0.0.1";
|
||||||
Username = Environment.GetEnvironmentVariable("DATABASE_USER") ?? "postgres";
|
Username = Environment.GetEnvironmentVariable("DATABASE_USER") ?? "postgres";
|
||||||
Password = Environment.GetEnvironmentVariable("DATABASE_PASSWORD")
|
Password = Environment.GetEnvironmentVariable("DATABASE_PASSWORD")
|
||||||
?? throw new InvalidOperationException("DATABASE_PASSWORD environment variable is required");
|
?? throw new InvalidOperationException("DATABASE_PASSWORD environment variable is required");
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Plugin.MongoLogs.Utils
|
||||||
?? throw new InvalidOperationException("WINGSEMU_MONGO_PWD environment variable is required");
|
?? throw new InvalidOperationException("WINGSEMU_MONGO_PWD environment variable is required");
|
||||||
|
|
||||||
return new MongoLogsConfiguration(
|
return new MongoLogsConfiguration(
|
||||||
Environment.GetEnvironmentVariable("WINGSEMU_MONGO_HOST") ?? "localhost",
|
Environment.GetEnvironmentVariable("WINGSEMU_MONGO_HOST") ?? "127.0.0.1",
|
||||||
short.Parse(Environment.GetEnvironmentVariable("WINGSEMU_MONGO_PORT") ?? "27017"),
|
short.Parse(Environment.GetEnvironmentVariable("WINGSEMU_MONGO_PORT") ?? "27017"),
|
||||||
Environment.GetEnvironmentVariable("WINGSEMU_MONGO_DB") ?? "wingsemu_logs",
|
Environment.GetEnvironmentVariable("WINGSEMU_MONGO_DB") ?? "wingsemu_logs",
|
||||||
username,
|
username,
|
||||||
|
|
|
||||||
7
toolkit.env
Normal file
7
toolkit.env
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
DATABASE_IP=127.0.0.1
|
||||||
|
DATABASE_PORT=5432
|
||||||
|
DATABASE_NAME=game
|
||||||
|
DATABASE_USER=postgres
|
||||||
|
DATABASE_PASSWORD=postgres
|
||||||
|
TOOLKIT_ADMIN_USERNAME=test
|
||||||
|
TOOLKIT_ADMIN_PASSWORD=test
|
||||||
38
tools/NosClientLauncherGui/Form1.Designer.cs
generated
Normal file
38
tools/NosClientLauncherGui/Form1.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
namespace NosClientLauncherGui;
|
||||||
|
|
||||||
|
partial class Form1
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Required designer variable.
|
||||||
|
/// </summary>
|
||||||
|
private System.ComponentModel.IContainer components = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean up any resources being used.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing && (components != null))
|
||||||
|
{
|
||||||
|
components.Dispose();
|
||||||
|
}
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Windows Form Designer generated code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required method for Designer support - do not modify
|
||||||
|
/// the contents of this method with the code editor.
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeComponent()
|
||||||
|
{
|
||||||
|
components = new System.ComponentModel.Container();
|
||||||
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
|
ClientSize = new Size(800, 450);
|
||||||
|
Text = "Form1";
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
185
tools/NosClientLauncherGui/Form1.cs
Normal file
185
tools/NosClientLauncherGui/Form1.cs
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace NosClientLauncherGui
|
||||||
|
{
|
||||||
|
public class LaunchConfig
|
||||||
|
{
|
||||||
|
public string ClientExePath { get; set; } = @"C:\\NosClient\\NostaleClientX.exe";
|
||||||
|
public string WorkingDirectory { get; set; } = @"C:\\NosClient";
|
||||||
|
public string ServerIp { get; set; } = "127.0.0.1";
|
||||||
|
public int ServerPort { get; set; } = 4001;
|
||||||
|
public string Arguments { get; set; } = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Form1 : Form
|
||||||
|
{
|
||||||
|
private const string ConfigFile = "launcher.config.json";
|
||||||
|
private const string LogFile = "launcher.log";
|
||||||
|
|
||||||
|
private TextBox txtExe = new() { Left = 20, Top = 30, Width = 500 };
|
||||||
|
private Button btnBrowse = new() { Left = 530, Top = 28, Width = 90, Text = "Browse" };
|
||||||
|
|
||||||
|
private TextBox txtWorkDir = new() { Left = 20, Top = 85, Width = 600 };
|
||||||
|
private TextBox txtIp = new() { Left = 20, Top = 140, Width = 250 };
|
||||||
|
private NumericUpDown numPort = new() { Left = 280, Top = 140, Width = 120, Minimum = 1, Maximum = 65535, Value = 4001 };
|
||||||
|
private TextBox txtArgs = new() { Left = 20, Top = 195, Width = 600 };
|
||||||
|
|
||||||
|
private Button btnSave = new() { Left = 20, Top = 245, Width = 120, Text = "Save" };
|
||||||
|
private Button btnLaunch = new() { Left = 150, Top = 245, Width = 120, Text = "Launch" };
|
||||||
|
private TextBox txtLog = new() { Left = 20, Top = 295, Width = 600, Height = 180, Multiline = true, ScrollBars = ScrollBars.Vertical, ReadOnly = true };
|
||||||
|
|
||||||
|
public Form1()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
Text = "Nos Client Launcher";
|
||||||
|
Width = 670;
|
||||||
|
Height = 560;
|
||||||
|
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||||||
|
MaximizeBox = false;
|
||||||
|
|
||||||
|
Controls.Clear();
|
||||||
|
Controls.Add(new Label { Left = 20, Top = 10, Text = "Client EXE" });
|
||||||
|
Controls.Add(txtExe);
|
||||||
|
Controls.Add(btnBrowse);
|
||||||
|
|
||||||
|
Controls.Add(new Label { Left = 20, Top = 65, Text = "Working Directory" });
|
||||||
|
Controls.Add(txtWorkDir);
|
||||||
|
|
||||||
|
Controls.Add(new Label { Left = 20, Top = 120, Text = "Server IP" });
|
||||||
|
Controls.Add(txtIp);
|
||||||
|
Controls.Add(new Label { Left = 280, Top = 120, Text = "Port" });
|
||||||
|
Controls.Add(numPort);
|
||||||
|
|
||||||
|
Controls.Add(new Label { Left = 20, Top = 175, Text = "Arguments (optional)" });
|
||||||
|
Controls.Add(txtArgs);
|
||||||
|
|
||||||
|
Controls.Add(btnSave);
|
||||||
|
Controls.Add(btnLaunch);
|
||||||
|
Controls.Add(txtLog);
|
||||||
|
|
||||||
|
btnBrowse.Click += (_, _) => BrowseExe();
|
||||||
|
btnSave.Click += (_, _) => SaveConfig();
|
||||||
|
btnLaunch.Click += (_, _) => LaunchClient();
|
||||||
|
|
||||||
|
LoadConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BrowseExe()
|
||||||
|
{
|
||||||
|
using var ofd = new OpenFileDialog
|
||||||
|
{
|
||||||
|
Filter = "Executable (*.exe)|*.exe",
|
||||||
|
Title = "Select NosTale Client EXE"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ofd.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
txtExe.Text = ofd.FileName;
|
||||||
|
if (string.IsNullOrWhiteSpace(txtWorkDir.Text))
|
||||||
|
txtWorkDir.Text = Path.GetDirectoryName(ofd.FileName) ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LaunchConfig cfg;
|
||||||
|
if (!File.Exists(ConfigFile))
|
||||||
|
{
|
||||||
|
cfg = new LaunchConfig();
|
||||||
|
File.WriteAllText(ConfigFile, JsonSerializer.Serialize(cfg, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cfg = JsonSerializer.Deserialize<LaunchConfig>(File.ReadAllText(ConfigFile)) ?? new LaunchConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
txtExe.Text = cfg.ClientExePath;
|
||||||
|
txtWorkDir.Text = cfg.WorkingDirectory;
|
||||||
|
txtIp.Text = cfg.ServerIp;
|
||||||
|
numPort.Value = cfg.ServerPort;
|
||||||
|
txtArgs.Text = cfg.Arguments;
|
||||||
|
|
||||||
|
AppendLog("Config loaded.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("LoadConfig ERROR: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveConfig()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cfg = ReadUi();
|
||||||
|
File.WriteAllText(ConfigFile, JsonSerializer.Serialize(cfg, new JsonSerializerOptions { WriteIndented = true }));
|
||||||
|
AppendLog("Config saved.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("SaveConfig ERROR: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LaunchClient()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cfg = ReadUi();
|
||||||
|
if (!File.Exists(cfg.ClientExePath))
|
||||||
|
{
|
||||||
|
MessageBox.Show("Client EXE nicht gefunden.", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveConfig();
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = cfg.ClientExePath,
|
||||||
|
WorkingDirectory = cfg.WorkingDirectory,
|
||||||
|
Arguments = cfg.Arguments,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
|
||||||
|
psi.Environment["NOSTALE_SERVER_IP"] = cfg.ServerIp;
|
||||||
|
psi.Environment["NOSTALE_SERVER_PORT"] = cfg.ServerPort.ToString();
|
||||||
|
|
||||||
|
var p = Process.Start(psi);
|
||||||
|
AppendLog(p != null
|
||||||
|
? $"Client gestartet. PID={p.Id} | Target={cfg.ServerIp}:{cfg.ServerPort}"
|
||||||
|
: "Process.Start returned null.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
AppendLog("Launch ERROR: " + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LaunchConfig ReadUi()
|
||||||
|
{
|
||||||
|
return new LaunchConfig
|
||||||
|
{
|
||||||
|
ClientExePath = txtExe.Text.Trim(),
|
||||||
|
WorkingDirectory = txtWorkDir.Text.Trim(),
|
||||||
|
ServerIp = txtIp.Text.Trim(),
|
||||||
|
ServerPort = (int)numPort.Value,
|
||||||
|
Arguments = txtArgs.Text.Trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendLog(string line)
|
||||||
|
{
|
||||||
|
var msg = $"[{DateTime.Now:HH:mm:ss}] {line}";
|
||||||
|
txtLog.AppendText(msg + Environment.NewLine);
|
||||||
|
File.AppendAllText(LogFile, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {line}{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
tools/NosClientLauncherGui/NosClientLauncherGui.csproj
Normal file
11
tools/NosClientLauncherGui/NosClientLauncherGui.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
29
tools/NosClientLauncherGui/Program.cs
Normal file
29
tools/NosClientLauncherGui/Program.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
using System;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace NosClientLauncherGui
|
||||||
|
{
|
||||||
|
internal static class Program
|
||||||
|
{
|
||||||
|
[STAThread]
|
||||||
|
static void Main()
|
||||||
|
{
|
||||||
|
if (!IsAdministrator())
|
||||||
|
{
|
||||||
|
MessageBox.Show("Bitte den Launcher als Administrator starten (Rechtsklick -> Als Administrator ausführen).", "Admin erforderlich", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationConfiguration.Initialize();
|
||||||
|
Application.Run(new Form1());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsAdministrator()
|
||||||
|
{
|
||||||
|
using var identity = WindowsIdentity.GetCurrent();
|
||||||
|
var principal = new WindowsPrincipal(identity);
|
||||||
|
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
tools/nostale-auth
Submodule
1
tools/nostale-auth
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit c3c4caeb5d6a5d89923c4cadad5c03861c71e97a
|
||||||
Loading…
Reference in a new issue