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:
|
||||
condition: service_started
|
||||
environment:
|
||||
SERVER_PORT: 4004
|
||||
SERVER_PORT: 4000
|
||||
DEV_LOGIN_BYPASS: "true"
|
||||
LOGIN_PACKET_TRACE: "true"
|
||||
MASTER_IP: master
|
||||
MASTER_PORT: 20500
|
||||
DB_SERVER_IP: database
|
||||
DB_SERVER_PORT: 29999
|
||||
REDIS_IP: redis
|
||||
REDIS_PORT: 6379
|
||||
MQTT_BROKER_ADDRESS: mqtt
|
||||
MQTT_BROKER_PORT: 1883
|
||||
command: ["/app/LoginServer.dll"]
|
||||
ports:
|
||||
- "4004:4004"
|
||||
- "4000:4000"
|
||||
|
||||
gamechannel:
|
||||
build:
|
||||
|
|
@ -170,7 +174,7 @@ services:
|
|||
MAIL_SERVER_PORT: 27777
|
||||
TRANSLATIONS_SERVER_IP: translation
|
||||
TRANSLATIONS_SERVER_PORT: 19999
|
||||
GAME_SERVER_IP: 0.0.0.0
|
||||
GAME_SERVER_IP: 127.0.0.1
|
||||
GAME_SERVER_PORT: 8000
|
||||
GAME_SERVER_CHANNEL_ID: 1
|
||||
GAME_SERVER_GROUP: 1
|
||||
|
|
@ -190,7 +194,7 @@ services:
|
|||
- ./config:/app/config
|
||||
ports:
|
||||
- "8000:8000"
|
||||
- "17500:17500"
|
||||
- "17666:17500"
|
||||
|
||||
database:
|
||||
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;
|
||||
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));
|
||||
Log.Debug($"[NEW_TYPED_AUTH_0577] WRONG_CREDENTIALS : {loadedAccount.Name}");
|
||||
|
|
@ -72,6 +76,15 @@ namespace LoginServer.Handlers
|
|||
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
|
||||
{
|
||||
AccountId = loadedAccount.Id,
|
||||
|
|
@ -136,7 +149,8 @@ namespace LoginServer.Handlers
|
|||
break;
|
||||
|
||||
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));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -140,7 +140,8 @@ namespace LoginServer.Handlers
|
|||
break;
|
||||
|
||||
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));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// Developed by NosWings Team
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
|
@ -52,6 +53,7 @@ namespace LoginServer.Network
|
|||
if (Socket?.RemoteEndPoint is IPEndPoint ip)
|
||||
{
|
||||
IpAddress = ip.Address.ToString();
|
||||
Log.Info($"[LOGIN_SERVER_SESSION] CONNECT from {ip.Address}:{ip.Port}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -65,16 +67,26 @@ namespace LoginServer.Network
|
|||
{
|
||||
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 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))
|
||||
{
|
||||
Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
TriggerHandler(packetHeader.Replace("#", ""), packet);
|
||||
TriggerHandler(packetHeader.Replace("#", ""), packet, payload);
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
|
@ -96,7 +108,42 @@ namespace LoginServer.Network
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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)
|
||||
{
|
||||
Disconnect();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace PhoenixLib.DAL.EFCore.PGSQL
|
|||
|
||||
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 password = Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD")
|
||||
?? throw new InvalidOperationException("POSTGRES_DATABASE_PASSWORD environment variable is required");
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace PhoenixLib.DAL.Redis
|
|||
|
||||
public static RedisConfiguration FromEnv() =>
|
||||
new(
|
||||
Environment.GetEnvironmentVariable("REDIS_IP") ?? "localhost",
|
||||
Environment.GetEnvironmentVariable("REDIS_IP") ?? "127.0.0.1",
|
||||
Convert.ToInt32(Environment.GetEnvironmentVariable("REDIS_PORT") ?? "6379"),
|
||||
Environment.GetEnvironmentVariable("REDIS_PASSWORD")
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace PhoenixLib.ServiceBus.Extensions
|
|||
public static void AddMqttConfigurationFromEnv(this IServiceCollection services)
|
||||
{
|
||||
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(),
|
||||
Convert.ToInt32(Environment.GetEnvironmentVariable("MQTT_BROKER_PORT") ?? "1883")
|
||||
));
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace WingsEmu.Communication.gRPC.Extensions
|
|||
|
||||
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 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()
|
||||
{
|
||||
Ip = Environment.GetEnvironmentVariable("DATABASE_IP") ?? "localhost";
|
||||
Ip = Environment.GetEnvironmentVariable("DATABASE_IP") ?? "127.0.0.1";
|
||||
Username = Environment.GetEnvironmentVariable("DATABASE_USER") ?? "postgres";
|
||||
Password = Environment.GetEnvironmentVariable("DATABASE_PASSWORD")
|
||||
?? 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");
|
||||
|
||||
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"),
|
||||
Environment.GetEnvironmentVariable("WINGSEMU_MONGO_DB") ?? "wingsemu_logs",
|
||||
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