diff --git a/cache/cache_profiles.go b/cache/cache_profiles.go index a66370b..129ffad 100644 --- a/cache/cache_profiles.go +++ b/cache/cache_profiles.go @@ -44,9 +44,7 @@ func (c *Cache) deleteProfile(data map[string]string) { _, ok1 := c.Profiles[data["profile_name"]] if !ok1 { fmt.Println("Profile deleted") - Database.Unlock() Database.Db.MustExec(Database.Db.Rebind("DELETE FROM urt_profiles WHERE name=?"), data["profile_name"]) - Database.Lock() } else { fmt.Println("Something goes wrong! Profile is still here!") } @@ -81,7 +79,6 @@ func (c *Cache) FlushProfiles(data map[string]string) { } } - Database.Unlock() tx := Database.Db.MustBegin() fmt.Println("Adding new profiles...") for _, profile := range new_profiles { @@ -92,7 +89,6 @@ func (c *Cache) FlushProfiles(data map[string]string) { tx.NamedExec("UPDATE urt_profiles SET name=:name, version=:version, binary=:binary, second_x_session=:second_x_session, additional_parameters=:additional_parameters WHERE name=:name", &profile) } tx.Commit() - Database.Lock() fmt.Println("Done") } diff --git a/cache/cache_servers.go b/cache/cache_servers.go index f85b20f..0b202ee 100644 --- a/cache/cache_servers.go +++ b/cache/cache_servers.go @@ -89,7 +89,6 @@ func (c *Cache) FlushServers(data map[string]string) { } } - Database.Unlock() tx := Database.Db.MustBegin() fmt.Println("Adding new servers...") if len(new_servers) > 0 { @@ -106,7 +105,6 @@ func (c *Cache) FlushServers(data map[string]string) { } tx.Commit() - Database.Lock() fmt.Println("Done") } diff --git a/common/const.go b/common/const.go index a3c9dc4..4990dee 100644 --- a/common/const.go +++ b/common/const.go @@ -17,4 +17,5 @@ const ( var SUPPORTED_URT_VERSIONS []string = []string{ "4.2.023", "4.3.1", + "4.3.2", } diff --git a/configuration/config_object.go b/configuration/config_object.go index 176efb3..22afdcc 100644 --- a/configuration/config_object.go +++ b/configuration/config_object.go @@ -31,6 +31,9 @@ func (c *Config) initializePathsMac() { fmt.Println("Will use data path: " + data_path) c.TEMP["DATA"] = data_path + profile_path := path.Join(home_path, "Library", "Application Support", "Quake3", "q3ut4") + c.TEMP["DEFAULT_PROFILE_PATH"] = profile_path + if _, err := os.Stat(data_path); os.IsNotExist(err) { os.MkdirAll(data_path, 0755) } @@ -46,6 +49,9 @@ func (c *Config) initializePathsNix() { fmt.Println("Will use data path: " + data_path) c.TEMP["DATA"] = data_path + profile_path := path.Join(home_path, ".q3a", "q3ut4") + c.TEMP["DEFAULT_PROFILE_PATH"] = profile_path + if _, err := os.Stat(data_path); os.IsNotExist(err) { os.MkdirAll(data_path, 0755) } @@ -58,6 +64,10 @@ func (c *Config) initializePathsWin() { data_path := path.Join(homedrive, homepath_without_drive, "AppData", "Roaming", "URTrator") c.TEMP["DATA"] = data_path + // Verify it! + profile_path := path.Join(homedrive, homepath_without_drive, "AppData", "UrbanTerror43", "q3ut4") + c.TEMP["DEFAULT_PROFILE_PATH"] = profile_path + if _, err := os.Stat(data_path); os.IsNotExist(err) { os.MkdirAll(data_path, 0755) } diff --git a/database/database_object.go b/database/database_object.go index 466064d..db779be 100644 --- a/database/database_object.go +++ b/database/database_object.go @@ -49,11 +49,13 @@ func (d *Database) Close() { tx.Commit() d.Db.Close() + runtime.UnlockOSThread() } func (d *Database) Initialize(cfg *configuration.Config) { fmt.Println("Initializing database...") + runtime.LockOSThread() // Connect to database. @@ -75,10 +77,6 @@ func (d *Database) Initialize(cfg *configuration.Config) { } } -func (d *Database) Lock() { - runtime.LockOSThread() -} - func (d *Database) Migrate() { // Getting current database version. dbver := 0 @@ -94,7 +92,3 @@ func (d *Database) Migrate() { migrate_full(d, dbver) } - -func (d *Database) Unlock() { - runtime.UnlockOSThread() -} diff --git a/database/migrations.go b/database/migrations.go index 23c4157..19c2646 100644 --- a/database/migrations.go +++ b/database/migrations.go @@ -49,6 +49,7 @@ func migrate_full(db *Database, version int) { if version == 7 {seven_to_eight(db); version = 8} if version == 8 {eight_to_nine(db); version = 9} if version == 9 {nine_to_ten(db); version = 10} + if version == 10 {ten_to_eleven(db); version = 11} } // Initial database structure. @@ -123,3 +124,10 @@ func nine_to_ten(db *Database) { db.Db.MustExec("ALTER TABLE servers ADD bots VARCHAR(2) NOT NULL DEFAULT '0'") db.Db.MustExec("UPDATE database SET version=10") } + +// Urban terror's profile path. +func ten_to_eleven(db *Database) { + fmt.Println("Upgrading database from 10 to 11...") + db.Db.MustExec("ALTER TABLE urt_profiles ADD profile_path VARCHAR(4096) NOT NULL DEFAULT '~/.q3ut4'") + db.Db.MustExec("UPDATE database SET version=11") +} diff --git a/datamodels/profile.go b/datamodels/profile.go index 5aaa255..a8eb9ee 100644 --- a/datamodels/profile.go +++ b/datamodels/profile.go @@ -20,4 +20,6 @@ type Profile struct { Second_x_session string `db:"second_x_session"` // Additional game parameters we will pass. Additional_params string `db:"additional_parameters"` + // Profile path. + Profile_path string `db:"profile_path"` } diff --git a/eventer/eventer_object.go b/eventer/eventer_object.go index acdd097..684a53f 100644 --- a/eventer/eventer_object.go +++ b/eventer/eventer_object.go @@ -17,6 +17,7 @@ import ( // github "github.com/mattn/go-gtk/glib" + "github.com/mattn/go-gtk/gtk" ) type Eventer struct { @@ -55,6 +56,14 @@ func (e *Eventer) LaunchEvent(event string, data map[string]string) error { return false }) + for { + if gtk.EventsPending() { + gtk.MainIteration() + } else { + break + } + } + return nil } diff --git a/requester/pooler.go b/requester/pooler.go index b35e2ab..8add1ef 100644 --- a/requester/pooler.go +++ b/requester/pooler.go @@ -45,7 +45,9 @@ func (p *Pooler) Initialize() { func (p *Pooler) PingOneServer(server_address string) { var wait sync.WaitGroup + Cache.ServersMutex.Lock() server := Cache.Servers[server_address].Server + Cache.ServersMutex.Unlock() wait.Add(1) go func(srv *datamodels.Server) { @@ -63,6 +65,7 @@ func (p *Pooler) PingServers(servers_type string) { cur_requests := 0 var wait sync.WaitGroup + Cache.ServersMutex.Lock() for _, server_to_ping := range Cache.Servers { if servers_type == "favorites" && server_to_ping.Server.Favorite != "1" { continue @@ -90,6 +93,7 @@ func (p *Pooler) PingServers(servers_type string) { }(server_to_ping.Server) } wait.Wait() + Cache.ServersMutex.Unlock() } func (p *Pooler) pingServersExecutor(server *datamodels.Server) error { @@ -132,7 +136,9 @@ func (p *Pooler) pingServersExecutor(server *datamodels.Server) error { func (p *Pooler) UpdateOneServer(server_address string) { var wait sync.WaitGroup + Cache.ServersMutex.Lock() server := Cache.Servers[server_address].Server + Cache.ServersMutex.Unlock() wait.Add(1) go func(server *datamodels.Server) { @@ -151,6 +157,7 @@ func (p *Pooler) UpdateOneServer(server_address string) { func (p *Pooler) UpdateServers(servers_type string) { var wait sync.WaitGroup + Cache.ServersMutex.Lock() for _, server := range Cache.Servers { if servers_type == "favorites" && server.Server.Favorite != "1" { continue @@ -162,6 +169,7 @@ func (p *Pooler) UpdateServers(servers_type string) { }(server.Server) } wait.Wait() + Cache.ServersMutex.Unlock() p.PingServers(servers_type) Eventer.LaunchEvent("flushServers", map[string]string{}) diff --git a/timer/timer.go b/timer/timer.go index 1cd3277..b176664 100644 --- a/timer/timer.go +++ b/timer/timer.go @@ -77,12 +77,14 @@ func (t *Timer) executeTasks() { } func (t *Timer) GetTaskStatus(task_name string) bool { - _, ok := t.tasks[task_name] + t.tasksMutex.Lock() + task, ok := t.tasks[task_name] + t.tasksMutex.Unlock() if !ok { return false } - return t.tasks[task_name].InProgress + return task.InProgress } func (t *Timer) Initialize() { @@ -105,7 +107,9 @@ func (t *Timer) initializeStorage() { } func (t *Timer) RemoveTask(task_name string) { + t.tasksMutex.Lock() _, ok := t.tasks[task_name] + t.tasksMutex.Unlock() if !ok { return } @@ -116,10 +120,12 @@ func (t *Timer) RemoveTask(task_name string) { } func (t *Timer) SetTaskNotInProgress(data map[string]string) { + t.tasksMutex.Lock() _, ok := t.tasks[data["task_name"]] if !ok { return } t.tasks[data["task_name"]].InProgress = false + t.tasksMutex.Unlock() } diff --git a/translator/translator_windows.go b/translator/translator_windows.go index ba441be..e390af2 100644 --- a/translator/translator_windows.go +++ b/translator/translator_windows.go @@ -28,7 +28,7 @@ import ( "os" ) -// Detect language on Unices. +// Detect language on Windows. func (t *Translator) detectLanguage() { fmt.Println("ToDo! Forcing en_US for now!") t.Language = "en_US" diff --git a/ui/gtk2/mainwindow.go b/ui/gtk2/mainwindow.go index e3e3c8c..a9c22e8 100644 --- a/ui/gtk2/mainwindow.go +++ b/ui/gtk2/mainwindow.go @@ -153,6 +153,10 @@ type MainWindow struct { // Used when user changed active tab, to show information about // server which is selected on activated tab. use_other_servers_tab bool + // Does servers updating already in progress? + // This helps to prevent random crashes when more than one + // updating process in progress. + servers_already_updating bool } func (m *MainWindow) addToFavorites() { @@ -617,6 +621,8 @@ func (m *MainWindow) serversUpdateCompleted(data map[string]string) { m.fav_servers.Emit("cursor-changed") } + m.servers_already_updating = false + } func (m *MainWindow) setQuickConnectDetails(data map[string]string) { @@ -628,8 +634,10 @@ func (m *MainWindow) setQuickConnectDetails(data map[string]string) { func (m *MainWindow) setToolbarLabelText(data map[string]string) { fmt.Println("Setting toolbar's label text...") if strings.Contains(data["text"], "") { + fmt.Println("With markup") m.toolbar_label.SetMarkup(data["text"]) } else { + fmt.Println("Without markup") m.toolbar_label.SetLabel(data["text"]) } } @@ -792,6 +800,11 @@ func (m *MainWindow) unlockInterface() { } func (m *MainWindow) updateOneServer() { + if m.servers_already_updating { + return + } + m.servers_already_updating = true + ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "" + ctx.Translator.Translate("Updating selected server...", nil) + ""}) current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage())) srv_address := m.getIpFromServersList(current_tab) @@ -803,6 +816,11 @@ func (m *MainWindow) updateOneServer() { // Triggered when "Update all servers" button is clicked. func (m *MainWindow) UpdateServers() { + if m.servers_already_updating { + return + } + m.servers_already_updating = true + ctx.Eventer.LaunchEvent("setToolbarLabelText", map[string]string{"text": "" + ctx.Translator.Translate("Updating servers...", nil) + ""}) current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage())) fmt.Println("Updating servers on tab '" + current_tab + "'...") diff --git a/ui/gtk2/mainwindow_init.go b/ui/gtk2/mainwindow_init.go index 352dee8..9db8432 100644 --- a/ui/gtk2/mainwindow_init.go +++ b/ui/gtk2/mainwindow_init.go @@ -233,7 +233,7 @@ func (m *MainWindow) InitializeMainMenu() { about_menu.Append(about_menu_sep1) // Drop databases thing. - about_menu_drop_database_data_item := gtk.NewMenuItemWithMnemonic(ctx.Translator.Translate("Drop database data...", nil)) + about_menu_drop_database_data_item := gtk.NewMenuItemWithMnemonic(ctx.Translator.Translate("Drop local caches and settings", nil)) about_menu.Append(about_menu_drop_database_data_item) about_menu_drop_database_data_item.Connect("activate", m.dropDatabasesData) } @@ -335,6 +335,7 @@ func (m *MainWindow) initializeStorages() { // Application isn't initialized. m.initialized = false m.use_other_servers_tab = false + m.servers_already_updating = false // Gamemodes. m.gamemodes = make(map[string]string) m.gamemodes = map[string]string{ diff --git a/ui/gtk2/options.go b/ui/gtk2/options.go index 76c6ef0..c23a126 100644 --- a/ui/gtk2/options.go +++ b/ui/gtk2/options.go @@ -194,7 +194,7 @@ func (o *OptionsDialog) initializeAppearanceTab() { } o.language_combo.SetActive(lang_active) - appearance_table.Attach(o.language_combo, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 5, 5) + appearance_table.Attach(o.language_combo, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 5, 5) appearance_vbox.PackStart(appearance_table, false, true, 0) o.tab_widget.AppendPage(appearance_vbox, gtk.NewLabel(ctx.Translator.Translate("Appearance", nil))) @@ -212,7 +212,7 @@ func (o *OptionsDialog) initializeGeneralTab() { o.show_tray_icon = gtk.NewCheckButtonWithLabel("") o.show_tray_icon.SetTooltipText(ctx.Translator.Translate("Show icon in tray", nil)) - general_table.Attach(o.show_tray_icon, 1, 2, 0, 1, gtk.FILL, gtk.FILL, 5, 5) + general_table.Attach(o.show_tray_icon, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, gtk.FILL, 5, 5) // Autoupdate checkbox. autoupdate_tooltip := ctx.Translator.Translate("Should URTrator check for updates and update itself? Not working now.", nil) @@ -223,7 +223,7 @@ func (o *OptionsDialog) initializeGeneralTab() { o.autoupdate = gtk.NewCheckButtonWithLabel("") o.autoupdate.SetTooltipText(autoupdate_tooltip) - general_table.Attach(o.autoupdate, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5) + general_table.Attach(o.autoupdate, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, gtk.FILL, 5, 5) // Vertical separator. sep := gtk.NewVBox(false, 0) @@ -260,7 +260,7 @@ func (o *OptionsDialog) initializeServersOptionsTab() { o.servers_autoupdate = gtk.NewCheckButtonWithLabel("") o.servers_autoupdate.SetTooltipText(servers_autoupdate_cb_tooptip) - servers_updating_table.Attach(o.servers_autoupdate, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5) + servers_updating_table.Attach(o.servers_autoupdate, 1, 2, 1, 2, gtk.EXPAND | gtk.FILL, gtk.FILL, 5, 5) // Servers update timeout. servers_autoupdate_timeout_tooltip := ctx.Translator.Translate("Timeout which will trigger servers information update, in minutes.", nil) @@ -417,7 +417,7 @@ func (o *OptionsDialog) ShowOptionsDialog() { o.window.SetTitle(ctx.Translator.Translate("URTrator - Options", nil)) o.window.Connect("destroy", o.closeOptionsDialogWithDiscard) o.window.SetModal(true) - o.window.SetSizeRequest(550, 400) + o.window.SetSizeRequest(750, 600) o.window.SetPosition(gtk.WIN_POS_CENTER) o.window.SetIcon(logo) diff --git a/ui/gtk2/options_profile.go b/ui/gtk2/options_profile.go index 2542991..5547402 100644 --- a/ui/gtk2/options_profile.go +++ b/ui/gtk2/options_profile.go @@ -33,6 +33,8 @@ type OptionsProfile struct { profile_name *gtk.Entry // Binary path. binary_path *gtk.Entry + // Profile directory path. + profile_path *gtk.Entry // Urban Terror versions combobox urt_version_combo *gtk.ComboBoxText // Another X session? @@ -42,6 +44,8 @@ type OptionsProfile struct { // File chooser dialog for selecting binary. f *gtk.FileChooserDialog + // Profile directory chooser dialog. + p *gtk.FileChooserDialog // Flags. // This is profile update? @@ -58,6 +62,15 @@ func (op *OptionsProfile) browseForBinary() { op.f.Run() } +func (op *OptionsProfile) browseForProfile() { + op.p = gtk.NewFileChooserDialog(ctx.Translator.Translate("URTrator - Select Urban Terror profile path", nil), op.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT) + op.p.Response(op.browseForProfileHelper) + if op.profile_path.GetText() != "" { + op.p.SetCurrentFolder(op.profile_path.GetText()) + } + op.p.Run() +} + func (op *OptionsProfile) browseForBinaryHelper() { filename := op.f.GetFilename() op.binary_path.SetText(filename) @@ -122,6 +135,16 @@ func (op *OptionsProfile) browseForBinaryHelper() { } } } + + if op.profile_path.GetText() == "" { + op.profile_path.SetText(ctx.Cfg.TEMP["DEFAULT_PROFILE_PATH"]) + } +} + +func (op *OptionsProfile) browseForProfileHelper() { + directory := op.p.GetFilename() + op.profile_path.SetText(directory) + op.p.Destroy() } func (op *OptionsProfile) closeByCancel() { @@ -148,7 +171,7 @@ func (op *OptionsProfile) Initialize(update bool) { op.window.SetPosition(gtk.WIN_POS_CENTER) op.window.SetIcon(logo) - op.table = gtk.NewTable(6, 2, false) + op.table = gtk.NewTable(7, 2, false) op.table.SetRowSpacings(2) // Profile name. @@ -173,8 +196,8 @@ func (op *OptionsProfile) Initialize(update bool) { op.urt_version_combo = gtk.NewComboBoxText() op.urt_version_combo.SetTooltipText(urt_version_tooltip) op.urt_version_combo.AppendText("4.2.023") - op.urt_version_combo.AppendText("4.3.0") op.urt_version_combo.AppendText("4.3.1") + op.urt_version_combo.AppendText("4.3.2") op.urt_version_combo.SetActive(2) op.table.Attach(op.urt_version_combo, 1, 2, 1, 2, gtk.FILL, gtk.FILL, 5, 5) @@ -195,34 +218,53 @@ func (op *OptionsProfile) Initialize(update bool) { binpath_hbox.PackStart(button_select_binary, false, true, 5) op.table.Attach(binpath_hbox, 1, 2, 2, 3, gtk.FILL, gtk.FILL, 0, 0) + // Path to Urban Terror's profile directory. + // Should be in user's home directory automatically, but can be + // changed :). + select_profile_path_tooltip := ctx.Translator.Translate("Urban Terror profile path.\n\nSpecify directory where configs, demos\nand downloaded maps are located.\n\nDefault: $HOME/.q3ut4", nil) + profile_path_hbox := gtk.NewHBox(false, 0) + profile_path_label := gtk.NewLabel(ctx.Translator.Translate("Profile path:", nil)) + profile_path_label.SetTooltipText(select_profile_path_tooltip) + profile_path_label.SetAlignment(0, 0) + op.table.Attach(profile_path_label, 0, 1, 3, 4, gtk.FILL, gtk.SHRINK, 5, 5) + + op.profile_path = gtk.NewEntry() + op.profile_path.SetTooltipText(select_profile_path_tooltip) + button_select_path := gtk.NewButtonWithLabel(ctx.Translator.Translate("Browse", nil)) + button_select_path.SetTooltipText(select_profile_path_tooltip) + button_select_path.Clicked(op.browseForProfile) + profile_path_hbox.PackStart(op.profile_path, true, true, 5) + profile_path_hbox.PackStart(button_select_path, false, true, 5) + op.table.Attach(profile_path_hbox, 1, 2, 3, 4, gtk.FILL, gtk.FILL, 0, 0) + // Should we use additional X session? another_x_tooltip := ctx.Translator.Translate("If this is checked, Urban Terror will be launched in another X session.\n\nThis could help if you're experiencing visual lag, glitches and FPS drops under compositing WMs, like Mutter and KWin.", nil) another_x_label := gtk.NewLabel(ctx.Translator.Translate("Start Urban Terror in another X session?", nil)) another_x_label.SetTooltipText(another_x_tooltip) another_x_label.SetAlignment(0, 0) - op.table.Attach(another_x_label, 0, 1, 3, 4, gtk.FILL, gtk.SHRINK, 5, 5) + op.table.Attach(another_x_label, 0, 1, 4, 5, gtk.FILL, gtk.SHRINK, 5, 5) op.another_x_session = gtk.NewCheckButtonWithLabel("") op.another_x_session.SetTooltipText(another_x_tooltip) // macOS and Windows can't do that :). if runtime.GOOS != "linux" { op.another_x_session.SetSensitive(false) } - op.table.Attach(op.another_x_session, 1, 2, 3, 4, gtk.FILL, gtk.FILL, 5, 5) + op.table.Attach(op.another_x_session, 1, 2, 4, 5, gtk.FILL, gtk.FILL, 5, 5) // Additional game parameters. params_tooltip := ctx.Translator.Translate("Additional parameters that will be passed to Urban Terror executable.", nil) params_label := gtk.NewLabel(ctx.Translator.Translate("Additional parameters:", nil)) params_label.SetTooltipText(params_tooltip) params_label.SetAlignment(0, 0) - op.table.Attach(params_label, 0, 1, 4, 5, gtk.FILL, gtk.SHRINK, 5, 5) + op.table.Attach(params_label, 0, 1, 5, 6, gtk.FILL, gtk.SHRINK, 5, 5) op.additional_parameters = gtk.NewEntry() op.additional_parameters.SetTooltipText(params_tooltip) - op.table.Attach(op.additional_parameters, 1, 2, 4, 5, gtk.FILL, gtk.FILL, 5, 5) + op.table.Attach(op.additional_parameters, 1, 2, 5, 6, gtk.FILL, gtk.FILL, 5, 5) // Invisible thing. inv_label := gtk.NewLabel("") - op.table.Attach(inv_label, 1, 2, 5, 6, gtk.EXPAND, gtk.FILL, 5, 5) + op.table.Attach(inv_label, 1, 2, 6, 7, gtk.EXPAND, gtk.FILL, 5, 5) // The buttons. buttons_box := gtk.NewHBox(false, 0) @@ -266,13 +308,18 @@ func (op *OptionsProfile) InitializeUpdate(profile_name string) { op.profile_name.SetText(profile.Name) op.binary_path.SetText(profile.Binary) op.additional_parameters.SetText(profile.Additional_params) + if profile.Profile_path == "" { + op.profile_path.SetText(ctx.Cfg.TEMP["DEFAULT_PROFILE_PATH"]) + } else { + op.profile_path.SetText(profile.Profile_path) + } if profile.Second_x_session == "1" { op.another_x_session.SetActive(true) } - if profile.Version == "4.3.0" { + if profile.Version == "4.3.1" { op.urt_version_combo.SetActive(1) - } else if profile.Version == "4.3.1" { + } else if profile.Version == "4.3.2" { op.urt_version_combo.SetActive(2) } else { op.urt_version_combo.SetActive(0) @@ -333,6 +380,12 @@ func (op *OptionsProfile) saveProfile() { ctx.Cache.Profiles[profile_name].Profile.Binary = op.binary_path.GetText() ctx.Cache.Profiles[profile_name].Profile.Additional_params = op.additional_parameters.GetText() + if op.profile_path.GetText() == "" { + ctx.Cache.Profiles[profile_name].Profile.Profile_path = "~/.q3ut4" + } else { + ctx.Cache.Profiles[profile_name].Profile.Profile_path = op.profile_path.GetText() + } + if op.another_x_session.GetActive() { ctx.Cache.Profiles[profile_name].Profile.Second_x_session = "1" } else {