From b46493c8fbbd87910e53181eda294cfda4c5fba3 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sun, 11 Dec 2022 23:37:22 -0500 Subject: [PATCH 001/138] Create the initial functionality for a Web Services module that will handle all remote connections --- Lib/Shared/Includes.ahk | 14 ++ .../WebServices/Entity/WebServiceEntity.ahk | 135 +++++++++++++ .../Entity/WebServiceProviderEntity.ahk | 90 +++++++++ .../Event/WebServiceRequestEvent.ahk | 17 ++ .../Event/WebServiceResponseEvent.ahk | 14 ++ .../WebServices/Events/WebServicesEvents.ahk | 7 + .../LaunchpadLoginWindow.ahk | 29 +++ .../ManageWindow/ManageWebServicesWindow.ahk | 30 +++ .../JwtWebServiceAuthenticator.ahk | 150 ++++++++++++++ .../WebServiceAuthenticatorBase.ahk | 71 +++++++ .../BasicWebServiceRequest.ahk | 3 + .../WebServiceRequestBase.ahk | 191 ++++++++++++++++++ .../CachedWebServiceResponse.ahk | 32 +++ .../HttpReqWebServiceResponse.ahk | 17 ++ .../WebServiceResponseBase.ahk | 57 ++++++ .../WebServices/WebServices.module.json | 76 +++++++ 16 files changed, 933 insertions(+) create mode 100644 Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk create mode 100644 Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk create mode 100644 Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk create mode 100644 Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk create mode 100644 Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk create mode 100644 Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk create mode 100644 Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServices.module.json diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 9cfe560e..c55e0691 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -5,6 +5,20 @@ #Include Modules\Auth\AuthProvider\JwtAuthProvider.ahk #Include Modules\LaunchpadApi\AuthProvider\LaunchpadApiAuthProvider.ahk #Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk +#Include Modules\WebServices\Entity\WebServiceEntity.ahk +#Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk +#Include Modules\WebServices\Event\WebServiceRequestEvent.ahk +#Include Modules\WebServices\Event\WebServiceResponseEvent.ahk +#Include Modules\WebServices\Events\WebServicesEvents.ahk +#Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk +#Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk +#Include Modules\WebServices\WebServiceAuthenticator\JwtWebServiceAuthenticator.ahk +#Include Modules\WebServices\WebServiceAuthenticator\WebServiceAuthenticatorBase.ahk +#Include Modules\WebServices\WebServiceRequest\BasicWebServiceRequest.ahk +#Include Modules\WebServices\WebServiceRequest\WebServiceRequestBase.ahk +#Include Modules\WebServices\WebServiceResponse\CachedWebServiceResponse.ahk +#Include Modules\WebServices\WebServiceResponse\HttpReqWebServiceResponse.ahk +#Include Modules\WebServices\WebServiceResponse\WebServiceResponseBase.ahk #Include Vendor\Gdip_All.ahk #Include Vendor\LV_Constants.ahk #Include Volantis.App\App\AppBase.ahk diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk new file mode 100644 index 00000000..6688f04c --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -0,0 +1,135 @@ +class WebServiceEntity extends AppEntityBase { + cacheObj := "" + stateObj := "" + persistentStateObj := "" + + AuthData[key] { + get => this.GetAuthData(key) + set => this.SetAuthData(key, value) + } + + PersistentAuthData[key] { + get => this.GetPersistentAuthData(key) + set => this.SetPersistentAuthData(key, value) + } + + __New(app, id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, eventMgr, storageObj, idSanitizer, parentEntity := "") { + this.cacheObj := cacheObj + this.stateObj := stateObj + this.persistentStateObj := persistentStateObj + + super.__New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity) + } + + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + className := this.Prototype.__Class + + return %className%( + container.GetApp(), + id, + entityTypeId, + container, + container.Get("cache.web_services"), + container.Get("state.web_services"), + container.Get("state.web_service_persistent"), + eventMgr, + storageObj, + idSanitizer, + parentEntity + ) + } + + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + definitions["Provider"] := Map( + "type", "entity_reference", + "entityType", "web_service_provider" + "required", true + ) + + return definitions + } + + Request(path, method := "", data := "", useAuthentication := -1, cacheResponse := true) { + if (!method) { + method := this["Provider"]["DefaultMethod"] + } + + if (useAuthentication == -1) { + useAuthentication := this["Provider"]["AuthenticateRequestsByDefault"] + } + + return BasicWebServiceRequest(this.eventMgr, this, this.cacheObj, method, path, data, useAuthentication, cacheResponse) + } + + GetAuthData(key := "") { + return this._getStateData(this.stateObj, key) + } + + SetAuthData(keyOrMap, value) { + return this._setStateData(this.stateObj, keyOrMap, value) + } + + ResetAuthData(newData := "") { + if (!newData) { + newData := Map() + } + + this._createStateParents(this.stateObj) + this.stateObj["WebServices"][this.Id]["AuthData"] := newData + this.stateObj.SaveState() + } + + GetPersistentAuthData(key := "") { + return this._getStateData(this.persistentStateObj, key) + } + + SetPersistentAuthData(key, value) { + return this._setStateData(this.persistentStateObj, key, value) + } + + _getStateData(stateObj, key) { + save := this._createStateParents(stateObj) + + if (save) { + stateObj.SaveState() + } + + authData := stateObj["WebServices"][this.Id]["AuthData"] + + if (key) { + authData := (authData.Has(key) ? authData[key] : "") + } + + return authData + } + + _setStateData(stateObj, key, value) { + this._createStateParents(stateObj) + stateObj["WebServices"][this.Id]["AuthData"][key] := value + stateObj.SaveState() + return this + } + + _createStateParents(stateObj) { + modified := false + + if (!stateObj.Has("WebServices")) { + stateObj["WebServices"] := Map() + modified := true + } + + if (!stateObj["WebServices"].Has(this.Id)) { + stateObj["WebServices"][this.Id] := Map() + modified := true + } + + if (!stateObj["WebServices"][this.Id].Has("AuthData")) { + stateObj["WebServices"][this.Id]["AuthData"] := Map() + modified := true + } + + return modified + } +} diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk new file mode 100644 index 00000000..8ee45343 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk @@ -0,0 +1,90 @@ +class WebServiceProviderEntity extends AppEntityBase { + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + definitions["EndpointUrl"] := Map( + "default", "", + "required", true + ) + + definitions["AuthenticationEndpointUrl"] := Map( + "default", "", + "required", false + ) + + definitions["AuthenticationRefreshPath"] := Map( + "default", "", + "required", false + ) + + definitions["IconSrc"] := Map( + "type", "icon_file", + "default", "webhook", + "required", true + ) + + definitions["SupportsAuthentication"] := Map( + "type", "boolean", + "required", false, + "default", false + ) + + definitions["Authenticator"] := Map( + "type", "service_reference", + "servicePrefix", "web_services_authenticator.", + "default", "", + "required", false + ) + + definitions["DefaultMethod"] := Map( + "default", "GET", + "required", false + ) + + definitions["AuthenticateRequestsByDefault"] := Map( + "type", "boolean", + "default", false, + "required", false + ) + + definitions["LoginWindow"] := Map( + "default", "", + "required", false + ) + + definitions["AppKey"] := Map( + "default", "", + "required", false + ) + + return definitions + } + + Url(path, queryParams := "") { + if (InStr(path, "/") != 1) { + path := "/" . path + } + + return UrlObj(this["EndpointUrl"] . path) + .AddQueryParams(queryParams) + } + + FullPath(path) { + url := this.Url(path) + return url.Path + } + + GetAuthenticationRefreshUrl(queryParams := "") { + endpointUrl := this["AuthenticationEndpointUrl"] + ? this["AuthenticationEndpointUrl"] + : this["EndpointUrl"] + refreshPath := this["AuthenticationRefreshPath"] + + if (refreshPath && InStr(refreshPath, "/") != 1) { + refreshPath := "/" . refreshPath + } + + return UrlObj(endpointUrl . refreshPath) + .AddQueryParams(queryParams) + } +} diff --git a/Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk new file mode 100644 index 00000000..1b8ba941 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk @@ -0,0 +1,17 @@ +class WebServiceRequestEvent extends EventBase { + _requestObj := "" + + __New(eventName, requestObj) { + this._requestObj := requestObj + + super.__New(eventName) + } + + Request { + get => this.requestObj + } + + HttpReq { + get => this.Request.GetHttpReq() + } +} diff --git a/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk new file mode 100644 index 00000000..8db376bb --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk @@ -0,0 +1,14 @@ +class WebServiceResponseEvent extends EventBase { + _responseObj := "" + + __New(eventName, responseObj) { + this._responseObj := responseObj + + super.__New(eventName) + } + + Response { + get => this._responseObj + set => this._responseObj := value + } +} diff --git a/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk new file mode 100644 index 00000000..787d5a1a --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk @@ -0,0 +1,7 @@ +class WebServicesEvents { + static WEB_SERVICES_HTTP_REQ_ALTER := 0x4200 + static WEB_SERVICES_REQUEST_PRESEND := 0x4210 + static WEB_SERVICES_CACHED_RESPONSE_CREATED := 0x4215 + static WEB_SERVICES_HTTP_RESPONSE_CREATED := 0x4217 + static WEB_SERVICES_RESPONSE_ALTER := 0x4220 +} \ No newline at end of file diff --git a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk new file mode 100644 index 00000000..53e76866 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk @@ -0,0 +1,29 @@ +class LaunchpadLoginWindow extends FormGuiBase { + entityObj := "" + entityManager := "" + missingFields := Map() + dataSource := "" + + GetDefaultConfig(container, config) { + defaults := super.GetDefaultConfig(container, config) + defaults["title"] := "Login" + defaults["text"] := "Logging in allows enhanced features such as online backup, restore, personalization, and sharing with the community.`n`nIf you'd like to log in, click the `"Get token`" button to go to the launchpad.games site to retrieve a valid login token and then paste it below." + defaults["buttons"] := "*&Login|&Cancel" + return defaults + } + + Controls() { + super.Controls() + this.Add("ButtonControl", "xs y+m vGetAuthToken w150 h30", "Get Token") + this.AddHeading("Login Token") + this.guiObj.AddEdit("vAuthToken xs y+m r1 w" . this.windowSettings["contentWidth"] . " c" . this.themeObj.GetColor("editText")) + } + + OnGetAuthToken(btn, info) { + Run("https://launchpad.games/profile") + } + + ProcessResult(result, submittedData := "") { + return (result == "Login") ? this.guiObj["AuthToken"].Text : "" + } +} diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk new file mode 100644 index 00000000..bc1a3c1f --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -0,0 +1,30 @@ +class ManageWebServicesWindow extends ManageEntitiesWindow { + listViewColumns := Array("ID", "PROVIDER", "NAME", "AUTHENTICATED") + + GetListViewData(lv) { + data := Map() + + for key, webService in this.entityMgr { + data[key] := [ + webService["id"], + webService["Provider"]["name"], + webService["name"], + webService.AuthData["authenticated"] ? "Yes" : "No" + ] + } + + return data + } + + ViewEntity(key) { + ; @todo generic view operation for double-clicking non-editable entities + } + + AddEntity() { + ; @todo Implement generic add dialog and operation + } + + DeleteEntity(key) { + ; @todo Implement generic delete dialog and operation + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk new file mode 100644 index 00000000..e5031f7d --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -0,0 +1,150 @@ +class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { + Login(webServiceEnt, retryCount := 0) { + if (retryCount > this.maxRetries) { + throw OperationFailedException("Login failed after " . retryCount . " tries.") + } + + if (!this._hasRefreshToken(webServiceEnt)) { + this._reauthenticate(webServiceEnt) + } + + success := this._hasRefreshToken(webServiceEnt) + ? this._refreshAuthentication(webServiceEnt) + : false + + if (!success) { + success := this.Login(webServiceEnt, retryCount + 1) + } + + return success + } + + Logout(webServiceEnt) { + webServiceEnt.PersistentAuthData["auth_token"] := "" + webServiceEnt.PersistentAuthData["refresh_token"] := "" + webServiceEnt.ResetAuthData(Map("authenticated", false)) + + return true + } + + RefreshAuthentication(webServiceEnt) { + if (this.NeedsRefresh(webServiceEnt)) { + this.Login(webServiceEnt) + } + } + + AlterRequest(webServiceEnt, request, httpReqObj) { + bearerToken := webServiceEnt.AuthData["auth_token"] + + if (bearerToken) { + httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken + } + } + + _hasRefreshToken(webServiceEnt) { + return !!(webServiceEnt.PersistentAuthData["refresh_token"]) + } + + _reauthenticate(webServiceEnt) { + refreshToken := this._authenticationGui(webServiceEnt) + + if (refreshToken) { + this._setRefreshToken(webServiceEnt, refreshToken) + } + + return refreshToken + } + + _getRefreshToken(webServiceEnt) { + return webServiceEnt.PersistentAuthData["refresh_token"] + } + + _setRefreshToken(webServiceEnt, refreshToken) { + webServiceEnt.PersistentAuthData["refresh_token"] := refreshToken + } + + _extractAuthData(webServiceEnt, response) { + loginData := response.GetJsonData() + authData := Map( + "authenticated", (loginData.Has("user_id") && !!(loginData["user_id"])) + ) + persistentData := Map() + authDataMap := Map() + persistentDataMap := Map( + "user_id", "user_id", + "refresh_token", "refresh_token", + "id_token", "auth_token", + "access_token", "access_token" + ) + skipKeys := [ + "expires_in" + ] + + if (loginData.Has("expires_in")) { + persistentData["expires"] := DateAdd(A_Now, loginData["expires_in"], "S") + } + + for key, val in loginData { + if (persistentDataMap.Has(key)) { + persistentData[persistentDataMap[key]] := loginData[key] + } else if (authDataMap.Has(key)) { + authData[authDataMap[key]] := loginData[key] + } else if (!authData.Has(key) && !persistentData.Has(key)) { + skip := false + + for index, skipKey in skipKeys { + if (key == skipKey) { + skip := true + break + } + } + + if (!skip) { + authData[key] := val + } + } + } + + for key, val in authData { + webServiceEnt.AuthData[key] := val + } + + for key, val in persistentData { + webServiceEnt.PersistentAuthData[key] := val + } + } + + _refreshAuthentication(webServiceEnt) { + apiKey := webServiceEnt["Provider"]["AppKey"] + refreshToken := webServiceEnt.PersistentAuthData["refresh_token"] + refreshUrl := webServiceEnt["Provider"].GetAuthenticationRefreshUrl(Map("token", apiKey)) + response := "" + + if (!apiKey) { + throw OperationFailedException("Missing API key for auth refresh.") + } + + if (!refreshToken) { + throw OperationFailedException("Missing refresh token for auth refresh.") + } + + if (refreshUrl) { + payload := Map( + "grant_type", "refresh_token", + "refresh_token", refreshToken + ) + + response := webServiceEnt.Request(refreshUrl, "POST", payload, false, false).Send() + } + + success := response.IsSuccessful() + + if (response && success) { + this._extractAuthData(webServiceEnt, response) + } else { + webServiceEnt.PersistentAuthData["refresh_token"] := "" + } + + return success + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk new file mode 100644 index 00000000..41b7ab38 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk @@ -0,0 +1,71 @@ +class WebServiceAuthenticatorBase { + refreshThresholdSeconds := 600 + authenticatedStateKey := "authenticated" + expiresStateKey := "expires" + maxRetries := 3 + guiMgr := "" + + __New(guiMgr) { + this.guiMgr := guiMgr + } + + Login(webServiceEnt, retryCount := 0) { + + } + + Logout(webServiceEnt) { + + } + + RefreshAuthentication(webServiceEnt) { + + } + + AlterRequest(webServiceEnt, request, httpReqObj) { + + } + + IsAuthenticated(webServiceEnt) { + return (webServiceEnt.AuthData[this.authenticatedStateKey] && !this.AuthenticationIsExpired(webServiceEnt)) + } + + NeedsRefresh(webServiceEnt) { + needsRefresh := false + + if (this.IsAuthenticated(webServiceEnt)) { + expires := webServiceEnt.AuthData[this.expiresStateKey] + + if (expires) { + diff := DateDiff(A_Now, expires, "S") + needsRefresh := (diff >= (0 - this.refreshThresholdSeconds)) + } else { + needsRefresh := true + } + } else { + needsRefresh := true + } + + return needsRefresh + } + + AuthenticationIsExpired(webServiceEnt) { + expired := true + + if (webServiceEnt.AuthData[this.authenticatedStateKey] && webServiceEnt.AuthData[this.expiresStateKey]) { + expired := (DateDiff(A_Now, webServiceEnt.AuthData["expires"], "S") >= 0) + } + + return expired + } + + _authenticationGui(webServiceEnt) { + loginWindowGui := webServiceEnt["Provider"]["LoginWindow"] + result := "" + + if (loginWindowGui) { + result := this.guiMgr.Dialog(Map("type", loginWindowGui)) + } + + return result + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk new file mode 100644 index 00000000..a723ff4c --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/BasicWebServiceRequest.ahk @@ -0,0 +1,3 @@ +class BasicWebServiceRequest extends WebServiceRequestBase { + +} \ No newline at end of file diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk new file mode 100644 index 00000000..dd5f400d --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -0,0 +1,191 @@ +class WebServiceRequestBase { + eventMgr := "" + webServiceEnt := "" + cacheObj := "" + path := "" + method := "" + data := "" + useAuthentication := false + httpReqObj := "" + responseObj := "" + cacheResponse := true + _url := "" + + Url { + get => this._getUrl() + } + + Response { + get => this.responseObj + } + + __New(eventMgr, webServiceEnt, cacheObj, method := "", path := "", data := "", useAuthentication := false, cacheResponse := true) { + this.eventMgr := eventMgr + this.webServiceEnt := webServiceEnt + + if (!method) { + method := "GET" + } + + this.method := method + + if (path) { + this.path := path + } + + if (data) { + this.data := data + } + + this.useAuthentication := useAuthentication + this.cacheResponse := cacheResponse + } + + GetPath() { + return this.path + } + + SetPath(path := "") { + this.path := path + + if (this._url) { + this._url.Path := this.webServiceEnt["Provider"].Path(path) + } + + return this + } + + GetMethod() { + return this.method + } + + SetMethod(method := "GET") { + this.method := method + + return this + } + + GetData() { + return this.data + } + + SetData(data := "", clearCache := false) { + this.data := data + + if (clearCache) { + this.cacheObj.RemoveItem(this.GetPath()) + } + + return this + } + + GetUseAuthentication() { + return this.useAuthentication + } + + SetUseAuthentication(useAuthentication := false) { + this.useAuthentication := useAuthentication + + return this + } + + GetHttpReq() { + if (!this.httpReqObj) { + this.httpReqObj := WinHttpReq(this.Url) + } + + return this.httpReqObj + } + + SetHttpReq(httpReqObj := "") { + this.httpReqObj := httpReqObj + } + + _getUrl() { + if (!this._url) { + this._url := this.webServiceEnt["Provider"].Url(this.path) + } + + return this._url + } + + Send(resend := false) { + if (resend || !this.responseObj) { + if (this.RequestIsCached()) { + this.responseObj := this._createCachedResponse() + } else { + httpReqObj := this.GetHttpReq() + + if (this.GetUseAuthentication() && this.webServiceEnt["Provider"].Has("Authenticator", false)) { + authenticator := this.webServiceEnt["Provider"]["Authenticator"] + + if (authenticator.NeedsRefresh(this.webServiceEnt)) { + authenticator.RefreshAuthentication(this.webServiceEnt) + } + + authenticator.AlterRequest(this.webServiceEnt, this, httpReqObj) + } + + event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_HTTP_REQ_ALTER, this) + this.eventMgr.DispatchEvent(event) + + event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_REQUEST_PRESEND, this) + this.eventMgr.DispatchEvent(event) + + httpReqObj.Send(this.GetMethod(), this.GetData()) + this.responseObj := this._createHttpReqResponse() + this._cacheResponse() + } + + event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_RESPONSE_ALTER, this, this.responseObj) + this.eventMgr.DispatchEvent(event) + + this.responseObj := event.Response + } + + return this.responseObj + } + + RequestIsCached() { + path := this.GetPath() + + return this.cacheObj.ItemExists(path && !this.cacheObj.ItemNeedsUpdate(path)) + } + + _createCachedResponse() { + response := CachedWebServiceResponse(this.webServiceEnt, this) + + event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_CACHED_RESPONSE_CREATED, this, response) + this.eventMgr.DispatchEvent(event) + + return event.Response + } + + _createHttpReqResponse() { + response := HttpReqWebServiceResponse(this.webServiceEnt, this) + + event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_HTTP_RESPONSE_CREATED, this, response) + this.eventMgr.DispatchEvent(event) + + return event.Response + } + + _cacheResponse() { + if (this.responseObj && this.cacheResponse) { + path := this.GetPath() + + if (this.responseObj.IsSuccessful()) { + body := this.responseObj.GetResponseBody() + + if (body) { + this.cacheObj.WriteItem(path, body, this.responseObj.GetHttpStatusCode()) + } else { + ; Response is empty, delete any existing cache for this item + this.cacheObj.RemoveItem(path) + } + } else if (this.responseObj.IsNotFound()) { + this.cacheObj.SetNotFound(path) + } + } + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk new file mode 100644 index 00000000..a237cfb8 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk @@ -0,0 +1,32 @@ +class CachedWebServiceResponse extends WebServiceResponseBase { + cacheObj := "" + + __New(webServiceEnt, webServiceReq) { + if (webServiceEnt.Has("Cache", false)) { + this.cacheObj := webServiceEnt.cacheObj + } + + super.__New(webServiceEnt, webServiceReq) + } + + GetHttpStatusCode() { + responseCode := 0 + + if (this.cacheObj) { + responseCode := this.cacheObj.GetResponseCode(this.webServiceReq.GetPath()) + } + + return responseCode + } + + GetResponseBody() { + body := "" + path := this.webServiceReq.GetPath() + + if (this.cacheObj.ItemExists(path)) { + body := this.cacheObj.ReadItem(path) + } + + return body + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk new file mode 100644 index 00000000..d7857400 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/HttpReqWebServiceResponse.ahk @@ -0,0 +1,17 @@ +class HttpReqWebServiceResponse extends WebServiceResponseBase { + httpReqObj := "" + + __New(webServiceEnt, webServiceReq) { + this.httpReqObj := webServiceReq.GetHttpReq() + + super.__New(webServiceEnt, webServiceReq) + } + + GetHttpStatusCode() { + return this.httpReqObj.GetStatusCode() + } + + GetResponseBody() { + return Trim(this.httpReqObj.GetResponseData()) + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk new file mode 100644 index 00000000..e7413bfa --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/WebServiceResponseBase.ahk @@ -0,0 +1,57 @@ +class WebServiceResponseBase { + webServiceEnt := "" + webServiceReq := "" + successCodes := [200] + notFoundCodes := [404] + + __New(webServiceEnt, webServiceReq) { + this.webServiceEnt := webServiceEnt + this.webServiceReq := webServiceReq + } + + GetHttpStatusCode() { + return "" + } + + GetResponseBody() { + return "" + } + + GetJsonData() { + body := this.GetResponseBody() + + if (!body) { + body := "{}" + } + + return JsonData().FromString(&body) + } + + IsSuccessful() { + httpCode := this.GetHttpStatusCode() + success := false + + for , successCode in this.successCodes { + if (httpCode == successCode) { + success := true + break + } + } + + return success + } + + IsNotFound() { + httpCode := this.GetHttpStatusCode() + notFound := false + + for , notFoundCode in this.notFoundCodes { + if (httpCode == notFoundCode) { + notFound := true + break + } + } + + return notFound + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json new file mode 100644 index 00000000..835a2290 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -0,0 +1,76 @@ +{ + "module": { + "name": "Web Services", + "type": "AppModule", + "icon": "", + "category": "Web Services", + "tags": ["Launchpad", "LaunchpadBuilder"], + "description": "Enables Launchpad to connect to and authenticate with remove services on the Internet for additional functionality.", + "author": { + "name": "Ben McClure, Volantis Dev", + "url": "https://volantisdev.com" + }, + "website": "https://launchpad.games", + "version": "{{VERSION}}", + "appVersion": "", + "dependencies": [] + }, + "parameters": { + "config.web_services_file": "@@{data_dir}\\WebServices.json", + "config.web_service_providers_file": "@@{data_dir}\\WebServiceProviders.json", + "config.web_services_state_path": "@@{data_dir}\\WebServicesState.json", + "config.web_services_view_mode": "Report", + "entity_type.web_service": { + "name_singular": "Web Service", + "name_plural": "Web Services", + "entity_class": "WebServiceEntity", + "storage_config_storage_parent_key": "WebServices", + "storage_config_path_parameter": "config.web_services_file", + "manager_view_mode_parameter": "config.web_services_view_mode", + "default_icon": "webhook", + "allow_add": true, + "allow_edit": true, + "allow_delete": true, + "manager_gui": "ManageWebServicesWindow", + "manager_link_in_tools_menu": true + }, + "entity_type.web_service_provider": { + "name_singular": "Service Provider", + "name_plural": "Service Providers", + "entity_class": "WebServiceProviderEntity", + "definition_loader_class": "ParameterEntityDefinitionLoader", + "definition_loader_parameter_key": "web_services.providers", + "storage_config_path_parameter": "config.web_service_providers_file" + }, + "web_services.providers.launchpad_api": { + "EndpointUrl": "https://api.launchpad.games/v1", + "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", + "IconSrc": "Logo.ico", + "SupportsAuthentication": true, + "Authenticator": "jwt", + "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", + "LoginWindow": "LaunchpadLoginWindow" + }, + "web_services.services.api": { + "Provider": "launchpad_api" + } + }, + "services": { + "cache_state.web_services": { + "class": "CacheState", + "arguments": ["@{App}", "@@config.cache_dir", "WebServices.json"] + }, + "cache.web_services": { + "class": "FileCache", + "arguments": ["@{App}", "@cache_state.web_services", "@@config.cache_dir", "WebServices"] + }, + "state.web_services": { + "class": "JsonState", + "arguments": ["@{App}", "@@config.web_services_state_path"] + }, + "web_services_authenticator.jwt": { + "class": "JwtWebServiceAuthenticator", + "arguments": ["@manager.gui"] + } + } +} From 506950e4cf5b890425583a3f797b90db072128de Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:24:37 -0500 Subject: [PATCH 002/138] Ignore task output on build-includes task in vscode --- .vscode/tasks.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3002c722..fb00b155 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,7 +10,8 @@ ], "options": { "cwd": "${workspaceFolder}" - } + }, + "problemMatcher": [] }, { "label": "build-launchpad", From 50c60384835588aef9245aa11ceb5456e3ce28b6 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:25:11 -0500 Subject: [PATCH 003/138] Add a ParameterState state class that loads and saves from temporary parameter storage --- Lib/Shared/Includes.ahk | 1 + .../Volantis.App/State/ParameterState.ahk | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 Lib/Shared/Volantis.App/State/ParameterState.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index c55e0691..1dcb7297 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -110,6 +110,7 @@ #Include Volantis.App\State\AppState.ahk #Include Volantis.App\State\CacheState.ahk #Include Volantis.App\State\JsonState.ahk +#Include Volantis.App\State\ParameterState.ahk #Include Volantis.App\State\StateBase.ahk #Include Volantis.Base\CLR\CLR.ahk #Include Volantis.Base\Event\EventBase.ahk diff --git a/Lib/Shared/Volantis.App/State/ParameterState.ahk b/Lib/Shared/Volantis.App/State/ParameterState.ahk new file mode 100644 index 00000000..7e573004 --- /dev/null +++ b/Lib/Shared/Volantis.App/State/ParameterState.ahk @@ -0,0 +1,39 @@ +class ParameterState extends StateBase { + parameterKey := "" + + __New(app, parameterKey, autoLoad := false) { + this.parameterKey := parameterKey + super.__New(app, "", autoLoad) + } + + SaveState(newState := "") { + if (newState != "") { + this.stateMap := newState + } + + if (this.parameterKey) { + this.container.Parameters[this.parameterKey] := this.stateMap + } + + return this.stateMap + } + + LoadState() { + if (this.parameterKey && !this.stateLoaded) { + newState := super.LoadState() + + if (this.container.HasParameter(this.parameterKey)) { + paramValue := this.container.Parameters[this.parameterKey] + + if (HasBase(paramValue, Map.Prototype)) { + newState := paramValue + } + } + + this.stateMap := newState + this.stateLoaded := true + } + + return this.stateMap + } +} From ad7ff99400186ffe2da6e21e50f4f82ed5db7e86 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:25:26 -0500 Subject: [PATCH 004/138] Save the container in StateBase --- Lib/Shared/Volantis.App/State/StateBase.ahk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Shared/Volantis.App/State/StateBase.ahk b/Lib/Shared/Volantis.App/State/StateBase.ahk index 4aaf8b0a..65f0c2b4 100644 --- a/Lib/Shared/Volantis.App/State/StateBase.ahk +++ b/Lib/Shared/Volantis.App/State/StateBase.ahk @@ -1,5 +1,6 @@ class StateBase { app := "" + container := "" stateMap := Map() stateLoaded := false @@ -21,6 +22,7 @@ class StateBase { InvalidParameterException.CheckTypes("StateBase", "app", app, "AppBase") this.app := app + this.container := app.Services if (state != "") { InvalidParameterException.CheckTypes("StateBase", "state", state, "Map") From 1fd39680ac7decdcefdb61a9af73dc6dd5bb03f2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:25:46 -0500 Subject: [PATCH 005/138] Move entity icon retrieval to a separate method in ManageEntitiesWindow --- .../Gui/ManageWindow/ManageEntitiesWindow.ahk | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 4d18b352..15be3be8 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -74,10 +74,9 @@ class ManageEntitiesWindow extends ManageWindowBase { defaultIcon := this.themeObj.GetIconPath(defaultIconName) iconNum := 1 - iconField := this.entityType.definition["icon_field"] for key, entityObj in this.entityMgr { - iconSrc := entityObj[iconField] + iconSrc := this.GetEntityIconSrc(entityObj) if (!InStr(iconSrc, ":\")) { iconSrc := this.themeObj.GetIconPath(iconSrc) @@ -94,6 +93,17 @@ class ManageEntitiesWindow extends ManageWindowBase { return IL } + GetEntityIconSrc(entityObj) { + iconSrc := "" + iconField := this.entityType.definition["icon_field"] + + if (iconField && entityObj.Has(iconField)) { + iconSrc := entityObj[iconField] + } + + return iconSrc + } + OnDoubleClick(LV, rowNum) { key := this.listView.GetRowKey(rowNum) From b8521c2995ef5da538232aa2eab8ead0c46518dc Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:26:02 -0500 Subject: [PATCH 006/138] Make API integration optional in AppEntityBase --- .../Volantis.App/Entity/AppEntityBase.ahk | 97 ++++++++++--------- 1 file changed, 53 insertions(+), 44 deletions(-) diff --git a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk index 5b0e84ce..803fd056 100644 --- a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk +++ b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk @@ -2,6 +2,7 @@ class AppEntityBase extends FieldableEntity { app := "" dataSourcePath := "" existsInDataSource := false + mergeDataFromApi := true __New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity := "") { this.app := app @@ -32,10 +33,12 @@ class AppEntityBase extends FieldableEntity { "weight", 100 ) - groups["api"] := Map( - "name", "API", - "weight", 150 - ) + if (this.mergeDataFromApi) { + groups["api"] := Map( + "name", "API", + "weight", 150 + ) + } return groups } @@ -43,23 +46,25 @@ class AppEntityBase extends FieldableEntity { BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - definitions["DataSourceKeys"] := Map( - "description", "The data source keys to load defaults from, in order.", - "help", "The default data source is 'api' which connects to the default api endpoint (Which can be any HTTP location compatible with Launchpad's API format)", - "default", [this.app.Config["data_source_key"]], - "multiple", true, - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["DataSourceItemKey"] := Map( - "description", "The key that is used to look up the entity's data from configured external data sources.", - "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataSourceKey", - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) + if (this.mergeDataFromApi) { + definitions["DataSourceKeys"] := Map( + "description", "The data source keys to load defaults from, in order.", + "help", "The default data source is 'api' which connects to the default api endpoint (Which can be any HTTP location compatible with Launchpad's API format)", + "default", [this.app.Config["data_source_key"]], + "multiple", true, + "group", "api", + "processValue", false, + "modes", Map("simple", Map("formField", false)) + ) + + definitions["DataSourceItemKey"] := Map( + "description", "The key that is used to look up the entity's data from configured external data sources.", + "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataSourceKey", + "group", "api", + "processValue", false, + "modes", Map("simple", Map("formField", false)) + ) + } definitions["AssetsDir"] := Map( "type", "directory", @@ -109,20 +114,21 @@ class AppEntityBase extends FieldableEntity { } AggregateDataSourceDefaults(includeParentData := true, includeChildData := true) { - defaults := (this.parentEntity != "" && includeParentData) - ? this.parentEntity.AggregateDataSourceDefaults(includeParentData, false) - : Map() + defaults := Map() - ; @todo Uncomment if needed, remove if not - ;this.GetData().SetLayer("ds", defaults) + if (this.mergeDataFromApi) { + defaults := (this.parentEntity != "" && includeParentData) + ? this.parentEntity.AggregateDataSourceDefaults(includeParentData, false) + : defaults - for index, dataSource in this.GetAllDataSources() { - defaults := this.merger.Merge(this.GetDataSourceDefaults(dataSource), defaults) - } + for index, dataSource in this.GetAllDataSources() { + defaults := this.merger.Merge(this.GetDataSourceDefaults(dataSource), defaults) + } - if (includeChildData) { - for key, child in this.GetReferencedEntities(true) { - defaults := this.merger.Merge(child.AggregateDataSourceDefaults(false, includeChildData), defaults) + if (includeChildData) { + for key, child in this.GetReferencedEntities(true) { + defaults := this.merger.Merge(child.AggregateDataSourceDefaults(false, includeChildData), defaults) + } } } @@ -132,7 +138,7 @@ class AppEntityBase extends FieldableEntity { GetAllDataSources() { dataSources := Map() - if (this.Has("DataSourceKeys", false)) { + if (this.mergeDataFromApi && this.Has("DataSourceKeys", false)) { dataSourceKeys := this["DataSourceKeys"] if (!HasBase(dataSourceKeys, Array.Prototype)) { @@ -155,21 +161,24 @@ class AppEntityBase extends FieldableEntity { GetDataSourceDefaults(dataSource) { defaults := Map() - itemKey := this.DiscoverDataSourceItemKey() - if (itemKey) { - dsData := dataSource.ReadJson(itemKey, this.GetDataSourceItemPath()) + if (this.mergeDataFromApi) { + itemKey := this.DiscoverDataSourceItemKey() - if (dsData) { - this.existsInDataSource := true + if (itemKey) { + dsData := dataSource.ReadJson(itemKey, this.GetDataSourceItemPath()) - if (dsData.Has("data")) { - dsData := dsData["data"] - } + if (dsData) { + this.existsInDataSource := true + + if (dsData.Has("data")) { + dsData := dsData["data"] + } - if (dsData.Has("defaults")) { - defaults := this.merger.Merge(dsData["defaults"], defaults) - defaults := this.MergeAdditionalDataSourceDefaults(defaults, dsData) + if (dsData.Has("defaults")) { + defaults := this.merger.Merge(dsData["defaults"], defaults) + defaults := this.MergeAdditionalDataSourceDefaults(defaults, dsData) + } } } } From 73d168f28fd81c7fd22428e3857e219e78632e81 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:26:21 -0500 Subject: [PATCH 007/138] Fix icon references in web services manager window --- .../Gui/ManageWindow/ManageWebServicesWindow.ahk | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index bc1a3c1f..9e1420da 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -1,14 +1,13 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { - listViewColumns := Array("ID", "PROVIDER", "NAME", "AUTHENTICATED") + listViewColumns := Array("SERVICE", "PROVIDER", "AUTHENTICATED") GetListViewData(lv) { data := Map() for key, webService in this.entityMgr { data[key] := [ - webService["id"], - webService["Provider"]["name"], webService["name"], + webService["Provider"]["name"], webService.AuthData["authenticated"] ? "Yes" : "No" ] } @@ -16,6 +15,10 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { return data } + GetEntityIconSrc(entityObj) { + return entityObj["Provider"]["IconSrc"] + } + ViewEntity(key) { ; @todo generic view operation for double-clicking non-editable entities } From 92ab4ad66c847a2899275f8c79379acc340a53d3 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:26:47 -0500 Subject: [PATCH 008/138] Remove old API fields from web services entities --- .../Modules/WebServices/Entity/WebServiceProviderEntity.ahk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk index 8ee45343..c8b02d43 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk @@ -1,4 +1,6 @@ class WebServiceProviderEntity extends AppEntityBase { + mergeDataFromApi := false + BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() From b84087000ac08ae2aa0d96a3cba1a412824f0761 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:27:23 -0500 Subject: [PATCH 009/138] Fix bugs with WebServiceEntity and make the fields not editable if the id is "api" --- .../WebServices/Entity/WebServiceEntity.ahk | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 6688f04c..70642c1b 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -2,6 +2,7 @@ class WebServiceEntity extends AppEntityBase { cacheObj := "" stateObj := "" persistentStateObj := "" + mergeDataFromApi := false AuthData[key] { get => this.GetAuthData(key) @@ -30,8 +31,8 @@ class WebServiceEntity extends AppEntityBase { entityTypeId, container, container.Get("cache.web_services"), + container.Get("state.web_services_tmp"), container.Get("state.web_services"), - container.Get("state.web_service_persistent"), eventMgr, storageObj, idSanitizer, @@ -42,10 +43,15 @@ class WebServiceEntity extends AppEntityBase { BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() + if (this.idVal == "api" && definitions.Has("name")) { + definitions["name"]["editable"] := false + } + definitions["Provider"] := Map( "type", "entity_reference", - "entityType", "web_service_provider" - "required", true + "entityType", "web_service_provider", + "required", true, + "editable", (this.idVal != "api") ) return definitions @@ -77,7 +83,7 @@ class WebServiceEntity extends AppEntityBase { } this._createStateParents(this.stateObj) - this.stateObj["WebServices"][this.Id]["AuthData"] := newData + this.stateObj.State["WebServices"][this.Id]["AuthData"] := newData this.stateObj.SaveState() } @@ -96,7 +102,7 @@ class WebServiceEntity extends AppEntityBase { stateObj.SaveState() } - authData := stateObj["WebServices"][this.Id]["AuthData"] + authData := stateObj.State["WebServices"][this.Id]["AuthData"] if (key) { authData := (authData.Has(key) ? authData[key] : "") @@ -107,7 +113,7 @@ class WebServiceEntity extends AppEntityBase { _setStateData(stateObj, key, value) { this._createStateParents(stateObj) - stateObj["WebServices"][this.Id]["AuthData"][key] := value + stateObj.State["WebServices"][this.Id]["AuthData"][key] := value stateObj.SaveState() return this } @@ -115,18 +121,18 @@ class WebServiceEntity extends AppEntityBase { _createStateParents(stateObj) { modified := false - if (!stateObj.Has("WebServices")) { - stateObj["WebServices"] := Map() + if (!stateObj.State.Has("WebServices")) { + stateObj.State["WebServices"] := Map() modified := true } - if (!stateObj["WebServices"].Has(this.Id)) { - stateObj["WebServices"][this.Id] := Map() + if (!stateObj.State["WebServices"].Has(this.Id)) { + stateObj.State["WebServices"][this.Id] := Map() modified := true } - if (!stateObj["WebServices"][this.Id].Has("AuthData")) { - stateObj["WebServices"][this.Id]["AuthData"] := Map() + if (!stateObj.State["WebServices"][this.Id].Has("AuthData")) { + stateObj.State["WebServices"][this.Id]["AuthData"] := Map() modified := true } From ffd8b48b4b87d43fd4f0adb6d76970a7099be1ea Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:27:41 -0500 Subject: [PATCH 010/138] Fixes to WebServices module file --- Lib/Shared/Modules/WebServices/WebServices.module.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 835a2290..df8f02f3 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -27,6 +27,7 @@ "storage_config_storage_parent_key": "WebServices", "storage_config_path_parameter": "config.web_services_file", "manager_view_mode_parameter": "config.web_services_view_mode", + "definition_loader_parameter_key": "web_services.services", "default_icon": "webhook", "allow_add": true, "allow_edit": true, @@ -43,6 +44,7 @@ "storage_config_path_parameter": "config.web_service_providers_file" }, "web_services.providers.launchpad_api": { + "name": "Launchpad API", "EndpointUrl": "https://api.launchpad.games/v1", "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", "IconSrc": "Logo.ico", @@ -52,6 +54,7 @@ "LoginWindow": "LaunchpadLoginWindow" }, "web_services.services.api": { + "name": "Launchpad API", "Provider": "launchpad_api" } }, @@ -68,6 +71,10 @@ "class": "JsonState", "arguments": ["@{App}", "@@config.web_services_state_path"] }, + "state.web_services_tmp": { + "class": "ParameterState", + "arguments": ["@{App}", "web_services.state.tmp"] + }, "web_services_authenticator.jwt": { "class": "JwtWebServiceAuthenticator", "arguments": ["@manager.gui"] From 7a11449dd3ed24ba2b803e2a56a85d682f09cee1 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:43:41 -0500 Subject: [PATCH 011/138] Fix logo icon reference for launchpad web service provider --- Lib/Shared/Modules/WebServices/WebServices.module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index df8f02f3..bbf68b85 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -47,7 +47,7 @@ "name": "Launchpad API", "EndpointUrl": "https://api.launchpad.games/v1", "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", - "IconSrc": "Logo.ico", + "IconSrc": "Logo", "SupportsAuthentication": true, "Authenticator": "jwt", "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", From e2461aafcbf2ae168af095d89f8540bd25de35f4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:44:17 -0500 Subject: [PATCH 012/138] Make Provider field never editable after creating a Web Service entity --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 70642c1b..49991050 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -51,7 +51,7 @@ class WebServiceEntity extends AppEntityBase { "type", "entity_reference", "entityType", "web_service_provider", "required", true, - "editable", (this.idVal != "api") + "editable", false ) return definitions From 286624c8320360ec6eea56c84ddfab7b88f707f7 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:45:29 -0500 Subject: [PATCH 013/138] Allow runtime config storage entities (entities that don't save data to a file directly) --- .../Volantis.Entity/Factory/EntityTypeFactory.ahk | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk index 88d866bd..8383c8fa 100644 --- a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk +++ b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk @@ -40,7 +40,8 @@ class EntityTypeFactory { "manager_view_mode_parameter", "", "manager_gui", "ManageEntitiesWindow", "manager_link_in_tools_menu", false, - "manager_menu_link_text", "" + "manager_menu_link_text", "", + "storage_type", "persistent" ) } @@ -97,7 +98,7 @@ class EntityTypeFactory { ), ) - if (definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) { + if (definition["storage_type"] == "persistent" && definition["storage_class"] == "ConfigEntityStorage" && definition["storage_config_path_parameter"]) { services["config_storage." . id] := Map( "class", definition["storage_config_storage_class"], "arguments", ["@@" . definition["storage_config_path_parameter"], definition["storage_config_storage_parent_key"]] @@ -107,6 +108,11 @@ class EntityTypeFactory { "class", "PersistentConfig", "arguments", ["@config_storage." . id, "@{}", "entity_data." . id] ) + } else if (definition["storage_type"] == "runtime") { + services["config." . id] := Map( + "class", "RuntimeConfig", + "arguments", ["@{}", "entity_data." . id] + ) } entityClass := definition["entity_class"] From d94ce496dc1a643a66bd9deb3a90ad6f9ec8a482 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:46:02 -0500 Subject: [PATCH 014/138] Make web_service_provider entity type store its data in runtime config only --- Lib/Shared/Modules/WebServices/WebServices.module.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index bbf68b85..2b8cff25 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -17,7 +17,6 @@ }, "parameters": { "config.web_services_file": "@@{data_dir}\\WebServices.json", - "config.web_service_providers_file": "@@{data_dir}\\WebServiceProviders.json", "config.web_services_state_path": "@@{data_dir}\\WebServicesState.json", "config.web_services_view_mode": "Report", "entity_type.web_service": { @@ -41,7 +40,7 @@ "entity_class": "WebServiceProviderEntity", "definition_loader_class": "ParameterEntityDefinitionLoader", "definition_loader_parameter_key": "web_services.providers", - "storage_config_path_parameter": "config.web_service_providers_file" + "storage_type": "runtime" }, "web_services.providers.launchpad_api": { "name": "Launchpad API", From 6977913002730b6e99f8feaa24fb2f241b33025e Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 00:54:59 -0500 Subject: [PATCH 015/138] Add helper methods Login, Logout, and IsAuthenticated to WebServiceEntity, as well as Authenticated property --- .../WebServices/Entity/WebServiceEntity.ahk | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 49991050..d483cec1 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -4,6 +4,10 @@ class WebServiceEntity extends AppEntityBase { persistentStateObj := "" mergeDataFromApi := false + Authenticated { + get => this.IsAuthenticated() + } + AuthData[key] { get => this.GetAuthData(key) set => this.SetAuthData(key, value) @@ -40,6 +44,28 @@ class WebServiceEntity extends AppEntityBase { ) } + IsAuthenticated() { + isAuthenticated := false + + if (this["Provider"]["SupportsAuthentication"]) { + isAuthenticated := this["Provider"]["Authenticator"].IsAuthenticated(this) + } + + return isAuthenticated + } + + Login() { + if (this["Provider"]["SupportsAuthentication"]) { + this["Provider"]["Authenticator"].Login(this) + } + } + + Logout() { + if (this["Provider"]["SupportsAuthentication"]) { + this["Provider"]["Authenticator"].Logout(this) + } + } + BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() From 6e296b958ae9a5eb2568e7757d572bf296f4d268 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:44:28 -0500 Subject: [PATCH 016/138] Store the request object in WebServiceResponseEvent as well --- .../Modules/WebServices/Event/WebServiceResponseEvent.ahk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk index 8db376bb..c2d5773d 100644 --- a/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk +++ b/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk @@ -1,12 +1,18 @@ class WebServiceResponseEvent extends EventBase { + _requestObj := "" _responseObj := "" - __New(eventName, responseObj) { + __New(eventName, requestObj, responseObj) { + this._requestObj := requestObj this._responseObj := responseObj super.__New(eventName) } + Request { + get => this._requestObj + } + Response { get => this._responseObj set => this._responseObj := value From e5b16fd76fb92782dabb640aaf955aa6cd2b686b Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:45:01 -0500 Subject: [PATCH 017/138] Add Login or Logout context menu item to Web Services --- .../ManageWindow/ManageWebServicesWindow.ahk | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index 9e1420da..9509d307 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -19,15 +19,47 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { return entityObj["Provider"]["IconSrc"] } + GetContextMenuItems(entityObj) { + menuItems := super.GetContextMenuItems(entityObj) + + if (entityObj["Provider"]["SupportsAuthentication"]) { + if (entityObj.Authenticated) { + menuItems.InsertAt(1, Map("label", "&Logout", "name", "WebServiceLogout")) + } else { + menuItems.InsertAt(1, Map("label", "&Login", "name", "WebServiceLogin")) + } + } + + return menuItems + } + + ProcessContextMenuResult(result, key) { + if (result == "WebServiceLogout") { + this.Logout(key) + } else if (result == "WebServiceLogin") { + this.Login(key) + } else { + super.ProcessContextMenuResult(result, key) + } + } + + Logout(key) { + return this.entityMgr[key].Logout() + } + + Login(key) { + return this.entityMgr[key].Login() + } + ViewEntity(key) { - ; @todo generic view operation for double-clicking non-editable entities + entityObj := this.entityMgr[key] } AddEntity() { - ; @todo Implement generic add dialog and operation + } DeleteEntity(key) { - ; @todo Implement generic delete dialog and operation + entityObj := this.entityMgr[key] } } From 6481fd4612185f0497bd28163fab138126108f92 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:45:44 -0500 Subject: [PATCH 018/138] Add missing cache assignment --- .../WebServices/WebServiceRequest/WebServiceRequestBase.ahk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk index dd5f400d..fff11911 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -22,6 +22,8 @@ class WebServiceRequestBase { __New(eventMgr, webServiceEnt, cacheObj, method := "", path := "", data := "", useAuthentication := false, cacheResponse := true) { this.eventMgr := eventMgr this.webServiceEnt := webServiceEnt + this.cacheObj := cacheObj + if (!method) { method := "GET" From 35cdf821fc2da472a2577785544b7c8a2a7a4570 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:46:16 -0500 Subject: [PATCH 019/138] Support passing UrlObj objects into the "path" parameter of WebServiceRequestBase --- .../WebServices/WebServiceRequest/WebServiceRequestBase.ahk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk index fff11911..063dc665 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -24,6 +24,10 @@ class WebServiceRequestBase { this.webServiceEnt := webServiceEnt this.cacheObj := cacheObj + if (HasBase(path, UrlObj.Prototype)) { + this._url := path + path := this._url.Path + } if (!method) { method := "GET" @@ -151,7 +155,7 @@ class WebServiceRequestBase { RequestIsCached() { path := this.GetPath() - return this.cacheObj.ItemExists(path && !this.cacheObj.ItemNeedsUpdate(path)) + return (this.cacheObj.ItemExists(path) && !this.cacheObj.ItemNeedsUpdate(path)) } _createCachedResponse() { From ceccd66ad7e22f589dd51ac75510f07fc34d9496 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:46:46 -0500 Subject: [PATCH 020/138] Add entityObj parameter to GetContextMenuItems method in ManageEntities window --- .../Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 15be3be8..41d57fab 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -159,7 +159,7 @@ class ManageEntitiesWindow extends ManageWindowBase { this.AutoXYWH("y", ["AddButton"]) } - GetContextMenuItems() { + GetContextMenuItems(entityObj) { definition := this.entityType.definition menuItems := [] @@ -182,7 +182,7 @@ class ManageEntitiesWindow extends ManageWindowBase { key := this.listView.GetRowKey(item) entityObj := this.entityMgr[key] - menuItems := this.GetContextMenuItems() + menuItems := this.GetContextMenuItems(entityObj) result := this.app.Service("manager.gui").Menu(menuItems, this) this.ProcessContextMenuResult(result, key) } From 4a50a006655c667ca0574cec35ef0f791260e6e8 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 01:47:01 -0500 Subject: [PATCH 021/138] Fix regex definition in UrlObj --- Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk b/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk index 18a0e7e4..1b011249 100644 --- a/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk +++ b/Lib/Shared/Volantis.Base/UrlObj/UrlObj.ahk @@ -51,19 +51,21 @@ class UrlObj { urlMap := Map() urlParts := "" - regexStr := "^((P[^:/?#]+):)?(//(P[^/?#]*))?(P[^?#]*)(\?(P[^#]*))?(#(P.*))?" - isUrl := RegExMatch(urlStr, regexStr, urlParts) - - loop urlParts.Count { - matchName := urlParts.Name[A_Index] - matchVal := urlParts[A_Index] - - if (matchName) { - if (matchName == "query") { - matchVal := this._splitQueryStr(matchVal) + regexStr := "^((?P[^:/?#]+):)?(//(?P[^/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?" + isUrl := RegExMatch(urlStr, regexStr, &urlParts) + + if (urlParts) { + loop urlParts.Count { + matchName := urlParts.Name[A_Index] + matchVal := urlParts[A_Index] + + if (matchName) { + if (matchName == "query") { + matchVal := this._splitQueryStr(matchVal) + } + + urlMap[matchName] := matchVal } - - urlMap[matchName] := matchVal } } From bbf5517355a1bcd9875cbb44133b460e5a192a61 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:09:55 -0500 Subject: [PATCH 022/138] Fix reference to PlatformsWindow --- Launchpad.services.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index 6a725f60..0fe6765a 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -66,7 +66,7 @@ "storage_config_path_parameter": "config.platforms_file", "manager_view_mode_parameter": "config.platforms_view_mode", "default_icon": "Platform", - "manager_gui": "ManagePlatformsWindow", + "manager_gui": "PlatformsWindow", "manager_link_in_tools_menu": true }, "entity_type.task": { From 6352c4b41499cffff1766be0ba2d342eb3ba5506 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:10:07 -0500 Subject: [PATCH 023/138] Hide Basic platform from Platforms window --- .../Gui/ManageWindow/PlatformsWindow.ahk | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk index 51a526af..5b13061f 100644 --- a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk @@ -4,7 +4,7 @@ class PlatformsWindow extends ManageWindowBase { __New(container, themeObj, config) { this.platformManager := container.Get("entity_manager.platform") - this.lvCount := this.platformManager.Count(true) + this.lvCount := this.platformManager.Count(true) - 1 super.__New(container, themeObj, config) } @@ -24,10 +24,12 @@ class PlatformsWindow extends ManageWindowBase { data := Map() for key, platform in this.platformManager { - enabledText := platform["IsEnabled"] ? "Yes" : "No" - detectGamesText := platform["DetectGames"] ? "Yes" : "No" - installedText := platform["IsInstalled"] ? "Yes" : "No" - data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]] + if (key != "Basic") { + enabledText := platform["IsEnabled"] ? "Yes" : "No" + detectGamesText := platform["DetectGames"] ? "Yes" : "No" + installedText := platform["IsInstalled"] ? "Yes" : "No" + data[key] := [platform.GetName(), enabledText, detectGamesText, installedText, platform["InstalledVersion"]] + } } return data @@ -47,14 +49,17 @@ class PlatformsWindow extends ManageWindowBase { iconNum := 1 for key, platform in this.platformManager { - iconSrc := platform["IconSrc"] - if (!iconSrc or !FileExist(iconSrc)) { - iconSrc := defaultIcon - } + if (key != "Basic") { + iconSrc := platform["IconSrc"] - IL_Add(IL, iconSrc) - iconNum++ + if (!iconSrc or !FileExist(iconSrc)) { + iconSrc := defaultIcon + } + + IL_Add(IL, iconSrc) + iconNum++ + } } return IL From 8a1279ce9770aeeca5e42deafc48245fb772648f Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:31:52 -0500 Subject: [PATCH 024/138] Standardize module versions for display --- Lib/Shared/Volantis.Module/Module/ModuleBase.ahk | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk index af874ef4..2798c9ed 100644 --- a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk +++ b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk @@ -28,7 +28,20 @@ class ModuleBase { } GetVersion() { - return this.moduleInfo.Has("version") ? this.moduleInfo["version"] : "" + versionStr := this.moduleInfo.Has("version") ? this.moduleInfo["version"] : "" + + if (versionStr == "{{VERSION}}") { + + if (AppBase.Instance) { + versionStr := AppBase.Instance.Version + } + + if (versionStr == "{{VERSION}}") { + versionStr := "Built-in" + } + } + + return versionStr } GetServiceFiles() { From 9adba30b5caed83619271c23450dbb1cd49b38c0 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:32:52 -0500 Subject: [PATCH 025/138] Pass isCore value in to each module constructor for safety --- Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk | 2 +- Lib/Shared/Volantis.Module/Module/ModuleBase.ahk | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk b/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk index b873f5a9..e5b73f15 100644 --- a/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk +++ b/Lib/Shared/Volantis.Module/Factory/ModuleFactory.ahk @@ -22,7 +22,7 @@ class ModuleFactory { ), "module." . key, Map( "class", this.classMap.Has(key) ? this.classMap[key] : "SimpleModule", - "arguments", [key, "@module_info." . key, "@module_config." . key], + "arguments", [key, "@module_info." . key, "@module_config." . key, isCore], "file", file, "enabled", enabled, "core", isCore, diff --git a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk index 2798c9ed..d8f03ca6 100644 --- a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk +++ b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk @@ -2,11 +2,13 @@ class ModuleBase { moduleInfo := "" config := "" key := "" + _core := false - __New(key, moduleInfo, config) { + __New(key, moduleInfo, config, isCore) { this.key := key this.moduleInfo := moduleInfo this.config := config + this._core := isCore } IsEnabled() { @@ -14,7 +16,7 @@ class ModuleBase { } IsCore() { - return (this.config.Has("core") && this.config["core"]) + return this._core } GetConfigValue(key, defaultValue := "") { From ab8d1a5c2638ec4ff35dfbb7a91ac1d4395553a6 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:36:08 -0500 Subject: [PATCH 026/138] Replace "Build-in" version string with "Core" --- Lib/Shared/Volantis.Module/Module/ModuleBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk index d8f03ca6..2712c2b5 100644 --- a/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk +++ b/Lib/Shared/Volantis.Module/Module/ModuleBase.ahk @@ -39,7 +39,7 @@ class ModuleBase { } if (versionStr == "{{VERSION}}") { - versionStr := "Built-in" + versionStr := "Core" } } From 591c10a471c3dbce20a5fd12f52a465a74358a83 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 02:36:36 -0500 Subject: [PATCH 027/138] Add category, source, and better module version strings to module manager --- Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk index bc961de2..7cc12037 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk @@ -1,5 +1,5 @@ class ManageModulesWindow extends ManageWindowBase { - listViewColumns := Array("NAME", "ENABLED", "SOURCE", "VERSION") + listViewColumns := Array("NAME", "CATEGORY", "ENABLED", "SOURCE", "VERSION") moduleManager := "" needsRestart := false @@ -26,9 +26,8 @@ class ManageModulesWindow extends ManageWindowBase { for name, module in this.moduleManager.All("", false, true) { enabledText := module.IsEnabled() ? "Yes" : "No" - ; TODO Define source - source := "" - data[name] := [name, enabledText, source, module.GetVersion()] + source := module.IsCore() ? "Built-in" : "Third-party" + data[name] := [name, module.moduleInfo["category"], enabledText, source, module.GetVersion()] } return data From b72c3bafd7c76c0122983b084ab9ab67aaa0aa76 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 10:46:06 -0500 Subject: [PATCH 028/138] Standardize all graphics resources to lower-case --- Launchpad.ahk | 2 +- Launchpad.services.json | 4 ++-- LaunchpadOverlay/LaunchpadOverlay.rc | 2 +- LaunchpadTest.ahk | 2 +- Lib/Launchpad/Entity/LauncherEntity.ahk | 2 +- .../Gui/ManageWindow/DetectedGamesWindow.ahk | 2 +- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 4 ++-- .../Gui/ManageWindow/ManageBackupsWindow.ahk | 2 +- .../Gui/ManageWindow/ManageModulesWindow.ahk | 2 +- Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk | 2 +- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 2 +- .../Modules/WebServices/WebServices.module.json | 2 +- Lib/Shared/Volantis.App/Entity/BackupEntity.ahk | 2 +- Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk | 2 +- .../Graphics/Icons/Dark/{Backup.ico => backup.ico} | Bin .../Graphics/Icons/Dark/{Config.ico => config.ico} | Bin .../Graphics/Icons/Dark/{Game.ico => game.ico} | Bin .../Graphics/Icons/Dark/{Logo.ico => logo.ico} | Bin .../Graphics/Icons/Dark/{Module.png => module.png} | Bin .../Icons/Dark/{Platform.ico => platform.ico} | Bin .../Icons/Gradient/{Backup.ico => backup.ico} | Bin .../Icons/Gradient/{Config.ico => config.ico} | Bin .../Graphics/Icons/Gradient/{Game.ico => game.ico} | Bin .../Graphics/Icons/Gradient/{Logo.ico => logo.ico} | Bin .../Icons/Gradient/{Platform.ico => platform.ico} | Bin .../Graphics/Icons/Light/{Backup.ico => backup.ico} | Bin .../Graphics/Icons/Light/{Config.ico => config.ico} | Bin .../Graphics/Icons/Light/{Game.ico => game.ico} | Bin .../Graphics/Icons/Light/{Logo.ico => logo.ico} | Bin .../Graphics/Icons/Light/{Module.png => module.png} | Bin .../Icons/Light/{Platform.ico => platform.ico} | Bin .../{Launchpad-256.png => launchpad-256.png} | Bin .../Graphics/{Launchpad-64.png => launchpad-64.png} | Bin Resources/Graphics/{Launchpad.ico => launchpad.ico} | Bin Resources/Graphics/{Logo.png => logo.png} | Bin .../{Spinner-Steam.gif => spinner-steam.gif} | Bin Resources/Graphics/{Spinner.gif => spinner.gif} | Bin Resources/Themes/Lightpad.json | 6 +++--- Resources/Themes/Steampad.json | 2 +- Scripts/Build.ahk | 2 +- 40 files changed, 21 insertions(+), 21 deletions(-) rename Resources/Graphics/Icons/Dark/{Backup.ico => backup.ico} (100%) rename Resources/Graphics/Icons/Dark/{Config.ico => config.ico} (100%) rename Resources/Graphics/Icons/Dark/{Game.ico => game.ico} (100%) rename Resources/Graphics/Icons/Dark/{Logo.ico => logo.ico} (100%) rename Resources/Graphics/Icons/Dark/{Module.png => module.png} (100%) rename Resources/Graphics/Icons/Dark/{Platform.ico => platform.ico} (100%) rename Resources/Graphics/Icons/Gradient/{Backup.ico => backup.ico} (100%) rename Resources/Graphics/Icons/Gradient/{Config.ico => config.ico} (100%) rename Resources/Graphics/Icons/Gradient/{Game.ico => game.ico} (100%) rename Resources/Graphics/Icons/Gradient/{Logo.ico => logo.ico} (100%) rename Resources/Graphics/Icons/Gradient/{Platform.ico => platform.ico} (100%) rename Resources/Graphics/Icons/Light/{Backup.ico => backup.ico} (100%) rename Resources/Graphics/Icons/Light/{Config.ico => config.ico} (100%) rename Resources/Graphics/Icons/Light/{Game.ico => game.ico} (100%) rename Resources/Graphics/Icons/Light/{Logo.ico => logo.ico} (100%) rename Resources/Graphics/Icons/Light/{Module.png => module.png} (100%) rename Resources/Graphics/Icons/Light/{Platform.ico => platform.ico} (100%) rename Resources/Graphics/{Launchpad-256.png => launchpad-256.png} (100%) rename Resources/Graphics/{Launchpad-64.png => launchpad-64.png} (100%) rename Resources/Graphics/{Launchpad.ico => launchpad.ico} (100%) rename Resources/Graphics/{Logo.png => logo.png} (100%) rename Resources/Graphics/{Spinner-Steam.gif => spinner-steam.gif} (100%) rename Resources/Graphics/{Spinner.gif => spinner.gif} (100%) diff --git a/Launchpad.ahk b/Launchpad.ahk index 00552dc8..6e5d905f 100644 --- a/Launchpad.ahk +++ b/Launchpad.ahk @@ -15,6 +15,6 @@ Launchpad(Map( "appName", "Launchpad", "developer", "Volantis Development", "version", appVersion, - "trayIcon", "Resources\Graphics\Launchpad.ico", + "trayIcon", "Resources\Graphics\launchpad.ico", "console", true )) diff --git a/Launchpad.services.json b/Launchpad.services.json index 0fe6765a..957f87ec 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -33,7 +33,7 @@ "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", "manager_view_mode_parameter": "config.launcher_view_mode", - "default_icon": "Game", + "default_icon": "game", "allow_add": true, "allow_edit": true, "allow_delete": true, @@ -65,7 +65,7 @@ "storage_config_storage_parent_key": "Platforms", "storage_config_path_parameter": "config.platforms_file", "manager_view_mode_parameter": "config.platforms_view_mode", - "default_icon": "Platform", + "default_icon": "platform", "manager_gui": "PlatformsWindow", "manager_link_in_tools_menu": true }, diff --git a/LaunchpadOverlay/LaunchpadOverlay.rc b/LaunchpadOverlay/LaunchpadOverlay.rc index a2535bad..2e1c9f22 100644 --- a/LaunchpadOverlay/LaunchpadOverlay.rc +++ b/LaunchpadOverlay/LaunchpadOverlay.rc @@ -52,7 +52,7 @@ END // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. -IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\Launchpad.ico" +IDI_ICON1 ICON "E:\\Tools\\Launchpad\\Resources\\Graphics\\launchpad.ico" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/LaunchpadTest.ahk b/LaunchpadTest.ahk index 304cb70f..c9720435 100644 --- a/LaunchpadTest.ahk +++ b/LaunchpadTest.ahk @@ -13,7 +13,7 @@ appVersion := "1.0.0" -TraySetIcon("Resources\Graphics\Launchpad.ico") +TraySetIcon("Resources\Graphics\launchpad.ico") HtmlResultViewer("Launchpad Test") .ViewResults(SimpleTestRunner(FilesystemTestLoader().GetTests()).RunTests()) diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index edaec377..71f3f1a1 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -471,7 +471,7 @@ class LauncherEntity extends AppEntityBase { detectedValues["IconSrc"] := this["ManagedGame"].LocateExe() } else { theme := this.container.Get("manager.theme").GetComponent() - detectedValues["IconSrc"] := theme.GetIconPath("Game") + detectedValues["IconSrc"] := theme.GetIconPath("game") } } diff --git a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk index e78ab041..5d9aed4f 100644 --- a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk @@ -83,7 +83,7 @@ GetListViewImgList(lv, large := false) { IL := IL_Create(this.detectedGames.Count, 1, large) - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") iconNum := 1 for key, detectedGameObj in this.detectedGames { diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 25dfa92e..6e84d702 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -513,7 +513,7 @@ GetListViewImgList(lv, large := false) { IL := IL_Create(this.launcherManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") iconNum := 1 for key, launcher in this.launcherManager { @@ -533,7 +533,7 @@ GetItemImage(launcher) { iconSrc := launcher["IconSrc"] assetIcon := launcher["AssetsDir"] . "\" . launcher.Id . ".ico" - defaultIcon := this.themeObj.GetIconPath("Game") + defaultIcon := this.themeObj.GetIconPath("game") if ((!iconSrc || !FileExist(iconSrc)) && FileExist(assetIcon)) { iconSrc := assetIcon diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk index a73baba9..3ceb36f4 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk @@ -40,7 +40,7 @@ class ManageBackupsWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.backupManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Backup") + defaultIcon := this.themeObj.GetIconPath("backup") iconNum := 1 for key, backup in this.backupManager { diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk index 7cc12037..ec0e10b3 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk @@ -43,7 +43,7 @@ class ManageModulesWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.lvCount, 1, large) - defaultIcon := this.themeObj.GetIconPath("Module") + defaultIcon := this.themeObj.GetIconPath("module") iconNum := 1 for key, module in this.moduleManager.All("", false, true) { diff --git a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk index 5b13061f..845423b6 100644 --- a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk @@ -45,7 +45,7 @@ class PlatformsWindow extends ManageWindowBase { GetListViewImgList(lv, large := false) { IL := IL_Create(this.platformManager.Count(true), 1, large) - defaultIcon := this.themeObj.GetIconPath("Platform") + defaultIcon := this.themeObj.GetIconPath("platform") iconNum := 1 for key, platform in this.platformManager { diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 7397b273..d787c321 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -7,7 +7,7 @@ class LaunchpadBuilder extends AppBase { parameters["config.api_authentication"] := true parameters["config.dist_dir"] := this.appDir . "\Dist" parameters["config.build_dir"] := this.appDir . "\Build" - parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\Launchpad.ico" + parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\launchpad.ico" parameters["config.github_username"] := "" parameters["config.github_token"] := "" parameters["config.github_repo"] := "VolantisDev/Launchpad" diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 2b8cff25..72d1a473 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -46,7 +46,7 @@ "name": "Launchpad API", "EndpointUrl": "https://api.launchpad.games/v1", "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", - "IconSrc": "Logo", + "IconSrc": "logo", "SupportsAuthentication": true, "Authenticator": "jwt", "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk index 9ff0d0b0..8d25858b 100644 --- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk +++ b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk @@ -25,7 +25,7 @@ class BackupEntity extends AppEntityBase { definitions["IconSrc"] := Map( "type", "icon_file", "description", "The path to this an icon (.ico or .exe).", - "default", this.app.Service("manager.theme")[].GetIconPath("Backup") + "default", this.app.Service("manager.theme")[].GetIconPath("backup") ) definitions["Source"] := Map( diff --git a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk b/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk index 61ead15e..a33e9428 100644 --- a/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk +++ b/Lib/Shared/Volantis.Theme/Theme/ThemeBase.ahk @@ -8,7 +8,7 @@ class ThemeBase { defaultTheme := "Lightpad" vars := Map() colors := Map("background", "FFFFFF", "text", "000000", "textInactive", "959595", "accent", "9466FC", "accentBright", "EEE6FF", "accentBg", "8A57F0", "transColor", "") - themeAssets := Map("logo", "Resources\Graphics\Logo.png", "icon", "Resources\Graphics\Launchpad.ico", "spinner", "Resources\Graphics\Spinner.gif") + themeAssets := Map("logo", "Resources\Graphics\logo.png", "icon", "Resources\Graphics\launchpad.ico", "spinner", "Resources\Graphics\spinner.gif") symbols := Map() buttons := Map("height", Map("s", 20, "m", 30, "l", 40, "xl", 80), "fixedWidth", Map("s", 80, "m", 100, "l", 120, "xl", 140)) labels := Map("height", "auto", "fixedWidth", 75, "font", "normal") diff --git a/Resources/Graphics/Icons/Dark/Backup.ico b/Resources/Graphics/Icons/Dark/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Backup.ico rename to Resources/Graphics/Icons/Dark/backup.ico diff --git a/Resources/Graphics/Icons/Dark/Config.ico b/Resources/Graphics/Icons/Dark/config.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Config.ico rename to Resources/Graphics/Icons/Dark/config.ico diff --git a/Resources/Graphics/Icons/Dark/Game.ico b/Resources/Graphics/Icons/Dark/game.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Game.ico rename to Resources/Graphics/Icons/Dark/game.ico diff --git a/Resources/Graphics/Icons/Dark/Logo.ico b/Resources/Graphics/Icons/Dark/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Logo.ico rename to Resources/Graphics/Icons/Dark/logo.ico diff --git a/Resources/Graphics/Icons/Dark/Module.png b/Resources/Graphics/Icons/Dark/module.png similarity index 100% rename from Resources/Graphics/Icons/Dark/Module.png rename to Resources/Graphics/Icons/Dark/module.png diff --git a/Resources/Graphics/Icons/Dark/Platform.ico b/Resources/Graphics/Icons/Dark/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Dark/Platform.ico rename to Resources/Graphics/Icons/Dark/platform.ico diff --git a/Resources/Graphics/Icons/Gradient/Backup.ico b/Resources/Graphics/Icons/Gradient/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Backup.ico rename to Resources/Graphics/Icons/Gradient/backup.ico diff --git a/Resources/Graphics/Icons/Gradient/Config.ico b/Resources/Graphics/Icons/Gradient/config.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Config.ico rename to Resources/Graphics/Icons/Gradient/config.ico diff --git a/Resources/Graphics/Icons/Gradient/Game.ico b/Resources/Graphics/Icons/Gradient/game.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Game.ico rename to Resources/Graphics/Icons/Gradient/game.ico diff --git a/Resources/Graphics/Icons/Gradient/Logo.ico b/Resources/Graphics/Icons/Gradient/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Logo.ico rename to Resources/Graphics/Icons/Gradient/logo.ico diff --git a/Resources/Graphics/Icons/Gradient/Platform.ico b/Resources/Graphics/Icons/Gradient/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Gradient/Platform.ico rename to Resources/Graphics/Icons/Gradient/platform.ico diff --git a/Resources/Graphics/Icons/Light/Backup.ico b/Resources/Graphics/Icons/Light/backup.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Backup.ico rename to Resources/Graphics/Icons/Light/backup.ico diff --git a/Resources/Graphics/Icons/Light/Config.ico b/Resources/Graphics/Icons/Light/config.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Config.ico rename to Resources/Graphics/Icons/Light/config.ico diff --git a/Resources/Graphics/Icons/Light/Game.ico b/Resources/Graphics/Icons/Light/game.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Game.ico rename to Resources/Graphics/Icons/Light/game.ico diff --git a/Resources/Graphics/Icons/Light/Logo.ico b/Resources/Graphics/Icons/Light/logo.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Logo.ico rename to Resources/Graphics/Icons/Light/logo.ico diff --git a/Resources/Graphics/Icons/Light/Module.png b/Resources/Graphics/Icons/Light/module.png similarity index 100% rename from Resources/Graphics/Icons/Light/Module.png rename to Resources/Graphics/Icons/Light/module.png diff --git a/Resources/Graphics/Icons/Light/Platform.ico b/Resources/Graphics/Icons/Light/platform.ico similarity index 100% rename from Resources/Graphics/Icons/Light/Platform.ico rename to Resources/Graphics/Icons/Light/platform.ico diff --git a/Resources/Graphics/Launchpad-256.png b/Resources/Graphics/launchpad-256.png similarity index 100% rename from Resources/Graphics/Launchpad-256.png rename to Resources/Graphics/launchpad-256.png diff --git a/Resources/Graphics/Launchpad-64.png b/Resources/Graphics/launchpad-64.png similarity index 100% rename from Resources/Graphics/Launchpad-64.png rename to Resources/Graphics/launchpad-64.png diff --git a/Resources/Graphics/Launchpad.ico b/Resources/Graphics/launchpad.ico similarity index 100% rename from Resources/Graphics/Launchpad.ico rename to Resources/Graphics/launchpad.ico diff --git a/Resources/Graphics/Logo.png b/Resources/Graphics/logo.png similarity index 100% rename from Resources/Graphics/Logo.png rename to Resources/Graphics/logo.png diff --git a/Resources/Graphics/Spinner-Steam.gif b/Resources/Graphics/spinner-steam.gif similarity index 100% rename from Resources/Graphics/Spinner-Steam.gif rename to Resources/Graphics/spinner-steam.gif diff --git a/Resources/Graphics/Spinner.gif b/Resources/Graphics/spinner.gif similarity index 100% rename from Resources/Graphics/Spinner.gif rename to Resources/Graphics/spinner.gif diff --git a/Resources/Themes/Lightpad.json b/Resources/Themes/Lightpad.json index 2049c352..bcabf3a0 100644 --- a/Resources/Themes/Lightpad.json +++ b/Resources/Themes/Lightpad.json @@ -108,9 +108,9 @@ "transColor": "" }, "themeAssets": { - "icon": "Graphics\\Launchpad.ico", - "logo": "Graphics\\Logo.png", - "spinner": "Graphics\\Spinner.gif" + "icon": "Graphics\\launchpad.ico", + "logo": "Graphics\\logo.png", + "spinner": "Graphics\\spinner.gif" }, "symbols": { "arrowDown": "ArrowDownSymbol", diff --git a/Resources/Themes/Steampad.json b/Resources/Themes/Steampad.json index 9b6eae9f..676bf731 100644 --- a/Resources/Themes/Steampad.json +++ b/Resources/Themes/Steampad.json @@ -107,7 +107,7 @@ }, "themeAssets": { "logo": "", - "spinner": "Graphics\\Spinner-Steam.gif" + "spinner": "Graphics\\spinner-Steam.gif" }, "buttons": { "styles": { diff --git a/Scripts/Build.ahk b/Scripts/Build.ahk index 9d166304..998e9099 100644 --- a/Scripts/Build.ahk +++ b/Scripts/Build.ahk @@ -11,6 +11,6 @@ LaunchpadBuilder(Map( "appName", "Launchpad", "developer", "Volantis Development", "version", appVersion, - "trayIcon", appDir . "\Resources\Graphics\Launchpad.ico", + "trayIcon", appDir . "\Resources\Graphics\launchpad.ico", "console", true, )) From 41faf6eaa32c4a091122208f4c484a93bd5e5cf8 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 10:46:16 -0500 Subject: [PATCH 029/138] Fix spinner filename --- Resources/Themes/Steampad.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Themes/Steampad.json b/Resources/Themes/Steampad.json index 676bf731..c4e36465 100644 --- a/Resources/Themes/Steampad.json +++ b/Resources/Themes/Steampad.json @@ -107,7 +107,7 @@ }, "themeAssets": { "logo": "", - "spinner": "Graphics\\spinner-Steam.gif" + "spinner": "Graphics\\spinner-steam.gif" }, "buttons": { "styles": { From ec9ba3d238bdeaae5046d85a2296bb39910bef96 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 11:09:43 -0500 Subject: [PATCH 030/138] Fix reg lookup key for Epic Games platform --- .../Modules/Epic/GamePlatform/EpicPlatform.ahk | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk index 06e768d5..9a9d87fe 100644 --- a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk +++ b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk @@ -3,12 +3,12 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { displayName := "Epic Store" launcherType := "Epic" gameType := "Epic" - installDirRegView := 32 - installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" - versionRegView := 32 - versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" - uninstallCmdRegView := 32 - uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{0E63B233-DC24-442C-BD38-0B91D90FEC5B}" + installDirRegView := 64 + installDirRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" + versionRegView := 64 + versionRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" + uninstallCmdRegView := 64 + uninstallCmdRegKey := "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products\B4B4F9022FD3528499604D6D8AE00CE9\InstallProperties" Install() { Run("https://www.epicgames.com/store/en-US/download") From 8946e3290b0101a2544feaff6ba75ea0254dd4fc Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 12:03:02 -0500 Subject: [PATCH 031/138] Filter keys in DetectedGame objects more thoroughly, allow detecting DisplayName for detected games --- Lib/Launchpad/DetectedGame/DetectedGame.ahk | 26 +++++++++++-------- .../Epic/GamePlatform/EpicPlatform.ahk | 18 +++++++++++-- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 02851d7c..600a58a7 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -17,9 +17,9 @@ class DetectedGame { prioritySuffixes := ["-Win64-Shipping", "-Win32-Shipping"] filterExes := [] - __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", launcherSpecificId := "", possibleExeNames := "") { + __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", launcherSpecificId := "", possibleExeNames := "", displayName := "") { this.key := key - this.displayName := key + this.displayName := displayName ? displayName : key this.platform := platform this.detectedKey := key this.launcherType := launcherType @@ -153,15 +153,19 @@ class DetectedGame { } } - key := StrReplace(key, ": ", " - ") - key := StrReplace(key, ":", "") - key := StrReplace(key, "\", "") - key := StrReplace(key, "/", "") - key := StrReplace(key, "*", "") - key := StrReplace(key, "?", "") - key := StrReplace(key, "`"", "") - key := StrReplace(key, "®", "") - key := StrReplace(key, "â„¢", "") + replacements := [ + [" : ", " - "], + [": ", " - "], + [":", "-"], + ["®", ""], + ["â„¢", ""] + ] + + for , vals in replacements { + key := StrReplace(key, vals[1], vals[2]) + } + + key := RegExReplace(key, "[\\/:*?`"<>|]'") return key } diff --git a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk index 9a9d87fe..b42c67bc 100644 --- a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk +++ b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk @@ -41,7 +41,21 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { } if (isGame) { - key := obj["Name"] + key := obj.Has("Name") ? obj["Name"] : "" + + if (!key && obj.Has("DisplayName")) { + key := obj["DisplayName"] + } + + if (!key && obj.Has("MandatoryAppFolderName")) { + key := obj["MandatoryAppFolderName"] + } + + if (!key) { + throw AppException("Could not determine detected game key.") + } + + displayName := obj.Has("DisplayName") ? obj["DisplayName"] : "" installDir := obj["InstallLocation"] launcherSpecificId := obj["AppName"] ;exeName := obj["LaunchExecutable"] @@ -49,7 +63,7 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { locator := GameExeLocator(installDir) possibleExes := locator.Locate("") mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes, displayName)) } } } From b5b650d4014a8fc09ad9e398ac3a8a368105b994 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 12:09:50 -0500 Subject: [PATCH 032/138] Fix saving existing entities during game detection --- Lib/Launchpad/DetectedGame/DetectedGame.ahk | 2 +- Lib/Launchpad/Entity/LauncherEntity.ahk | 4 ++-- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 600a58a7..5707278f 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -95,7 +95,7 @@ class DetectedGame { } if (modified) { - launcher.SaveModifiedData() + launcher.SaveEntity(true) } } diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index 71f3f1a1..1122a0ac 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -379,8 +379,8 @@ class LauncherEntity extends AppEntityBase { return ValidateResult } - SaveModifiedData() { - super.SaveModifiedData() + SaveEntity(recurse := true) { + super.SaveEntity(recurse) this.app.State.SetLauncherConfigInfo(this.Id) } diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index a92092c7..826e1d58 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -20,10 +20,12 @@ class EntityBase { EntityTypeId { get => this.entityTypeIdVal + set => this.entityTypeIdVal := value } EntityType { get => this.GetEntityType() + set => this.EntityTypeId := value } FieldData { From 2dcb43d518e716713f7fdc878c9ae7f6246646d5 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 12:43:55 -0500 Subject: [PATCH 033/138] Show username for each auth service if available --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 4 ++++ .../WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index d483cec1..623d7281 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -8,6 +8,10 @@ class WebServiceEntity extends AppEntityBase { get => this.IsAuthenticated() } + UserId { + get => this.PersistentAuthData["user_id"] + } + AuthData[key] { get => this.GetAuthData(key) set => this.SetAuthData(key, value) diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index 9509d307..7b56457f 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -1,5 +1,5 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { - listViewColumns := Array("SERVICE", "PROVIDER", "AUTHENTICATED") + listViewColumns := Array("SERVICE", "PROVIDER", "USER", "AUTHENTICATED") GetListViewData(lv) { data := Map() @@ -8,7 +8,8 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { data[key] := [ webService["name"], webService["Provider"]["name"], - webService.AuthData["authenticated"] ? "Yes" : "No" + webService.UserId ? webService.UserId : "None", + webService.Authenticated ? "Yes" : "No" ] } From 0caa0c9ae163e79651763b8f7650b404f4121a80 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 15:35:31 -0500 Subject: [PATCH 034/138] Standardize how window and control resizing cascades --- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 1 + Lib/Shared/Volantis.App/Gui/GuiBase.ahk | 19 +++++++++++++++---- .../Gui/ManageWindow/ManageEntitiesWindow.ahk | 1 + .../Gui/ManageWindow/ManageWindowBase.ahk | 5 +++-- .../GuiControl/ListViewControl.ahk | 2 +- .../GuiControl/TitlebarControl.ahk | 4 ++-- 6 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 6e84d702..b01f9160 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -3,6 +3,7 @@ launcherManager := "" platformManager := "" showDetailsPane := true + lvResizeOpts := "h" __New(container, themeObj, config) { this.launcherManager := container.Get("entity_manager.launcher") diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk index 04c1ab3b..0cb58f9e 100644 --- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk @@ -30,10 +30,12 @@ class GuiBase { isShown := false config := "" merger := "" + addedControls := [] GetDefaultConfig(container, config) { return Map( "id", Type(this), + "resizable", false, "titlebar", true, "waitForResult", false, "titleIsMenu", false, @@ -81,6 +83,10 @@ class GuiBase { extraOptions["Border"] := true } + if (this.config["resizable"]) { + extraOptions["Resize"] := true + } + if (this.owner != "") { extraOptions["Owner" . this.owner.Hwnd] := true } @@ -106,7 +112,6 @@ class GuiBase { this.margin := this.windowSettings["spacing"]["margin"] this.guiId := this.config["id"] - this.RegisterCallbacks() this.Create() } @@ -169,7 +174,9 @@ class GuiBase { } Add(ctlClass, options := "", params*) { - return %ctlClass%(this, options, params*) + ctlObj := %ctlClass%(this, options, params*) + this.addedControls.Push(ctlObj) + return ctlObj } OnCalcSize(wParam, lParam, msg, hwnd) { @@ -762,8 +769,12 @@ class GuiBase { } OnSize(guiObj, minMax, width, height) { - if (this.config["titlebar"]) { - this.titlebar.OnSize(minMax, width, height) + for index, ctlObj in this.addedControls { + ctlObj.OnSize(guiObj, minMax, width, height) } + + ; if (this.config["titlebar"]) { + ; this.titlebar.OnSize(minMax, width, height) + ; } } } diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 41d57fab..0a782c82 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -24,6 +24,7 @@ class ManageEntitiesWindow extends ManageWindowBase { defaults := super.GetDefaultConfig(container, config) defaults["entity_type"] := this.entityTypeId defaults["title"] := this.entityType.definition["name_plural"] + return defaults } diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk index 56c85348..72cb7f01 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageWindowBase.ahk @@ -6,11 +6,13 @@ lvWidth := 0 showDetailsPane := false detailsFields := [] + lvResizeOpts := "wh" GetDefaultConfig(container, config) { defaults := super.GetDefaultConfig(container, config) defaults["frameShadow"] := false defaults["saveWindowState"] := true + defaults["resizable"] := true return defaults } @@ -74,8 +76,7 @@ opts.Push("w" . this.lvWidth) } - this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow") - this.listView.resizeOpts := "h" + this.listView := this.Add("ListViewControl", opts, "", this.listViewColumns, "GetListViewData", "GetListViewImgList", "InitListView", "ShouldHighlightRow", this.lvResizeOpts) return this.listView } diff --git a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk b/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk index 2d5c06f8..650cad6e 100644 --- a/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/ListViewControl.ahk @@ -11,7 +11,7 @@ class ListViewControl extends GuiControlBase { imgListL := "" resizeOpts := "wh" - CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "") { + CreateControl(columns, dataCallback, imgListCallback := "", initCallback := "", highlightRowCallback := "", resizeOpts := "wh") { global LVM_GETHEADER super.CreateControl(false) columns.InsertAt(this.keyCol, "") diff --git a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk index 3e4ed8cb..44cadd03 100644 --- a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk @@ -182,7 +182,7 @@ class TitlebarControl extends GuiControlBase { } } - OnSize(minMax, width, height) { + OnSize(guiObj, minMax, width, height) { if (minMax == 1 and this.guiObj.config["showMaximize"]) { this.guiObj.guiObj["WindowUnmaxButton"].Visible := true this.guiObj.guiObj["WindowMaxButton"].Visible := false @@ -209,7 +209,7 @@ class TitlebarControl extends GuiControlBase { this.guiObj.AutoXYWH("x*", ["WindowMaxButton", "WindowUnmaxButton"]) } - if (this.guiObj.config["showMaximize"]) { + if (this.guiObj.config["showMinimize"]) { this.guiObj.AutoXYWH("x*", ["WindowMinButton"]) } } From 9680f3c21e2918533fd61f982676bde6e2da436e Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 15:35:50 -0500 Subject: [PATCH 035/138] Fix missing refresh path for Launchpad API provider --- Lib/Shared/Modules/WebServices/WebServices.module.json | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 72d1a473..aa2a1367 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -46,6 +46,7 @@ "name": "Launchpad API", "EndpointUrl": "https://api.launchpad.games/v1", "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", + "AuthenticationRefreshPath": "token", "IconSrc": "logo", "SupportsAuthentication": true, "Authenticator": "jwt", From 2c3496d3cf02485dd572917d3476c40df9812301 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 15:37:59 -0500 Subject: [PATCH 036/138] Simplify access to AuthData and resolve data-related login issues --- .../WebServices/Entity/WebServiceEntity.ahk | 79 ++++++++++--- .../JwtWebServiceAuthenticator.ahk | 109 +++++++++++------- .../WebServiceAuthenticatorBase.ahk | 6 +- 3 files changed, 131 insertions(+), 63 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 623d7281..6ccb3202 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -9,19 +9,14 @@ class WebServiceEntity extends AppEntityBase { } UserId { - get => this.PersistentAuthData["user_id"] + get => this.AuthData["user_id"] } AuthData[key] { - get => this.GetAuthData(key) + get => this.GetAuthData(key, true) set => this.SetAuthData(key, value) } - PersistentAuthData[key] { - get => this.GetPersistentAuthData(key) - set => this.SetPersistentAuthData(key, value) - } - __New(app, id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, eventMgr, storageObj, idSanitizer, parentEntity := "") { this.cacheObj := cacheObj this.stateObj := stateObj @@ -99,33 +94,60 @@ class WebServiceEntity extends AppEntityBase { return BasicWebServiceRequest(this.eventMgr, this, this.cacheObj, method, path, data, useAuthentication, cacheResponse) } - GetAuthData(key := "") { - return this._getStateData(this.stateObj, key) + GetAuthData(key := "", includePersistent := true) { + val := this._getStateData(this.stateObj, key) + + if (!val && includePersistent) { + val := this._getStateData(this.persistentStateObj, key) + } + + return val } - SetAuthData(keyOrMap, value) { - return this._setStateData(this.stateObj, keyOrMap, value) + SetAuthData(keyOrMap, value, persist := false) { + result := this._setStateData(this.stateObj, keyOrMap, value) + + if (persist) { + this._setStateData(this.persistentStateObj, keyOrMap, value) + } + + return this } - ResetAuthData(newData := "") { + ResetAuthData(newData := "", persist := false) { if (!newData) { newData := Map() } + if (!newData.Has("authenticated")) { + newData["authenticated"] := false + } + this._createStateParents(this.stateObj) this.stateObj.State["WebServices"][this.Id]["AuthData"] := newData this.stateObj.SaveState() - } - GetPersistentAuthData(key := "") { - return this._getStateData(this.persistentStateObj, key) + if (persist) { + this._createStateParents(this.persistentStateObj) + this.persistentStateObj.State["WebServices"][this.Id]["AuthData"] := Map( + "authenticated", newData["authenticated"] + ) + } + + return this } - SetPersistentAuthData(key, value) { - return this._setStateData(this.persistentStateObj, key, value) + DeleteAuthData(key, persist := false) { + this._deleteStateData(this.stateObj, key) + + if (persist) { + this._deleteStateData(this.persistentStateObj, key) + } + + return this } - _getStateData(stateObj, key) { + _getStateData(stateObj, key := "") { save := this._createStateParents(stateObj) if (save) { @@ -145,6 +167,27 @@ class WebServiceEntity extends AppEntityBase { this._createStateParents(stateObj) stateObj.State["WebServices"][this.Id]["AuthData"][key] := value stateObj.SaveState() + + return this + } + + _deleteStateData(stateObj, key) { + created := this._createStateParents(stateObj) + save := created + + if (!created) { + parent := this._getStateData(stateObj) + + if (HasBase(parent, Map.Prototype) && parent.Has(key)) { + parent.Delete(key) + save := true + } + } + + if (save) { + stateObj.SaveState() + } + return this } diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk index e5031f7d..f080cd92 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -20,9 +20,13 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { } Logout(webServiceEnt) { - webServiceEnt.PersistentAuthData["auth_token"] := "" - webServiceEnt.PersistentAuthData["refresh_token"] := "" - webServiceEnt.ResetAuthData(Map("authenticated", false)) + webServiceEnt + .ResetAuthData() + .DeleteAuthData("auth_token", true) + .DeleteAuthData("refresh_token", true) + .DeleteAuthData("expires", true) + .SetAuthData("authenticated", false, true) + return true } @@ -42,7 +46,7 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { } _hasRefreshToken(webServiceEnt) { - return !!(webServiceEnt.PersistentAuthData["refresh_token"]) + return !!(webServiceEnt.AuthData["refresh_token"]) } _reauthenticate(webServiceEnt) { @@ -56,68 +60,82 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { } _getRefreshToken(webServiceEnt) { - return webServiceEnt.PersistentAuthData["refresh_token"] + return webServiceEnt.AuthData["refresh_token"] } _setRefreshToken(webServiceEnt, refreshToken) { - webServiceEnt.PersistentAuthData["refresh_token"] := refreshToken + webServiceEnt.SetAuthData("refresh_token", refreshToken, true) } _extractAuthData(webServiceEnt, response) { loginData := response.GetJsonData() - authData := Map( - "authenticated", (loginData.Has("user_id") && !!(loginData["user_id"])) - ) - persistentData := Map() - authDataMap := Map() - persistentDataMap := Map( - "user_id", "user_id", - "refresh_token", "refresh_token", - "id_token", "auth_token", - "access_token", "access_token" + + if (!loginData.Has("authenticated")) { + loginData["authenticated"] := !!(loginData.Has("refresh_token") && loginData["refresh_token"]) + } + + keyMap := Map( + "id_token", "auth_token", + "expires_in", "expires" ) - skipKeys := [ - "expires_in" + + persistentKeys := [ + "user_id", + "refresh_token", + "auth_token", + "access_token", + "authenticated", + "expires" ] - if (loginData.Has("expires_in")) { - persistentData["expires"] := DateAdd(A_Now, loginData["expires_in"], "S") - } + expiresInKeys := [ + "expires" + ] + + skipKeys := [] for key, val in loginData { - if (persistentDataMap.Has(key)) { - persistentData[persistentDataMap[key]] := loginData[key] - } else if (authDataMap.Has(key)) { - authData[authDataMap[key]] := loginData[key] - } else if (!authData.Has(key) && !persistentData.Has(key)) { - skip := false - - for index, skipKey in skipKeys { - if (key == skipKey) { - skip := true - break - } + if (keyMap.Has(key)) { + key := keyMap[key] + } + + persist := false + + for , persistKey in persistentKeys { + if (key == persistKey) { + persist := true + break } + } - if (!skip) { - authData[key] := val + expires := false + + for , expiresKey in expiresInKeys { + if (key == expiresKey) { + val := DateAdd(A_Now, val, "S") + break } } - } - for key, val in authData { - webServiceEnt.AuthData[key] := val - } + skip := false + + for , skipKey in skipKeys { + if (key == skipKey) { + skip := true + break + } + } - for key, val in persistentData { - webServiceEnt.PersistentAuthData[key] := val + if (!skip) { + webServiceEnt.SetAuthData(key, val, persist) + } } } _refreshAuthentication(webServiceEnt) { apiKey := webServiceEnt["Provider"]["AppKey"] - refreshToken := webServiceEnt.PersistentAuthData["refresh_token"] - refreshUrl := webServiceEnt["Provider"].GetAuthenticationRefreshUrl(Map("token", apiKey)) + refreshToken := webServiceEnt.AuthData["refresh_token"] + refreshUrl := webServiceEnt["Provider"].GetAuthenticationRefreshUrl(Map("key", apiKey)) response := "" if (!apiKey) { @@ -142,7 +160,10 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { if (response && success) { this._extractAuthData(webServiceEnt, response) } else { - webServiceEnt.PersistentAuthData["refresh_token"] := "" + url := response.httpReqObj.url.ToString(true) + webServiceEnt.SetAuthData("refresh_token", "", true) + + ; @todo handle common http error codes } return success diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk index 41b7ab38..87fa435e 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk @@ -26,7 +26,11 @@ class WebServiceAuthenticatorBase { } IsAuthenticated(webServiceEnt) { - return (webServiceEnt.AuthData[this.authenticatedStateKey] && !this.AuthenticationIsExpired(webServiceEnt)) + auth := webServiceEnt.AuthData[this.authenticatedStateKey] + expired := this.AuthenticationIsExpired(webServiceEnt) + + return auth && !expired + } NeedsRefresh(webServiceEnt) { From 7c268aae82b0e4caf1f5f87e52dc54c64e044267 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 15:38:51 -0500 Subject: [PATCH 037/138] Add Auto Login option to web services and show it in the manager window. Additionally, blank out username in manage window for now. --- .../Gui/ManageWindow/ManageWebServicesWindow.ahk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index 7b56457f..d5c68d24 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -1,5 +1,5 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { - listViewColumns := Array("SERVICE", "PROVIDER", "USER", "AUTHENTICATED") + listViewColumns := Array("SERVICE", "PROVIDER", "USER", "AUTHENTICATED", "AUTO-LOGIN") GetListViewData(lv) { data := Map() @@ -8,8 +8,9 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { data[key] := [ webService["name"], webService["Provider"]["name"], - webService.UserId ? webService.UserId : "None", - webService.Authenticated ? "Yes" : "No" + "", + webService.Authenticated ? "Yes" : "No", + webService["AutoLogin"] ? "Yes" : "No" ] } From 176e2cadabbbca0f64761b583dc3cd2bfde851ad Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 15:39:12 -0500 Subject: [PATCH 038/138] Add missing file from last change --- .../WebServices/Entity/WebServiceEntity.ahk | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 6ccb3202..6d9920fb 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -43,6 +43,36 @@ class WebServiceEntity extends AppEntityBase { ) } + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + if (this.idVal == "api" && definitions.Has("name")) { + definitions["name"]["editable"] := false + } + + definitions["Provider"] := Map( + "type", "entity_reference", + "entityType", "web_service_provider", + "required", true, + "editable", false + ) + + autoLoginDefault := false + + if (this.Id == "api") { + autoLoginDefault := this.container.GetParameter("config.api_auto_login") + } + + definitions["AutoLogin"] := Map( + "type", "boolean", + "description", "Automatically authenticate with this service when Launchpad starts.", + "required", false, + "default", autoLoginDefault + ) + + return definitions + } + IsAuthenticated() { isAuthenticated := false @@ -65,23 +95,6 @@ class WebServiceEntity extends AppEntityBase { } } - BaseFieldDefinitions() { - definitions := super.BaseFieldDefinitions() - - if (this.idVal == "api" && definitions.Has("name")) { - definitions["name"]["editable"] := false - } - - definitions["Provider"] := Map( - "type", "entity_reference", - "entityType", "web_service_provider", - "required", true, - "editable", false - ) - - return definitions - } - Request(path, method := "", data := "", useAuthentication := -1, cacheResponse := true) { if (!method) { method := this["Provider"]["DefaultMethod"] From 971c3fce8e4c142b1f6e7684c02a38c81f0aa770 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:04:32 -0500 Subject: [PATCH 039/138] Remove previous auto-login functionality --- Launchpad.services.json | 1 - Lib/Launchpad/App/Launchpad.ahk | 4 ---- Lib/Launchpad/Gui/Form/SettingsWindow.ahk | 2 -- Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json | 3 +-- .../Modules/WebServices/Entity/WebServiceEntity.ahk | 8 +------- 5 files changed, 2 insertions(+), 16 deletions(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index 957f87ec..233455d7 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -2,7 +2,6 @@ "parameters": { "backups_config": {}, "config.api_authentication": false, - "config.api_auto_login": false, "config.api_endpoint": "", "config.assets_dir": "@@{data_dir}\\Launcher Assets", "config.auto_backup_config_files": true, diff --git a/Lib/Launchpad/App/Launchpad.ahk b/Lib/Launchpad/App/Launchpad.ahk index 73eb3320..767330d7 100644 --- a/Lib/Launchpad/App/Launchpad.ahk +++ b/Lib/Launchpad/App/Launchpad.ahk @@ -109,10 +109,6 @@ RunApp(config) { this.MigrateConfiguration() - - if (this.Config["api_auto_login"] && this.Services.Has("Auth")) { - this.Service("Auth").Login() - } super.RunApp(config) diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk index e6482347..5ffa8e5c 100644 --- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk +++ b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk @@ -127,8 +127,6 @@ this.AddHeading("API Settings") ctl := this.AddConfigCheckBox("Enable API login for enhanced functionality", "api_authentication") ctl.ctl.NeedsRestart := true - ctl := this.AddConfigCheckBox("Automatically initiate API login when needed", "api_auto_login") - ctl.ctl.NeedsRestart := true tabs.UseTab() diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index bd3b74bc..b7b53128 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -35,7 +35,6 @@ "parameters": { "config.data_source_key": "api", "config.api_endpoint": "https://api.launchpad.games/v1", - "config.api_authentication": true, - "config.api_auto_login": false + "config.api_authentication": true } } diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 6d9920fb..4bd46cb9 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -57,17 +57,11 @@ class WebServiceEntity extends AppEntityBase { "editable", false ) - autoLoginDefault := false - - if (this.Id == "api") { - autoLoginDefault := this.container.GetParameter("config.api_auto_login") - } - definitions["AutoLogin"] := Map( "type", "boolean", "description", "Automatically authenticate with this service when Launchpad starts.", "required", false, - "default", autoLoginDefault + "default", (this.idVal == "api") ) return definitions From 0d8de85c68e9510d5c41e7c9cf1f1d53c09ae9f8 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:04:45 -0500 Subject: [PATCH 040/138] Always pass container into event subscribers --- .../Volantis.Base/EventSubscriber/EventSubscriberBase.ahk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk b/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk index 871cef25..3043d56d 100644 --- a/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk +++ b/Lib/Shared/Volantis.Base/EventSubscriber/EventSubscriberBase.ahk @@ -2,6 +2,12 @@ Extending this class is optional as its main purpose is to document the API */ class EventSubscriberBase { + container := "" + + __New(container) { + this.container := container + } + /* Format: Map( @@ -12,6 +18,6 @@ class EventSubscriberBase { ) */ GetEventSubscribers() { - return [] + return Map() } } From a59b4ed4b132821bda09c52296e93bb161880770 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:05:06 -0500 Subject: [PATCH 041/138] Move APP_POST_STARTUP event to after the RunApp method has run --- Lib/Shared/Volantis.App/App/AppBase.ahk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/Lib/Shared/Volantis.App/App/AppBase.ahk index 14051eb9..082990c8 100644 --- a/Lib/Shared/Volantis.App/App/AppBase.ahk +++ b/Lib/Shared/Volantis.App/App/AppBase.ahk @@ -425,13 +425,13 @@ class AppBase { event := AppRunEvent(Events.APP_POST_INITIALIZE, this, config) this.Service("manager.event").DispatchEvent(event) - event := AppRunEvent(Events.APP_POST_STARTUP, this, config) - this.Service("manager.event").DispatchEvent(event) - event := AppRunEvent(Events.APP_PRE_RUN, this, config) this.Service("manager.event").DispatchEvent(event) this.RunApp(config) + + event := AppRunEvent(Events.APP_POST_STARTUP, this, config) + this.Service("manager.event").DispatchEvent(event) } LoadServices(config) { From 2c82a37524fccd31c18bb01c72270bbc54444318 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:05:24 -0500 Subject: [PATCH 042/138] Add Enabled field to WebService entities --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 4bd46cb9..506bdd28 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -64,6 +64,12 @@ class WebServiceEntity extends AppEntityBase { "default", (this.idVal == "api") ) + definitions["Enabled"] := Map( + "type", "boolean", + "required", false, + "default", true + ) + return definitions } From d8561c65eb66e1f0da43eb82feda475d0b78ba8a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:09:01 -0500 Subject: [PATCH 043/138] Query(Create a WebServicesEventSubscriber that handles auto-login services --- Lib/Shared/Includes.ahk | 1 + .../WebServicesEventSubscriber.ahk | 21 +++++++++++++++++++ .../WebServices/WebServices.module.json | 5 +++++ 3 files changed, 27 insertions(+) create mode 100644 Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 1dcb7297..a426f7cc 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -10,6 +10,7 @@ #Include Modules\WebServices\Event\WebServiceRequestEvent.ahk #Include Modules\WebServices\Event\WebServiceResponseEvent.ahk #Include Modules\WebServices\Events\WebServicesEvents.ahk +#Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk #Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk #Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk #Include Modules\WebServices\WebServiceAuthenticator\JwtWebServiceAuthenticator.ahk diff --git a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk new file mode 100644 index 00000000..66ea089b --- /dev/null +++ b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk @@ -0,0 +1,21 @@ +class WebServicesEventSubscriber extends EventSubscriberBase { + GetEventSubscribers() { + return Map( + Events.APP_POST_STARTUP, [ + ObjBindMethod(this, "OnPostStartup") + ] + ) + } + + OnPostStartup(event, extra, eventName, hwnd) { + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "AutoLogin") + .Execute() + + for key, webService in webServices { + webService.Login() + } + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index aa2a1367..74985cb7 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -78,6 +78,11 @@ "web_services_authenticator.jwt": { "class": "JwtWebServiceAuthenticator", "arguments": ["@manager.gui"] + }, + "event_subscriber.web_services": { + "class": "WebServicesEventSubscriber", + "arguments": ["@{}"], + "tags": ["event_subscriber"] } } } From 358ae4902b607804282050de007368d620b82fd8 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:21:10 -0500 Subject: [PATCH 044/138] Allow canceling authentication GUI without retrying --- .../LaunchpadLoginWindow.ahk | 6 +++++- .../JwtWebServiceAuthenticator.ahk | 20 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk index 53e76866..d6ef8ff1 100644 --- a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk @@ -24,6 +24,10 @@ } ProcessResult(result, submittedData := "") { - return (result == "Login") ? this.guiObj["AuthToken"].Text : "" + if (result == "Login") { + result := this.guiObj["AuthToken"].Text + } + + return result } } diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk index f080cd92..9f61b760 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -4,16 +4,22 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { throw OperationFailedException("Login failed after " . retryCount . " tries.") } + authResult := "" + if (!this._hasRefreshToken(webServiceEnt)) { - this._reauthenticate(webServiceEnt) + authResult := this._reauthenticate(webServiceEnt) } - success := this._hasRefreshToken(webServiceEnt) - ? this._refreshAuthentication(webServiceEnt) - : false + success := false + + if (authResult != "Cancel") { + if (this._hasRefreshToken(webServiceEnt)) { + success := this._refreshAuthentication(webServiceEnt) + } - if (!success) { - success := this.Login(webServiceEnt, retryCount + 1) + if (!success) { + success := this.Login(webServiceEnt, retryCount + 1) + } } return success @@ -52,7 +58,7 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { _reauthenticate(webServiceEnt) { refreshToken := this._authenticationGui(webServiceEnt) - if (refreshToken) { + if (refreshToken != "Cancel") { this._setRefreshToken(webServiceEnt, refreshToken) } From dba5a62ce55e531a241682d6c0ea966ca31c39b1 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:30:32 -0500 Subject: [PATCH 045/138] Show a normal dialog message after exceeding the max login retries instead of throwing an exception --- .../JwtWebServiceAuthenticator.ahk | 3 ++- .../WebServiceAuthenticatorBase.ahk | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk index 9f61b760..ddb016fd 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -1,7 +1,8 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { Login(webServiceEnt, retryCount := 0) { if (retryCount > this.maxRetries) { - throw OperationFailedException("Login failed after " . retryCount . " tries.") + this._handleLoginFailure("You have used " . retryCount . " of " . this.maxRetries + 1 . " login attempts. Canceling login.") + return false } authResult := "" diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk index 87fa435e..e1b8717c 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk @@ -72,4 +72,12 @@ class WebServiceAuthenticatorBase { return result } + + _handleLoginFailure(message) { + this.guiMgr.Dialog(Map( + "title", "Login Failure", + "text", message, + "buttons", "*&OK" + )) + } } From 22386c04f86b8864f532705d78acb37ce0c3f3d7 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 16:40:49 -0500 Subject: [PATCH 046/138] Allow overriding which default context menu buttons show in entity editors --- .../ManageWindow/ManageWebServicesWindow.ahk | 26 ++++++++++++++++--- .../Gui/ManageWindow/ManageEntitiesWindow.ahk | 10 ++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index d5c68d24..83a36400 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -35,6 +35,16 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { return menuItems } + _shouldShowButton(entityObj, buttonName) { + shouldShow := super._shouldShowButton(entityObj, buttonName) + + if (shouldShow && buttonName == "DeleteEntity") { + shouldShow := entityObj.Id != "api" + } + + return shouldShow + } + ProcessContextMenuResult(result, key) { if (result == "WebServiceLogout") { this.Logout(key) @@ -46,11 +56,19 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { } Logout(key) { - return this.entityMgr[key].Logout() + result := this.entityMgr[key].Logout() + + this.UpdateListView() + + return result } Login(key) { - return this.entityMgr[key].Login() + result := this.entityMgr[key].Login() + + this.UpdateListView() + + return result } ViewEntity(key) { @@ -58,7 +76,9 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { } AddEntity() { - + ; @todo open add wizard + + this.UpdateListView() } DeleteEntity(key) { diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 0a782c82..3c3950d0 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -164,21 +164,25 @@ class ManageEntitiesWindow extends ManageWindowBase { definition := this.entityType.definition menuItems := [] - if (definition["allow_view"]) { + if (definition["allow_view"] && this._shouldShowButton(entityObj, "ViewEntity")) { menuItems.Push(Map("label", "&View", "name", "ViewEntity")) } - if (definition["allow_edit"]) { + if (definition["allow_edit"] && this._shouldShowButton(entityObj, "EditEntity")) { menuItems.Push(Map("label", "Edit", "name", "EditEntity")) } - if (definition["allow_delete"]) { + if (definition["allow_delete"] && this._shouldShowButton(entityObj, "DeleteEntity")) { menuItems.Push(Map("label", "Delete", "name", "DeleteEntity")) } return menuItems } + _shouldShowButton(entityObj, buttonName) { + return true + } + ShowListViewContextMenu(lv, item, isRightClick, X, Y) { key := this.listView.GetRowKey(item) entityObj := this.entityMgr[key] From 650f2cb0c39b7b51e20bba2a93756595f5a697d3 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:14:54 -0500 Subject: [PATCH 047/138] Update MainWindow to use new API web service --- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index b01f9160..066e43a4 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -177,11 +177,26 @@ } } + _getApiWebService() { + webService := "" + + if (this.app.Services.Has("entity_manager.web_service")) { + entityMgr := this.app.Services["entity_manager.web_service"] + + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { + webService := entityMgr["api"] + } + } + + return webService + } + GetStatusInfo() { info := "" + webService := this._getApiWebService() - if (this.container.Has("Auth")) { - info := this.container["Auth"].GetStatusInfo() + if (webService) { + info := webService.GetStatusInfo() } return info @@ -189,9 +204,10 @@ OnStatusIndicatorClick(btn, info) { menuItems := [] + webService := this._getApiWebService() - if (this.container.Has("Auth")) { - if (this.container["Auth"].IsAuthenticated()) { + if (webService) { + if (webService.Authenticated) { menuItems.Push(Map("label", "Account Details", "name", "AccountDetails")) menuItems.Push(Map("label", "Logout", "name", "Logout")) } else { @@ -212,22 +228,24 @@ this.UpdateStatusIndicator() } } else if (result == "Logout") { - if (this.container.Has("Auth")) { - this.container["Auth"].Logout() + if (webService) { + webService.Logout() } } else if (result == "Login") { - if (this.container.Has("Auth")) { - this.container["Auth"].Login() + if (webService) { + webService.Login() } } } StatusWindowIsOnline() { isOnline := false + webService := this._getApiWebService() - if (this.container.Has("Auth")) { - isOnline := this.container["Auth"].IsAuthenticated() + if (webService) { + isOnline := webService.Authenticated } + return isOnline } From 0ec7b0cc152d50b12feaf52a8fe9de13e33035ee Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:15:10 -0500 Subject: [PATCH 048/138] Update LaunchpadBuilder to use new API web service --- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index d787c321..0f1b87c7 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -60,8 +60,12 @@ class LaunchpadBuilder extends AppBase { this.ExitApp() } - if (buildInfo.DeployToApi && this.Services.Has("Auth")) { - this.Service("Auth").Login() + if (buildInfo.DeployToApi && this.Services.Has("entity_manager.web_service")) { + entityMgr := this.Services["entity_manager.web_service"] + + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { + entityMgr["api"].Login() + } } version := buildInfo.Version From c8e272a7027066bee775ccdf17634221f4917763 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:15:58 -0500 Subject: [PATCH 049/138] Update LaunchpadApi module to depend on WebServices module, and remove old auth provider --- .../AuthProvider/LaunchpadApiAuthProvider.ahk | 30 ------------------- .../LaunchpadApi/LaunchpadApi.module.json | 6 +--- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk diff --git a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk b/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk deleted file mode 100644 index ff44127d..00000000 --- a/Lib/Shared/Modules/LaunchpadApi/AuthProvider/LaunchpadApiAuthProvider.ahk +++ /dev/null @@ -1,30 +0,0 @@ -class LaunchpadApiAuthProvider extends JwtAuthProvider { - app := "" - - __New(app, stateObj) { - this.app := app - - persistentData := "" - - if (stateObj) { - state := stateObj.GetState() - - if (state.Has("Authentication")) { - persistentData := state["Authentication"] - } - } - - authEndpointUrl := "https://securetoken.googleapis.com/v1" - webApiKey := "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ" - super.__New(app.Service("manager.data_source").GetDefaultDataSource(), authEndpointUrl, webApiKey, persistentData) - } - - ShowLoginWindow() { - return this.app.Service("manager.gui").Dialog(Map("type", "LoginWindow")) - } - - ExtractAuthInfoFromResponse(httpReqObj) { - authInfoObj := super.ExtractAuthInfoFromResponse(httpReqObj) - return authInfoObj - } -} diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index b7b53128..bc9da04e 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -12,17 +12,13 @@ "website": "https://launchpad.games", "version": "{{VERSION}}", "appVersion": "", - "dependencies": ["Auth"] + "dependencies": ["WebServices"] }, "services": { "data_source.api": { "class": "ApiDataSource", "arguments": ["@{App}", "@manager.cache", "api", "@@config.api_endpoint"] }, - "auth_provider.launchpad_api": { - "class": "LaunchpadApiAuthProvider", - "arguments": ["@{App}", "@state.app"] - }, "cache_state.api": { "class": "CacheState", "arguments": ["@{App}", "@@config.cache_dir", "API.json"] From c39399d0fda4de7bf7663eeb96c32d62fb8c4080 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:16:22 -0500 Subject: [PATCH 050/138] Remove unused AuthInfo tests --- Lib/Shared/Includes.test.ahk | 1 - .../Modules/Auth/AuthInfo/AuthInfo.test.ahk | 103 ------------------ 2 files changed, 104 deletions(-) delete mode 100644 Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk diff --git a/Lib/Shared/Includes.test.ahk b/Lib/Shared/Includes.test.ahk index 2fe8682e..f157d4f8 100644 --- a/Lib/Shared/Includes.test.ahk +++ b/Lib/Shared/Includes.test.ahk @@ -1,5 +1,4 @@ ; Automatically-generated file. Manual edits will be overwritten. -#Include Modules\Auth\AuthInfo\AuthInfo.test.ahk #Include Volantis.App\App\AppBase.test.ahk #Include Volantis.Base\Event\EventBase.test.ahk #Include Volantis.Utility\Debugger\Debugger.test.ahk diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk deleted file mode 100644 index dfbcde79..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.test.ahk +++ /dev/null @@ -1,103 +0,0 @@ -class AuthInfoTest extends AppTestBase { - TestAuthenticated() { - authInfoObj := AuthInfo() - - authInfoObj.isAuthenticated := false - - this.AssertFalse( - authInfoObj.Authenticated, - "Test setting isAuthenticated to false" - ) - - authInfoObj.Authenticated := true - - this.AssertTrue( - authInfoObj.Authenticated, - "Test changing Authenticated to true" - ) - } - - TestUserId() { - authInfoObj := AuthInfo() - - this.AssertEquals( - "", - authInfoObj.UserId, - "Assert that user ID is blank initially" - ) - - userIds := [ - 123, - "456", - "1234-5678-9012-3456", - "r@nd0m" - ] - - for userId in userIds { - authInfoObj.Set(authInfoObj.userIdField, userId) - - this.AssertEquals( - userId, - authInfoObj.UserId, - "Test changing user ID to " . userId - ) - } - } - - TestGet() { - authInfoObj := AuthInfo() - - this.AssertEmpty( - authInfoObj.Get("nonexistantValue"), - "Getting a non-existent value returns an empty string" - ) - - authInfoObj.Set("testValue", "persistent", true) - - this.AssertEquals( - "persistent", - authInfoObj.Get("testValue"), - "Getting a persistent value is possible" - ) - - authInfoObj.Set("testValue", "overridden", false) - - this.AssertEquals( - "overridden", - authInfoObj.Get("testValue"), - "Getting a secure value overrides a persistent one" - ) - } - - TestSet() { - authInfoObj := AuthInfo() - - authInfoObj.Set("testValue", "persistent", true) - - this.AssertEquals( - "persistent", - authInfoObj.Get("testValue"), - "Setting a persistent value is possible" - ) - - authInfoObj.Set("testValue", "overridden", false) - - this.AssertEquals( - "overridden", - authInfoObj.Get("testValue"), - "Setting a secure value overrides a persistent one" - ) - } - - TestGetPersistentData() { - persistentData := Map("testValue", "persistent") - authInfoObj := AuthInfo() - authInfoObj.persistentData := persistentData - - this.AssertEquals( - persistentData, - authInfoObj.GetPersistentData(), - "GetPersistentData returns all persistent data" - ) - } -} From 156743578c076a81692e7efa1cf5bc400cf6efd0 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:17:13 -0500 Subject: [PATCH 051/138] Standardize AlterRequest parameters --- .../WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk | 2 +- .../WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk | 2 +- .../WebServices/WebServiceRequest/WebServiceRequestBase.ahk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk index ddb016fd..ce99bdce 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/JwtWebServiceAuthenticator.ahk @@ -44,7 +44,7 @@ class JwtWebServiceAuthenticator extends WebServiceAuthenticatorBase { } } - AlterRequest(webServiceEnt, request, httpReqObj) { + AlterRequest(webServiceEnt, httpReqObj) { bearerToken := webServiceEnt.AuthData["auth_token"] if (bearerToken) { diff --git a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk index e1b8717c..91664f03 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAuthenticator/WebServiceAuthenticatorBase.ahk @@ -21,7 +21,7 @@ class WebServiceAuthenticatorBase { } - AlterRequest(webServiceEnt, request, httpReqObj) { + AlterRequest(webServiceEnt, httpReqObj) { } diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk index 063dc665..754981b0 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -129,7 +129,7 @@ class WebServiceRequestBase { authenticator.RefreshAuthentication(this.webServiceEnt) } - authenticator.AlterRequest(this.webServiceEnt, this, httpReqObj) + authenticator.AlterRequest(this.webServiceEnt, httpReqObj) } event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_HTTP_REQ_ALTER, this) From 5b2b4dcba54385f2ba8a8cc5a47d071865dd42a3 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:17:31 -0500 Subject: [PATCH 052/138] Add GetStatusInfo to WebServiceEntity (but it isn't finished yet) --- .../WebServices/Entity/WebServiceEntity.ahk | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 506bdd28..11c29b32 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -224,4 +224,33 @@ class WebServiceEntity extends AppEntityBase { return modified } + + GetStatusInfo() { + ; @todo fix this data + statusText := "Not logged in" + imgPath := "" + email := "" + + if (this.Authenticated) { + playerName := this.app.Config["player_name"] + email := this.AuthData["email"] + + if (playerName) { + statusText := playerName + } else if (email) { + statusText := email + } else { + statusText := "Logged in" + } + + imgPath := this.AuthData["photo"] + + if (SubStr(imgPath, 1, 4) == "http") { + cachePath := "account--profile.jpg" + imgPath := this.app.Service("manager.cache")["file"].GetCachedDownload(cachePath, imgPath) + } + } + + return Map("name", statusText, "email", email, "photo", imgPath) + } } From c9b41a6def7c0a48878e82a764314b108a71c7a2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:17:57 -0500 Subject: [PATCH 053/138] Update ApiDataSource to get data through the API web service --- .../LaunchpadApi/DataSource/ApiDataSource.ahk | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk index bf0cf3aa..d8c4363b 100644 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk @@ -35,8 +35,14 @@ class ApiDataSource extends DataSourceBase { if (private) { request.requestHeaders["Cache-Control"] := "no-cache" - if (this.app.Config["api_authentication"] && this.app.Services.Has("Api")) { - this.app.Service("Auth").AlterApiRequest(request) + if (this.app.Config["api_authentication"]) { + entityMgr := webService := this.app.Service("entity_manager.web_service") + + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { + webService := this.app.Service("entity_manager.web_service")["api"] + webService["Provider"]["Authenticator"].AlterRequest(webService, request) + } + } } @@ -85,12 +91,15 @@ class ApiDataSource extends DataSourceBase { status := Map("authenticated", false, "email", "", "photo", "") - if (this.app.Config["api_authentication"] && this.app.Service("Auth").IsAuthenticated()) { - statusResult := this.ReadItem(path, true) + if (this.app.Config["api_authentication"]) { + entityMgr := webService := this.app.Service("entity_manager.web_service") - if (statusResult) { - json := JsonData() - status := json.FromString(&statusResult) + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"] && entityMgr["api"]["Authenticated"]) { + statusResult := this.ReadItem(path, true) + + if (statusResult) { + status := JsonData().FromString(&statusResult) + } } } From 343e1335167088835808c405f2e3519d790409e1 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:18:19 -0500 Subject: [PATCH 054/138] Update account info window to use API web service --- .../Gui/Dialog/AccountInfoWindow.ahk | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk index 79b752ba..cdf5ba42 100644 --- a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk +++ b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk @@ -9,13 +9,23 @@ Controls() { super.Controls() - if (this.app.Services.Has("Auth")) { - info := this.app.Service("Auth").GetStatusInfo() + if (this.app.Services.Has("entity_manager.web_service")) { + entityMgr := this.app.Services["entity_manager.web_service"] - if (info) { - opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin - this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) - this.guiObj.AddText(opts, "Email: " . info["email"]) + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { + info := Map( + "name", "", + "email", "", + "photo", "" + ) + + ; @todo Pull this information from the API web service + + if (info) { + opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin + this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) + this.guiObj.AddText(opts, "Email: " . info["email"]) + } } } @@ -29,9 +39,15 @@ } ProcessResult(result, submittedData := "") { + + if (result == "Logout") { - if (this.app.Services.Has("Auth")) { - this.app.Service("Auth").Logout() + if (this.app.Services.Has("entity_manager.web_service")) { + entityMgr := this.app.Services["entity_manager.web_service"] + + if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { + entityMgr["api"].Logout() + } } } else if (result == "Save" && submittedData) { this.app.Config["player_name"] := submittedData.PlayerName From 23ec7a90793d36cd98be3be04a56119af1b86c6d Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 17:18:34 -0500 Subject: [PATCH 055/138] Remove unused Auth module --- Lib/Shared/Includes.ahk | 6 - Lib/Shared/Modules/Auth/Auth.module.json | 27 --- Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk | 40 ----- .../Modules/Auth/AuthInfo/JwtAuthInfo.ahk | 42 ----- .../Auth/AuthProvider/AuthProviderBase.ahk | 37 ----- .../Auth/AuthProvider/JwtAuthProvider.ahk | 150 ----------------- .../Volantis.App/Service/AuthService.ahk | 154 ------------------ 7 files changed, 456 deletions(-) delete mode 100644 Lib/Shared/Modules/Auth/Auth.module.json delete mode 100644 Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk delete mode 100644 Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk delete mode 100644 Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk delete mode 100644 Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk delete mode 100644 Lib/Shared/Volantis.App/Service/AuthService.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index a426f7cc..129aa118 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -1,9 +1,4 @@ ; Automatically-generated file. Manual edits will be overwritten. -#Include Modules\Auth\AuthInfo\AuthInfo.ahk -#Include Modules\Auth\AuthInfo\JwtAuthInfo.ahk -#Include Modules\Auth\AuthProvider\AuthProviderBase.ahk -#Include Modules\Auth\AuthProvider\JwtAuthProvider.ahk -#Include Modules\LaunchpadApi\AuthProvider\LaunchpadApiAuthProvider.ahk #Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk #Include Modules\WebServices\Entity\WebServiceEntity.ahk #Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk @@ -98,7 +93,6 @@ #Include Volantis.App\Installer\InstallerComponent\GitHubReleaseInstallerComponent.ahk #Include Volantis.App\Installer\InstallerComponent\InstallerComponentBase.ahk #Include Volantis.App\Service\AppServiceBase.ahk -#Include Volantis.App\Service\AuthService.ahk #Include Volantis.App\Service\EventManager.ahk #Include Volantis.App\Service\LoggerService.ahk #Include Volantis.App\Service\NotificationService.ahk diff --git a/Lib/Shared/Modules/Auth/Auth.module.json b/Lib/Shared/Modules/Auth/Auth.module.json deleted file mode 100644 index 27e65909..00000000 --- a/Lib/Shared/Modules/Auth/Auth.module.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "module": { - "name": "Authentication", - "type": "AppModule", - "icon": "", - "category": "Web Services", - "tags": ["Launchpad", "LaunchpadBuilder"], - "description": "Authenticate with remote accounts, such as the Launchpad API", - "author": { - "name": "Ben McClure, Volantis Dev", - "url": "https://volantisdev.com" - }, - "website": "https://launchpad.games", - "version": "{{VERSION}}", - "appVersion": "", - "dependencies": [] - }, - "parameters": { - "config.auth_service": "" - }, - "services": { - "Auth": { - "class": "AuthService", - "arguments": ["@{App}", "@@config.auth_service", "@state.app"] - } - } -} diff --git a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk deleted file mode 100644 index 3428b9e0..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/AuthInfo.ahk +++ /dev/null @@ -1,40 +0,0 @@ -class AuthInfo { - isAuthenticated := false - secureData := Map() - persistentData := Map() - userIdField := "userId" - - Authenticated { - get => this.isAuthenticated - set => this.isAuthenticated := !!(value) - } - - UserId { - get => this.Get(this.userIdField) - } - - __New() { - - } - - Get(key) { - value := "" - - if (this.secureData.Has(key)) { - value := this.secureData[key] - } else if (this.persistentData.Has(key)) { - value := this.persistentData[key] - } - - return value - } - - Set(key, value, persist := false) { - mapObj := persist ? this.persistentData : this.secureData - mapObj[key] := value - } - - GetPersistentData() { - return this.persistentData - } -} diff --git a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk b/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk deleted file mode 100644 index 57b560ca..00000000 --- a/Lib/Shared/Modules/Auth/AuthInfo/JwtAuthInfo.ahk +++ /dev/null @@ -1,42 +0,0 @@ -class JwtAuthInfo extends AuthInfo { - __New(userInfo) { - super.__New() - - this.Authenticated := false - - added := Map() - - if (userInfo.Has("user_id")) { - this.Authenticated := !!(userInfo["user_id"]) - this.Set("userId", userInfo["user_id"], true) - added["user_id"] := true - } - - if (userInfo.Has("refresh_token")) { - this.Set("refresh", userInfo["refresh_token"], true) - added["refresh_token"] := true - } - - if (userInfo.Has("expires_in")) { - timestamp := DateAdd(A_Now, userInfo["expires_in"], "S") - this.Set("expires", timestamp, true) - added["expires_in"] := true - } - - if (userInfo.Has("id_token")) { - this.Set("authToken", userInfo["id_token"]) - added["id_token"] := true - } - - if (userInfo.Has("access_token")) { - this.Set("accessToken", userInfo["access_token"]) - added["authToken"] := true - } - - for key, value in userInfo { - if (!added.Has(key) || !added[key]) { - this.Set(key, value) - } - } - } -} diff --git a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk b/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk deleted file mode 100644 index 475bc81f..00000000 --- a/Lib/Shared/Modules/Auth/AuthProvider/AuthProviderBase.ahk +++ /dev/null @@ -1,37 +0,0 @@ -class AuthProviderBase { - __New(persistentData := "") { - - } - - Login() { - - } - - Logout(authInfoObj) { - - } - - NeedsRefresh(authInfoObj) { - - } - - RefreshAuthentication(authInfoObj) { - - } - - AddRefreshInfoToRequest(authInfoObj, httpReqObj) { - ; Add refresh token to the request - } - - AddLoginInfoToRequest(authToken, httpReqObj) { - ; Add auth info needed to complete login to the request - } - - AddAuthInfoToRequest(authInfoObj, httpReqObj) { - ; Add auth info from authInfoObj to httpReqObj as needed - } - - ExtractAuthInfoFromResponse(httpReqObj) { - - } -} diff --git a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk b/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk deleted file mode 100644 index 5fde6ad2..00000000 --- a/Lib/Shared/Modules/Auth/AuthProvider/JwtAuthProvider.ahk +++ /dev/null @@ -1,150 +0,0 @@ -class JwtAuthProvider extends AuthProviderBase { - dataSourceObj := "" - authEndpointUrl := "" - webApiKey := "" - refreshPath := "token" - authToken := "" - refreshToken := "" - - __New(dataSourceObj, authEndpointUrl, webApiKey, persistentData := "") { - this.dataSourceObj := dataSourceObj - this.authEndpointUrl := authEndpointUrl - this.webApiKey := webApiKey - - if (persistentData != "" and persistentData.Has("authToken")) { - this.authToken := persistentData["authToken"] - } - } - - Login(isRetry := false) { - refreshToken := this.refreshToken - - if (!refreshToken) { - refreshToken := this.ShowLoginWindow() - - if (refreshToken) { - this.refreshToken := refreshToken - } - } - - userInfo := "" - - if (refreshToken) { - url := this.GetAuthUrl(this.refreshPath) - request := WinHttpReq(url) - payload := Map("grant_type", "refresh_token", "refresh_token", refreshToken) - response := request.Send("POST", payload) - - if (request.GetStatusCode() == 200) { - userInfo := this.ExtractAuthInfoFromResponse(request) - } else { - this.refreshToken := "" - - if (isRetry) { - ; TODO: Log user out instead of throwing an exception - throw OperationFailedException("Login failed.") - } else { - return this.Login(true) - } - } - } - - return userInfo - } - - GetAuthUrl(path) { - return this.authEndpointUrl . "/" . path . "?key=" . this.webApiKey - } - - ShowLoginWindow() { - return "" - } - - Logout(authInfoObj) { - this.authToken := "" - this.refreshToken := "" - return true - } - - RefreshAuthentication(authInfoObj) { - refreshToken := authInfoObj.Get("refresh") - - if (refreshToken) { - this.refreshToken := refreshToken - } - - return this.Login() - } - - IsAuthenticationValid(authInfoObj) { - isValid := false - - if (authInfoObj && authInfoObj.Authenticated) { - isValid := !this.IsAuthenticationExpired(authInfoObj) - } - - return isValid - } - - AddAuthInfoToRequest(authInfoObj, httpReqObj) { - authToken := authInfoObj.Get("authToken") - - if (authToken) { - this.AddAuthorizationHeader(authToken, httpReqObj) - } - } - - AddAuthorizationHeader(bearerToken, httpReqObj) { - if (bearerToken) { - httpReqObj.requestHeaders["Authorization"] := "Bearer " . bearerToken - } - } - - ExtractAuthInfoFromResponse(httpReqObj) { - responseData := Trim(httpReqObj.GetResponseData()) - - userInfo := Map() - - if (responseData) { - data := JsonData() - userInfo := data.FromString(&responseData) - } - - return JwtAuthInfo(userInfo) - } - - IsAuthenticationExpired(authInfoObj) { - expired := true - - if (authInfoObj and authInfoObj.Authenticated) { - expires := authInfoObj.Get("expires") - - if (expires) { - diff := DateDiff(A_Now, expires, "S") - expired := (diff >= 0) - } - } - - return expired - } - - NeedsRefresh(authInfoObj) { - needsRefresh := false - thresholdSeconds := -600 - - if (!this.IsAuthenticationValid(authInfoObj)) { - needsRefresh := true - } else { - expires := authInfoObj.Get("expires") - - if (expires) { - diff := DateDiff(A_Now, expires, "S") - needsRefresh := (diff >= thresholdSeconds) - } else { - needsRefresh := true - } - } - - return needsRefresh - } -} diff --git a/Lib/Shared/Volantis.App/Service/AuthService.ahk b/Lib/Shared/Volantis.App/Service/AuthService.ahk deleted file mode 100644 index 9d9b4cd2..00000000 --- a/Lib/Shared/Volantis.App/Service/AuthService.ahk +++ /dev/null @@ -1,154 +0,0 @@ -class AuthService extends AppServiceBase { - authProviderObj := "" - stateObj := "" - authenticationEnabled := false - authInfoObj := "" - - __New(app, authProviderObj, stateObj) { - InvalidParameterException.CheckTypes("AuthenticationService", "stateObj", stateObj, "StateBase") - - if (authProviderObj && Type(authProviderObj) == "String") { - authProviderObj := app.Services.Get(authProviderObj) - } - - this.authProviderObj := authProviderObj - this.stateObj := stateObj - - authState := this.stateObj.Authentication - - if (authState && authState.Count > 0) { - authInfoObj := AuthInfo() - authInfoObj.Authenticated := true - - for key, value in authState { - authInfoObj.Set(key, value, true) - } - - this.authInfoObj := authInfoObj - } - - super.__New(app) - } - - SetAuthProvider(authProviderObj) { - this.authProviderObj := authProviderObj - } - - SetState(stateObj) { - this.stateObj := stateObj - } - - Login() { - if (this.app.Config["api_authentication"] && this.authProviderObj) { - authInfoObj := "" - - if (!this.IsAuthenticated()) { - authInfoObj := this.authProviderObj.Login() - } else if (this.AuthenticationNeedsRefresh()) { - authInfoObj := this.RefreshAuthentication() - } - - if (authInfoObj) { - this.UpdateAuthState(authInfoObj) - this.app.UpdateStatusIndicators() - } - } - } - - GetStatusInfo() { - statusText := "Not logged in" - imgPath := "" - email := "" - - if (this.IsAuthenticated()) { - playerName := this.app.Config["player_name"] - email := this.authInfoObj.Get("email") - - if (playerName) { - statusText := playerName - } else if (email) { - statusText := email - } else { - statusText := "Logged in" - } - - imgPath := this.authInfoObj.Get("photo") - - if (SubStr(imgPath, 1, 4) == "http") { - cachePath := "account--profile.jpg" - imgPath := this.app.Service("manager.cache")["file"].GetCachedDownload(cachePath, imgPath) - } - } - - return Map("name", statusText, "email", email, "photo", imgPath) - } - - Logout() { - if (this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj) { - this.authProviderObj.Logout(this.authInfoObj) - this.authInfoObj := "" - this.stateObj.Authentication := Map() - this.app.UpdateStatusIndicators() - } - } - - IsAuthenticated() { - return this.app.Config["api_authentication"] && this.authProviderObj && this.authInfoObj && this.authInfoObj.Authenticated - } - - AuthenticationNeedsRefresh() { - needsRefresh := false - - if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) { - needsRefresh := this.authProviderObj.NeedsRefresh(this.authInfoObj) - } - - return needsRefresh - } - - RefreshAuthentication() { - if (this.app.Config["api_authentication"] && this.authProviderObj && this.IsAuthenticated()) { - authInfoObj := this.authProviderObj.RefreshAuthentication(this.authInfoObj) - - if (authInfoObj) { - this.UpdateAuthState(authInfoObj) - } - } - } - - UpdateAuthState(authInfoObj) { - if (this.app.Config["api_authentication"] && this.authProviderObj && authInfoObj) { - this.authInfoObj := authInfoObj - this.AddUserInfoFromApi(authInfoObj) - this.stateObj.SetAuthentication(authInfoObj.GetPersistentData()) - } - } - - AddUserInfoFromApi(authInfoObj) { - dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() - - if (dataSource) { - apiStatus := dataSource.GetStatus() - - if (apiStatus) { - if (apiStatus.Has("email")) { - authInfoObj.Set("email", apiStatus["email"], true) - } - - if (apiStatus.Has("photo")) { - authInfoObj.Set("photo", apiStatus["photo"], true) - } - } - } - } - - AlterApiRequest(request) { - if (this.IsAuthenticated()) { - if (this.AuthenticationNeedsRefresh()) { - this.RefreshAuthentication() - } - - this.authProviderObj.AddAuthInfoToRequest(this.authInfoObj, request) - } - } -} From 981d4023ad499c52d2cda8d372a02739f66bd7bd Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 18:48:15 -0500 Subject: [PATCH 056/138] Refactor status indicators to show one per account, work in progress --- Lib/Launchpad/App/Launchpad.ahk | 12 ++++- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 8 ++- .../WebServices/Entity/WebServiceEntity.ahk | 27 ++++++++++ Lib/Shared/Volantis.App/Gui/GuiBase.ahk | 8 +-- .../GuiControl/GuiControlBase.ahk | 2 + .../GuiControl/StatusIndicatorControl.ahk | 37 ++++++++----- .../GuiControl/TitlebarControl.ahk | 53 +++++++++++++------ 7 files changed, 108 insertions(+), 39 deletions(-) diff --git a/Lib/Launchpad/App/Launchpad.ahk b/Lib/Launchpad/App/Launchpad.ahk index 767330d7..b752ac6e 100644 --- a/Lib/Launchpad/App/Launchpad.ahk +++ b/Lib/Launchpad/App/Launchpad.ahk @@ -155,7 +155,17 @@ UpdateStatusIndicators() { if (this.Service("manager.gui").Has("MainWindow")) { - this.Service("manager.gui")["MainWindow"].UpdateStatusIndicator() + serviceMgr := this.container["entity_manager.web_service"] + webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "StatusIndicator") + .Execute() + + windowObj := this.Service("manager.gui")["MainWindow"] + + for serviceId, webService in webServices { + windowObj.UpdateStatusIndicator(webService) + } } } diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 066e43a4..082f57dd 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -191,9 +191,8 @@ return webService } - GetStatusInfo() { + GetStatusInfo(webService) { info := "" - webService := this._getApiWebService() if (webService) { info := webService.GetStatusInfo() @@ -225,7 +224,7 @@ )) if (accountResult == "OK") { - this.UpdateStatusIndicator() + this.UpdateStatusIndicator(webService) } } else if (result == "Logout") { if (webService) { @@ -238,9 +237,8 @@ } } - StatusWindowIsOnline() { + StatusWindowIsOnline(webService) { isOnline := false - webService := this._getApiWebService() if (webService) { isOnline := webService.Authenticated diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 11c29b32..6d5fb68f 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -3,6 +3,7 @@ class WebServiceEntity extends AppEntityBase { stateObj := "" persistentStateObj := "" mergeDataFromApi := false + statusIndicators := [] Authenticated { get => this.IsAuthenticated() @@ -70,9 +71,35 @@ class WebServiceEntity extends AppEntityBase { "default", true ) + definitions["StatusIndicator"] := Map( + "type", "boolean", + "required", false, + "default", (this.idVal == "api") + ) + + definitions["StatusIndicatorExpanded"] := Map( + "type", "boolean", + "required", false, + "default", (this.idVal == "api") + ) + return definitions } + GetStatusIndicators() { + return this.statusIndicators + } + + AddStatusIndicator(statusIndicatorCtl) { + this.statusIndicators.Push(statusIndicatorCtl) + } + + UpdateStatusIndicators() { + for , statusIndicatorCtl in this.statusIndicators { + statusIndicatorCtl.UpdateStatusIndicator() + } + } + IsAuthenticated() { isAuthenticated := false diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk index 0cb58f9e..8a89cabc 100644 --- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk @@ -349,17 +349,17 @@ class GuiBase { return this.guiObj.AddEdit(opts, defaultValue) } - UpdateStatusIndicator() { + UpdateStatusIndicator(webService) { if (this.config["showStatusIndicator"]) { - this.titlebar.statusIndicator.UpdateStatusIndicator(this.GetStatusInfo(), this.StatusWindowIsOnline() ? "status" : "statusOffline") + webService.UpdateStatusIndicators() } } - StatusWindowIsOnline() { + StatusWindowIsOnline(webService) { return false } - GetStatusInfo() { + GetStatusInfo(webService) { return Map("name", "", "photo", "") } diff --git a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk b/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk index 399d18cc..9d58675e 100644 --- a/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/GuiControlBase.ahk @@ -1,5 +1,6 @@ class GuiControlBase { app := "" + container := "" guiObj := "" ctl := "" defaultH := 20 @@ -12,6 +13,7 @@ class GuiControlBase { __New(guiObj, options := "", heading := "", params*) { InvalidParameterException.CheckTypes("GuiControlBase", "guiObj", guiObj, "GuiBase") this.app := guiObj.app + this.container := this.app.Services this.guiObj := guiObj if (HasBase(options, GuiControlParameters.Prototype)) { diff --git a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk index bec24d81..a3aebcef 100644 --- a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk @@ -1,32 +1,42 @@ class StatusIndicatorControl extends GuiControlBase { - statusIndicatorW := 120 - statusIndicatorMinW := 120 + statusIndicatorW := 40 + statusIndicatorMinW := 40 + statusIndicatorExpandedMinW := 120 innerControl := "" + webService := "" - CreateControl(statusInfo, handler := "", statusStyle := "status") { + CreateControl(webService, handler := "") { super.CreateControl(false) + this.webService := webService if (handler == "" && HasMethod(this.guiObj, "OnStatusIndicatorClick")) { handler := "OnStatusIndicatorClick" } + this.statusIndicatorW := webService["StatusIndicatorExpanded"] ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW + options := this.parameters.SetDefaultOptions(this.parameters["options"].Clone(), [ "x+" . this.guiObj.margin, "yp", "w" . this.statusIndicatorW, "h26", - "vStatusIndicator" + "vStatusIndicator" . webService.Id ]) - - name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : "" - photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : "" + + statusInfo := this.guiObj.GetStatusInfo(webService) + statusStyle := this.guiObj.StatusWindowIsOnline(webService) ? "status" : "statusOffline" + name := (statusInfo && statusInfo.Has("name") && webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : "" + photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : "" this.innerControl := this.guiObj.Add("ButtonControl", options, name, handler, statusStyle, Map("photo", photo)) this.ctl := this.innerControl.ctl return this.ctl } - UpdateStatusIndicator(statusInfo, statusStyle := "status") { + UpdateStatusIndicator() { + statusInfo := this.guiObj.GetStatusInfo(this.webService) + statusStyle := this.guiObj.StatusWindowIsOnline(this.webService) ? "status" : "statusOffline" + oldW := this.statusIndicatorW newW := this.CalculateWidth(statusInfo) this.statusIndicatorW := newW @@ -38,23 +48,24 @@ class StatusIndicatorControl extends GuiControlBase { this.ctl.Move(statusX - difference,, statusW + difference) } - name := statusInfo && statusInfo.Has("name") ? statusInfo["name"] : "" - photo := statusInfo && statusInfo.Has("photo") ? statusInfo["photo"] : "" + name := (statusInfo && statusInfo.Has("name") && this.webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : "" + photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : "" this.guiObj.themeObj.DrawButton(this.ctl, name, statusStyle, Map("photo", photo)) return difference } CalculateWidth(statusInfo) { - width := this.statusIndicatorMinW + expanded := this.webService["StatusIndicatorExpanded"] + width := expanded ? this.statusIndicatorExpandedMinW : this.statusIndicatorMinW requiredW := 10 if (statusInfo) { - if (statusInfo.Has("name")) { + if (statusInfo.Has("name") && expanded) { requiredW += this.guiObj.themeObj.CalculateTextWidth(statusInfo["name"]) } - if (StatusInfo.Has("photo")) { + if (StatusInfo.Has("photo") || !expanded) { requiredW += 26 } } diff --git a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk index 44cadd03..872dccae 100644 --- a/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/TitlebarControl.ahk @@ -5,10 +5,12 @@ class TitlebarControl extends GuiControlBase { iconW := 16 titlebarH := 31 titlebarButtonW := 16 - initialStatusIndicatorW := 120 + initialStatusIndicatorW := 40 + initialStatusIndicatorExpandedW := 120 titleButton := "" titleText := "" statusIndicator := "" + statusIndicators := [] minBtn := "" maxBtn := "" unmaxBtn := "" @@ -33,9 +35,21 @@ class TitlebarControl extends GuiControlBase { buttonsW := 0 statusIndicatorW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorW : 0 + statusIndicatorExpandedW := this.guiObj.config["showStatusIndicator"] ? this.initialStatusIndicatorExpandedW : 0 + + serviceMgr := this.container["entity_manager.web_service"] + webServices := Map() if (this.guiObj.config["showStatusIndicator"]) { - buttonsW += statusIndicatorW + (this.guiObj.margin * 2) + webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Condition(IsTrueCondition(), "StatusIndicator") + .Execute() + + for serviceId, webService in webServices { + serviceStatusW := (webService["StatusIndicatorExpanded"]) ? statusIndicatorExpandedW : statusIndicatorW + buttonsW += serviceStatusW + (this.guiObj.margin * 2) + } } if (this.guiObj.config["showMinimize"]) { @@ -84,20 +98,19 @@ class TitlebarControl extends GuiControlBase { this.titleText := this.guiObj.guiObj.AddText(opts, titleText) } - if (this.guiObj.config["showStatusIndicator"]) { - opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . statusIndicatorW - statusStyle := this.guiObj.StatusWindowIsOnline() ? "status" : "statusOffline" - initialInfo := Map() - statusInfo := this.guiObj.GetStatusInfo() - - if (statusInfo) { - initialInfo := statusInfo.Clone() - initialInfo["name"] := "" + if (this.guiObj.config["showStatusIndicator"] && webServices.Count) { + for serviceId, service in webServices { + expanded := service["StatusIndicatorExpanded"] + serviceStatusW := expanded ? statusIndicatorExpandedW : statusIndicatorW + opts := "x" . buttonsX . " y" . (this.topMargin - 5) . " w" . serviceStatusW + + statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", service, "") + this.statusIndicators.Push(statusIndicator) + service.AddStatusIndicator(statusIndicator) + + difference := statusIndicator.UpdateStatusIndicator() + buttonsX += serviceStatusW + (this.guiObj.margin * 2) } - - this.statusIndicator := this.guiObj.Add("StatusIndicatorControl", opts, "", initialInfo, "", statusStyle) - difference := this.statusIndicator.UpdateStatusIndicator(statusInfo, statusStyle) - buttonsX += this.initialStatusIndicatorW + (this.guiObj.margin * 2) } handler := this.RegisterCallback("OnTitlebarButtonClick") @@ -198,7 +211,15 @@ class TitlebarControl extends GuiControlBase { this.guiObj.AutoXYWH("w", ["WindowTitlebar"]) if (this.guiObj.config["showStatusIndicator"]) { - this.guiObj.AutoXYWH("x*", ["StatusIndicator"]) + indicatorCtlNames := [] + + for index, statusIndicator in this.statusIndicators { + indicatorCtlNames.Push(statusIndicator.ctl.Name) + } + + if (indicatorCtlNames.Length) { + this.guiObj.AutoXYWH("x*", indicatorCtlNames) + } } if (this.guiObj.config["showClose"]) { From 9fd1e786f2655ee51cfba9a59c608e08ab908d5a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 19:41:21 -0500 Subject: [PATCH 057/138] Remove player_name setting --- Launchpad.services.json | 1 - Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index 233455d7..704774b4 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -21,7 +21,6 @@ "config.platforms_file": "@@{data_dir}\\Platforms.json", "config.platforms_view_mode": "Report", "config.tasks_view_mode": "Report", - "config.player_name": "", "config.rebuild_existing_launchers": false, "config.tasks_file": "@@{data_dir}\\Tasks.json", "config.use_advanced_launcher_editor": false, diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 6d5fb68f..2307d117 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -259,12 +259,9 @@ class WebServiceEntity extends AppEntityBase { email := "" if (this.Authenticated) { - playerName := this.app.Config["player_name"] email := this.AuthData["email"] - if (playerName) { - statusText := playerName - } else if (email) { + if (email) { statusText := email } else { statusText := "Logged in" From b87c2c9bb826988b121540657e4060e88258c07a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Mon, 12 Dec 2022 19:41:57 -0500 Subject: [PATCH 058/138] Move callbacks for status indicators into the StatusIndicator control and WebServiceEntity object --- .../Gui/Dialog/AccountInfoWindow.ahk | 51 ++++++++--------- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 56 ------------------- .../WebServices/Entity/WebServiceEntity.ahk | 13 +++++ Lib/Shared/Volantis.App/Gui/GuiBase.ahk | 8 --- .../GuiControl/StatusIndicatorControl.ahk | 49 ++++++++++++++-- .../Service/ComponentManager/GuiManager.ahk | 2 +- 6 files changed, 82 insertions(+), 97 deletions(-) diff --git a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk index cdf5ba42..942ca295 100644 --- a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk +++ b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk @@ -3,6 +3,7 @@ defaults := super.GetDefaultConfig(container, config) defaults["title"] := "Account Info" defaults["buttons"] := "*&Save|&Cancel|&Logout" + defaults["webService"] := "" return defaults } @@ -12,26 +13,20 @@ if (this.app.Services.Has("entity_manager.web_service")) { entityMgr := this.app.Services["entity_manager.web_service"] - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { - info := Map( - "name", "", - "email", "", - "photo", "" - ) - - ; @todo Pull this information from the API web service - - if (info) { - opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin - this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) - this.guiObj.AddText(opts, "Email: " . info["email"]) - } + webService := this.config["webService"] + + if (!webService) { + throw AppException("Opened AccountInfoWindow without a webService specified.") } - } - this.AddHeading("Player Name") - this.AddEdit("PlayerName", this.app.Config["player_name"], "", 250) - this.guiObj.AddText("w" . this.windowSettings["contentWidth"], "Note: Player name is stored locally and not synced with your online Launchpad account yet.") + info := webService.GetStatusInfo() + + if (info) { + opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin + this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) + this.guiObj.AddText(opts, "Email: " . info["email"]) + } + } position := "Wrap x" . this.margin . " y+" . this.margin options := position . " w" . this.windowSettings["contentWidth"] . " +0x200 c" . this.themeObj.GetColor("textLink") @@ -39,18 +34,18 @@ } ProcessResult(result, submittedData := "") { - - if (result == "Logout") { - if (this.app.Services.Has("entity_manager.web_service")) { - entityMgr := this.app.Services["entity_manager.web_service"] - - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { - entityMgr["api"].Logout() - } + webService := this.config["webService"] + + if (webService) { + webService.Logout() + } + } else if (result == "Login") { + webService := this.config["webService"] + + if (webService) { + webService.Login() } - } else if (result == "Save" && submittedData) { - this.app.Config["player_name"] := submittedData.PlayerName } return result diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 082f57dd..65108178 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -191,62 +191,6 @@ return webService } - GetStatusInfo(webService) { - info := "" - - if (webService) { - info := webService.GetStatusInfo() - } - - return info - } - - OnStatusIndicatorClick(btn, info) { - menuItems := [] - webService := this._getApiWebService() - - if (webService) { - if (webService.Authenticated) { - menuItems.Push(Map("label", "Account Details", "name", "AccountDetails")) - menuItems.Push(Map("label", "Logout", "name", "Logout")) - } else { - menuItems.Push(Map("label", "Login", "name", "Login")) - } - } - - result := this.container["manager.gui"].Menu(menuItems, this, btn) - - if (result == "AccountDetails") { - accountResult := this.container["manager.gui"].Dialog(Map( - "type", "AccountInfoWindow", - "ownerOrParent", this.guiId, - "child", true - )) - - if (accountResult == "OK") { - this.UpdateStatusIndicator(webService) - } - } else if (result == "Logout") { - if (webService) { - webService.Logout() - } - } else if (result == "Login") { - if (webService) { - webService.Login() - } - } - } - - StatusWindowIsOnline(webService) { - isOnline := false - - if (webService) { - isOnline := webService.Authenticated - } - - return isOnline - } - FormatDate(timestamp) { shortDate := FormatTime(timestamp, "ShortDate") shortTime := FormatTime(timestamp, "Time") diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 2307d117..c55ae3ea 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -277,4 +277,17 @@ class WebServiceEntity extends AppEntityBase { return Map("name", statusText, "email", email, "photo", imgPath) } + + ShowAccountDetails() { + accountResult := this.container["manager.gui"].Dialog(Map( + "type", "AccountInfoWindow", + "ownerOrParent", this.guiId, + "child", true, + "webService", this + )) + + if (accountResult == "OK" || accountResult == "Logout" || accountResult == "Login") { + this.UpdateStatusIndicators() + } + } } diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk index 8a89cabc..1c59feaf 100644 --- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk @@ -355,14 +355,6 @@ class GuiBase { } } - StatusWindowIsOnline(webService) { - return false - } - - GetStatusInfo(webService) { - return Map("name", "", "photo", "") - } - SetFont(fontPreset := "normal", extraStyles := "", colorName := "text") { this.guiObj.SetFont() this.guiObj.SetFont("c" . this.themeObj.GetColor(colorName) . " " . this.themeObj.GetFont(fontPreset) . " " . extraStyles) diff --git a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk index a3aebcef..12bceb51 100644 --- a/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/StatusIndicatorControl.ahk @@ -9,6 +9,10 @@ class StatusIndicatorControl extends GuiControlBase { super.CreateControl(false) this.webService := webService + if (!handler) { + handler := ObjBindMethod(this, "OnStatusIndicatorClick") + } + if (handler == "" && HasMethod(this.guiObj, "OnStatusIndicatorClick")) { handler := "OnStatusIndicatorClick" } @@ -23,8 +27,8 @@ class StatusIndicatorControl extends GuiControlBase { "vStatusIndicator" . webService.Id ]) - statusInfo := this.guiObj.GetStatusInfo(webService) - statusStyle := this.guiObj.StatusWindowIsOnline(webService) ? "status" : "statusOffline" + statusInfo := webService.GetStatusInfo() + statusStyle := webService.Authenticated ? "status" : "statusOffline" name := (statusInfo && statusInfo.Has("name") && webService["StatusIndicatorExpanded"]) ? statusInfo["name"] : "" photo := (statusInfo && statusInfo.Has("photo")) ? statusInfo["photo"] : "" @@ -33,9 +37,46 @@ class StatusIndicatorControl extends GuiControlBase { return this.ctl } + OnStatusIndicatorClick(btn, info) { + webService := this.webService + menuItems := [] + + if (webService) { + if (webService.Authenticated) { + menuItems.Push(Map("label", "Account Details", "name", "AccountDetails")) + menuItems.Push(Map("label", "Logout", "name", "Logout")) + } else { + menuItems.Push(Map("label", "Login", "name", "Login")) + } + } + + result := this.container["manager.gui"].Menu(menuItems, this, btn) + + if (result == "AccountDetails") { + accountResult := this.container["manager.gui"].Dialog(Map( + "type", "AccountInfoWindow", + "webService", this.webService, + "ownerOrParent", this.guiObj.guiId, + "child", true + )) + + if (accountResult == "OK") { + this.UpdateStatusIndicator(webService) + } + } else if (result == "Logout") { + if (webService) { + webService.Logout() + } + } else if (result == "Login") { + if (webService) { + webService.Login() + } + } + } + UpdateStatusIndicator() { - statusInfo := this.guiObj.GetStatusInfo(this.webService) - statusStyle := this.guiObj.StatusWindowIsOnline(this.webService) ? "status" : "statusOffline" + statusInfo := this.webService.GetStatusInfo() + statusStyle := this.webService.Authenticated ? "status" : "statusOffline" oldW := this.statusIndicatorW newW := this.CalculateWidth(statusInfo) diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk b/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk index cfbcfd9e..022e8688 100644 --- a/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk +++ b/Lib/Shared/Volantis.App/Service/ComponentManager/GuiManager.ahk @@ -278,7 +278,7 @@ class GuiManager extends ComponentManagerBase { guiObj := obj } else if (HasBase(obj, GuiBase.Prototype)) { guiObj := obj.guiObj - } else if (Type(guiObj) == "String" && this.Has(obj)) { + } else if (Type(obj) == "String" && this.Has(obj)) { guiObj := this[obj].guiObj } From dd9dba1bd05002d7a9e0c5278509eb22d404f71c Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 02:57:29 -0500 Subject: [PATCH 059/138] Change "email" to "account" in authentication data so it is more generic --- Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk | 2 +- .../Modules/LaunchpadApi/DataSource/ApiDataSource.ahk | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk index 942ca295..11299d17 100644 --- a/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk +++ b/Lib/Launchpad/Gui/Dialog/AccountInfoWindow.ahk @@ -24,7 +24,7 @@ if (info) { opts := "w" . this.windowSettings["contentWidth"] . " x" . this.margin . " y+" . this.margin this.guiObj.AddPicture("x" . this.margin . " y+" . this.margin, info["photo"]) - this.guiObj.AddText(opts, "Email: " . info["email"]) + this.guiObj.AddText(opts, "Account: " . info["account"]) } } diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk index d8c4363b..479951ef 100644 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk @@ -89,7 +89,7 @@ class ApiDataSource extends DataSourceBase { path := "status" statusExpire := 5 ;60 - status := Map("authenticated", false, "email", "", "photo", "") + status := Map("authenticated", false, "account", "", "photo", "") if (this.app.Config["api_authentication"]) { entityMgr := webService := this.app.Service("entity_manager.web_service") @@ -99,6 +99,11 @@ class ApiDataSource extends DataSourceBase { if (statusResult) { status := JsonData().FromString(&statusResult) + + if (status.Has("email")) { + status["account"] := status["email"] + status.Delete("email") + } } } } From 383d2e295ea77cf7766dcfbbd0de037c92bcc3b0 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 03:42:58 -0500 Subject: [PATCH 060/138] Committing WebServiceLayerSource for posterity because I'm about to rewrite it --- .../LayerSource/WebServiceLayerSource.ahk | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk diff --git a/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk new file mode 100644 index 00000000..37c4a943 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk @@ -0,0 +1,59 @@ +class WebServiceLayerSource extends LayerSourceBase { + webService := "" + path := "" + useAuthentication := false + + ; A map in the format oldKey => newKey + fieldMap := Map() + + __New(webService, path, useAuthentication := false) { + this.webService := webService + this.path := path + this.useAuthentication := useAuthentication + } + + SaveData(data := "") { + ; Web services don't supoport saving layer data yet. + return this + } + + LoadData() { + request := this.webService.Request(this.path, "GET", "", this.useAuthentication, true) + response := request.Send() + responseBody := response.GetResponseBody() + + data := "" + + if (responseBody) { + data := JsonData().FromString(&responseBody) + } else { + data := Map() + } + + for oldKey, newKey in this.fieldMap { + if data.Has(oldKey) { + data[newKey] := data[oldKey] + data.Delete(oldKey) + } + } + + return data + } + + HasData() { + request := this.webService.Request(this.path, "HEAD", "", this.useAuthentication, false) + response := request.Send() + exists := response.GetHttpStatusCode() + + if (!exists) { + request.cacheObj.SetNotFound(this.path) + } + + return exists + } + + DeleteData() { + ; Web services don't support deleting layer data yet. + return this + } +} From f10500f36c9aa17e9dd679e7e7849a0a5ffea617 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:23:23 -0500 Subject: [PATCH 061/138] Rename "api" services to "launchpad_api" --- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 4 ++-- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 4 ++-- .../LaunchpadApi/DataSource/ApiDataSource.ahk | 6 +++--- .../LaunchpadApi/LaunchpadApi.module.json | 20 +++++++++---------- .../ManageWindow/ManageWebServicesWindow.ahk | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 65108178..d5f1b6bc 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -183,8 +183,8 @@ if (this.app.Services.Has("entity_manager.web_service")) { entityMgr := this.app.Services["entity_manager.web_service"] - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { - webService := entityMgr["api"] + if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { + webService := entityMgr["launchpad_api"] } } diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 0f1b87c7..95b11baa 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -63,8 +63,8 @@ class LaunchpadBuilder extends AppBase { if (buildInfo.DeployToApi && this.Services.Has("entity_manager.web_service")) { entityMgr := this.Services["entity_manager.web_service"] - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { - entityMgr["api"].Login() + if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { + entityMgr["launchpad_api"].Login() } } diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk index 479951ef..83747962 100644 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk @@ -38,8 +38,8 @@ class ApiDataSource extends DataSourceBase { if (this.app.Config["api_authentication"]) { entityMgr := webService := this.app.Service("entity_manager.web_service") - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"]) { - webService := this.app.Service("entity_manager.web_service")["api"] + if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { + webService := this.app.Service("entity_manager.web_service")["launchpad_api"] webService["Provider"]["Authenticator"].AlterRequest(webService, request) } @@ -94,7 +94,7 @@ class ApiDataSource extends DataSourceBase { if (this.app.Config["api_authentication"]) { entityMgr := webService := this.app.Service("entity_manager.web_service") - if (entityMgr.Has("api") && entityMgr["api"]["Enabled"] && entityMgr["api"]["Authenticated"]) { + if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"] && entityMgr["launchpad_api"]["Authenticated"]) { statusResult := this.ReadItem(path, true) if (statusResult) { diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index bc9da04e..ad878fd5 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -14,23 +14,23 @@ "appVersion": "", "dependencies": ["WebServices"] }, + "parameters": { + "config.data_source_key": "launchpad_api", + "config.api_endpoint": "https://api.launchpad.games/v1", + "config.api_authentication": true + }, "services": { - "data_source.api": { + "data_source.launchpad_api": { "class": "ApiDataSource", - "arguments": ["@{App}", "@manager.cache", "api", "@@config.api_endpoint"] + "arguments": ["@{App}", "@manager.cache", "launchpad_api", "@@config.api_endpoint"] }, - "cache_state.api": { + "cache_state.launchpad_api": { "class": "CacheState", "arguments": ["@{App}", "@@config.cache_dir", "API.json"] }, - "cache.api": { + "cache.launchpad_api": { "class": "FileCache", - "arguments": ["@{App}", "@cache_state.api", "@@config.cache_dir", "API"] + "arguments": ["@{App}", "@cache_state.launchpad_api", "@@config.cache_dir", "API"] } - }, - "parameters": { - "config.data_source_key": "api", - "config.api_endpoint": "https://api.launchpad.games/v1", - "config.api_authentication": true } } diff --git a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk index 83a36400..873a9596 100644 --- a/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/ManageWindow/ManageWebServicesWindow.ahk @@ -39,7 +39,7 @@ class ManageWebServicesWindow extends ManageEntitiesWindow { shouldShow := super._shouldShowButton(entityObj, buttonName) if (shouldShow && buttonName == "DeleteEntity") { - shouldShow := entityObj.Id != "api" + shouldShow := entityObj.Id != "launchpad_api" } return shouldShow From a882b8cfb224fb7759afe5397791466bdbee4a7f Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:30:37 -0500 Subject: [PATCH 062/138] Add WebServiceAdapter entity types, the concept of web service data types, and move launchpad api definitions into the LaunchpadApi module --- Lib/Shared/Includes.ahk | 4 + .../LaunchpadApi/LaunchpadApi.module.json | 95 +++++++- .../WebServices/Entity/WebServiceEntity.ahk | 63 +++++- .../Factory/WebServiceAdapterFactory.ahk | 41 ++++ .../FileWebServiceAdapter.ahk | 5 + .../JsonWebServiceAdapter.ahk | 3 + .../WebServiceAdapterBase.ahk | 210 ++++++++++++++++++ .../WebServices/WebServices.module.json | 65 ++++-- 8 files changed, 459 insertions(+), 27 deletions(-) create mode 100644 Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk create mode 100644 Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 129aa118..46f370e3 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -6,8 +6,12 @@ #Include Modules\WebServices\Event\WebServiceResponseEvent.ahk #Include Modules\WebServices\Events\WebServicesEvents.ahk #Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk +#Include Modules\WebServices\Factory\WebServiceDataAdapterFactory.ahk #Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk #Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk +#Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk +#Include Modules\WebServices\WebServiceAdapter\JsonWebServiceAdapter.ahk +#Include Modules\WebServices\WebServiceAdapter\WebServiceAdapterBase.ahk #Include Modules\WebServices\WebServiceAuthenticator\JwtWebServiceAuthenticator.ahk #Include Modules\WebServices\WebServiceAuthenticator\WebServiceAuthenticatorBase.ahk #Include Modules\WebServices\WebServiceRequest\BasicWebServiceRequest.ahk diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index ad878fd5..81f1c228 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -17,7 +17,100 @@ "parameters": { "config.data_source_key": "launchpad_api", "config.api_endpoint": "https://api.launchpad.games/v1", - "config.api_authentication": true + "config.api_authentication": true, + "web_services.providers.launchpad_api": { + "name": "Launchpad API", + "EndpointUrl": "https://api.launchpad.games/v1", + "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", + "AuthenticationRefreshPath": "token", + "IconSrc": "logo", + "SupportsAuthentication": true, + "Authenticator": "jwt", + "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", + "LoginWindow": "LaunchpadLoginWindow" + }, + "web_services.services.launchpad_api": { + "name": "Launchpad API", + "Provider": "launchpad_api" + }, + "web_services.adapters.launchpad_api.account_info": { + "dataType": "account_info", + "requestPath": "/status", + "cacheResponse": false, + "readAuth": true, + "dataMap": { + "email": "account" + } + }, + "web_services.adapters.launchpad_api.error_submission": { + "dataType": "error_submission", + "requestPath": "/error-submissions/{submission}", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.adapters.launchpad_api.feedback_submission": { + "dataType": "feedback_submission", + "requestPath": "/feedback-submissions/{submission}", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.adapters.launchpad_api.game_submission": { + "dataType": "game_submission", + "requestPath": "/game-submissions/{submission}", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.adapters.launchpad_api.release_info": { + "dataType": "release_info", + "requestPath": "/release-info/{tag}", + "cacheMaxAge": 1800 + }, + "web_services.adapters.launchpad_api.platform_list": { + "dataType": "entity_list", + "requestPath": "/game-platforms", + "entityType": "platform" + }, + "web_services.adapters.launchpad_api.platform_data": { + "dataType": "entity_data", + "requestPath": "/game-platforms/{id}", + "entityType": "platform" + }, + "web_services.adapters.launchpad_api.game_type_list": { + "dataType": "entity_list", + "requestPath": "/game-types", + "entityType": "managed_game" + }, + "web_services.adapters.launchpad_api.game_type_data": { + "dataType": "entity_data", + "requestPath": "/game-types/{id}", + "entityType": "managed_game" + }, + "web_services.adapters.launchpad_api.launcher_list": { + "dataType": "entity_list", + "requestPath": "/games", + "entityType": "launcher" + }, + "web_services.adapters.launchpad_api.launcher_data": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "entityType": "launcher" + }, + "web_services.adapters.launchpad_api.launcher_type_list": { + "dataType": "entity_list", + "requestPath": "/launcher-types", + "entityType": "managed_launcher" + }, + "web_services.adapters.launchpad_api.launcher_type_data": { + "dataType": "entity_data", + "requestPath": "/launcher-types/{id}", + "entityType": "managed_launcher" + } }, "services": { "data_source.launchpad_api": { diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index c55ae3ea..530c0603 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -4,6 +4,8 @@ class WebServiceEntity extends AppEntityBase { persistentStateObj := "" mergeDataFromApi := false statusIndicators := [] + adapters := Map() + adapterFactory := "" Authenticated { get => this.IsAuthenticated() @@ -18,10 +20,11 @@ class WebServiceEntity extends AppEntityBase { set => this.SetAuthData(key, value) } - __New(app, id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, eventMgr, storageObj, idSanitizer, parentEntity := "") { + __New(app, id, entityTypeId, container, adapterFactory, cacheObj, stateObj, persistentStateObj, eventMgr, storageObj, idSanitizer, parentEntity := "") { this.cacheObj := cacheObj this.stateObj := stateObj this.persistentStateObj := persistentStateObj + this.adapterFactory := adapterFactory super.__New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity) } @@ -34,6 +37,7 @@ class WebServiceEntity extends AppEntityBase { id, entityTypeId, container, + container.Get("web_services.adapter_factory"), container.Get("cache.web_services"), container.Get("state.web_services_tmp"), container.Get("state.web_services"), @@ -44,10 +48,59 @@ class WebServiceEntity extends AppEntityBase { ) } + GetAdapters(filters := "") { + if (!filters) { + filters := Map() + } + + adapterData := this.container.GetParameter("web_services.adapters." . this["Provider"]["id"]) + + adapters := Map() + + for key, definition in adapterData { + adapter := this.GetAdapter(key) + definition := adapter.definition + include := true + + for filterKey, filterVal in filters { + if (!definition.Has(filterKey) || definition[filterKey] != filterVal) { + include := false + + break + } + } + + if (include) { + adapters[key] := adapter + } + } + + return adapters + } + + GetAdapter(key) { + adapter := "" + + if (this.adapters.Has(key)) { + adapter := this.adapters[key] + } + + if (!adapter) { + param := "web_services.adapters." . this["Provider"]["id"] + + if (this.container.HasParameter(param)) { + adapter := this.adapterFactory.CreateWebServiceAdapter(this, this.container.GetParameter(param)) + this.adapters[key] := adapter + } + } + + return adapter + } + BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - if (this.idVal == "api" && definitions.Has("name")) { + if (this.idVal == "launchpad_api" && definitions.Has("name")) { definitions["name"]["editable"] := false } @@ -62,7 +115,7 @@ class WebServiceEntity extends AppEntityBase { "type", "boolean", "description", "Automatically authenticate with this service when Launchpad starts.", "required", false, - "default", (this.idVal == "api") + "default", (this.idVal == "launchpad_api") ) definitions["Enabled"] := Map( @@ -74,13 +127,13 @@ class WebServiceEntity extends AppEntityBase { definitions["StatusIndicator"] := Map( "type", "boolean", "required", false, - "default", (this.idVal == "api") + "default", (this.idVal == "launchpad_api") ) definitions["StatusIndicatorExpanded"] := Map( "type", "boolean", "required", false, - "default", (this.idVal == "api") + "default", (this.idVal == "launchpad_api") ) return definitions diff --git a/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk b/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk new file mode 100644 index 00000000..d1c423a4 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Factory/WebServiceAdapterFactory.ahk @@ -0,0 +1,41 @@ +class WebServiceAdapterFactory { + container := "" + + __New(container) { + this.container := container + } + + CreateWebServiceAdapter(webService, definition) { + adapterTypes := this.container.GetParameter("web_services.adapter_types") + + if (!definition.Has("adapterType") || !definition["adapterType"]) { + definition["adapterType"] := "json" + } + + if (adapterTypes.Has(definition["adapterType"])) { + defaults := adapterTypes[definition["adapterType"]] + + if (Type(defaults) == "String") { + defaults := Map("class", defaults) + } + + for key, val in defaults { + if (!definition.Has(key)) { + definition[key] := val + } + } + } + + if (!definition.Has("class")) { + throw AppException("Adapter class not known.") + } + + adapterClass := definition["class"] + + if (!HasMethod(%adapterClass%)) { + throw AppException("Adapter class " . adapterClass . " was not found.") + } + + return %adapterClass%.Create(this.container, webService, definition) + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk new file mode 100644 index 00000000..22d44841 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk @@ -0,0 +1,5 @@ +class FileWebServiceAdapter extends WebServiceAdapterBase { + dataType := "" + + ; @todo Implement file downloading +} diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk new file mode 100644 index 00000000..7c61899b --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk @@ -0,0 +1,3 @@ +class JsonWebServiceAdapter extends WebServiceAdapterBase { + dataType := "JsonData" +} \ No newline at end of file diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk new file mode 100644 index 00000000..a7b3e7d6 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -0,0 +1,210 @@ +class WebServiceAdapterBase { + container := "" + webService := "" + definition := "" + dataType := "" + merger := "" + + __New(container, merger, webService, definition) { + this.container := container + this.merger := merger + this.webService := webService + + if (!definition) { + definition := Map() + } + + if (!HasBase(definition, Map.Prototype)) { + throw AppException("Definition must be a Map-like object.") + } + + this.definition := this.merger.Merge(this.GetDefaultDefinition(), definition) + } + + static Create(container, webService, definition) { + className := this.Prototype.__Class + + return %className%( + container, + container.Get("merger.list"), + webService, + definition + ) + } + + GetDefaultDefinition() { + return Map( + "dataType", "", + "adapterType", "json", + "requestPath", "", + "requestData", "", + "cacheResponse", true, + "cacheMaxAge", 186400, + "createAllow", false, + "createMethod", "POST", + "createAuth", true, + "readAllow", true, + "readMethod", "GET", + "readAuth", false, + "updateAllow", false, + "updateMethod", "POST", + "updateAuth", true, + "deleteAllow", false, + "deleteMethod", "PUT", + "deleteAuth", true, + "dataMap", Map() + ) + } + + CreateData(data, params := "") { + if (!this.definition["createAllow"]) { + throw AppException("The 'create' operation is not allowed on this data adapter.") + } + + return this._request( + params, + this.definition["createMethod"], + data ? data : this._getData(params), + this.definition["createAuth"], + false + ).Send().IsSuccessful() + } + + DataExists(params := "") { + if (!this.definition["readAllow"]) { + throw AppException("The 'read' operation is not allowed on this data adapter.") + } + + return this._request( + params, + this.definition["readMethod"], + this._getData(params), + this.definition["readAuth"], + false + ).Send().IsSuccessful() + } + + ReadData(params := "") { + if (!this.definition["readAllow"]) { + throw AppException("The 'read' operation is not allowed on this data adapter.") + } + + response := this._request( + params, + this.definition["readMethod"], + this._getData(params), + this.definition["readAuth"], + this.definition["cacheResponse"] + ).Send() + + data := "" + + if (response.IsSuccessful()) { + data := response.GetResponseBody() + data := this._parseData(data, params) + this._mapData(data, params) + } + + return data + } + + UpdateData(data, params := "") { + if (!this.definition["updateAllow"]) { + throw AppException("The 'update' operation is not allowed on this data adapter.") + } + + return this._request( + params, + this.definition["updateMethod"], + data ? data : this._getData(params), + this.definition["updateAuth"], + false + ).Send().IsSuccessful() + } + + DeleteData(params := "") { + if (!this.definition["deleteAllow"]) { + throw AppException("The 'delete' operation is not allowed on this data adapter.") + } + + return this._request( + params, + this.definition["deleteMethod"], + this._getData(params), + this.definition["deleteAuth"], + false + ).Send().IsSuccessful() + } + + _requestPath(params) { + requestPath := this.definition["requestPath"] + isFound := true + + while isFound { + match := "" + isFound := RegExMatch(requestPath, "({([^}]+)})", &match) + + if (isFound) { + key := match[2] + + replacement := (params && params.Has(key)) + ? params[key] + : "" + + requestPath := StrReplace(requestPath, match[1], replacement) + } + } + + return requestPath + } + + _request(params, method, data, cacheResponse) { + return this.webService.Request( + this.definition["requestPath"], + method, + data, + this.definition["useAuthentication"], + cacheResponse + ) + } + + _mapData(data, params, reverse := false) { + if ( + data + && HasBase(data, Map.Prototype) + && this.definition["dataMap"] + && HasBase(this.definition["dataMap"], Map.Prototype) + ) { + for key1, key2 in this.definition["dataMap"] { + oldKey := reverse ? key2 : key1 + newKey := reverse ? key1 : key2 + + if (data.Has(oldKey)) { + data[newKey] := data[oldKey] + data.Delete(oldKey) + } + } + } + } + + _parseData(data, params) { + if (data && this.dataType) { + dataType := this.dataType + data := %dataType%().FromString(data) + } + + return data + } + + _getData(params, data := "") { + if (!data) { + if (params.Has("data") && params["data"]) { + data := params["data"] + } else if (this.definition["requestData"]) { + data := this.definition["requestData"] + } + } + + return data + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 74985cb7..ec745348 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -42,30 +42,54 @@ "definition_loader_parameter_key": "web_services.providers", "storage_type": "runtime" }, - "web_services.providers.launchpad_api": { - "name": "Launchpad API", - "EndpointUrl": "https://api.launchpad.games/v1", - "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", - "AuthenticationRefreshPath": "token", - "IconSrc": "logo", - "SupportsAuthentication": true, - "Authenticator": "jwt", - "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", - "LoginWindow": "LaunchpadLoginWindow" - }, - "web_services.services.api": { - "name": "Launchpad API", - "Provider": "launchpad_api" + "web_services.data_types.account_info": { + "name": "Account Info", + "description": "Account information related to an authentication session." + }, + "web_services.data_types.error_submission": { + "name": "Error Submission", + "description": "An endpoint to submit application errors to." + }, + "web_services.data_types.feedback_submission": { + "name": "Feedback Submission", + "description": "An endpoint to submit feedback to." + }, + "web_services.data_types.game_submission": { + "name": "Game Submission", + "description": "An endpoint to game launcher data to for sharing with the community." + }, + "web_services.data_types.release_info": { + "name": "Release Info", + "description": "Information about the latest (or any) version of Launchpad" + }, + "web_services.data_types.entity_list": { + "name": "Entity List", + "description": "A listing of entities from a web service." + }, + "web_services.data_types.entity_data": { + "name": "Entity Data", + "description": "Data to be imported into an entity within the application." + }, + "web_services.adapter_types.json": { + "class": "JsonWebServiceAdapter" + }, + "web_services.adapter_types.file": { + "class": "FileWebServiceAdapter" } }, "services": { + "cache.web_services": { + "class": "FileCache", + "arguments": ["@{App}", "@cache_state.web_services", "@@config.cache_dir", "WebServices"] + }, "cache_state.web_services": { "class": "CacheState", "arguments": ["@{App}", "@@config.cache_dir", "WebServices.json"] }, - "cache.web_services": { - "class": "FileCache", - "arguments": ["@{App}", "@cache_state.web_services", "@@config.cache_dir", "WebServices"] + "event_subscriber.web_services": { + "class": "WebServicesEventSubscriber", + "arguments": ["@{}"], + "tags": ["event_subscriber"] }, "state.web_services": { "class": "JsonState", @@ -79,10 +103,9 @@ "class": "JwtWebServiceAuthenticator", "arguments": ["@manager.gui"] }, - "event_subscriber.web_services": { - "class": "WebServicesEventSubscriber", - "arguments": ["@{}"], - "tags": ["event_subscriber"] + "web_services.adapter_factory": { + "class": "WebServiceAdapterFactory", + "arguments": ["@{}"] } } } From 65af969a3bfbbee094fe7ef27aa59874dc640866 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:30:51 -0500 Subject: [PATCH 063/138] Fix extra function in TabsControl --- Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk b/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk index 3b6a1e62..591a2a05 100644 --- a/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/TabsControl.ahk @@ -122,6 +122,5 @@ class TabsControl extends GuiControlBase { } this.guiObj.AutoXYWH("wh", [this.ctl.Name]) - this.ResizeColumns() } } From e33084f3924b3efef8f38ce24da09add409c083b Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:31:34 -0500 Subject: [PATCH 064/138] Fix include --- Lib/Shared/Includes.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 46f370e3..28258306 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -6,7 +6,7 @@ #Include Modules\WebServices\Event\WebServiceResponseEvent.ahk #Include Modules\WebServices\Events\WebServicesEvents.ahk #Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk -#Include Modules\WebServices\Factory\WebServiceDataAdapterFactory.ahk +#Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk #Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk #Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk #Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk From 44759b2eed8d3b2a27624d7f0774ffde7d000c15 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:32:07 -0500 Subject: [PATCH 065/138] Replace WebServiceLayerSource with WebServiceAdapterLayerSource to simplify the interface --- .../WebServiceAdapterLayerSource.ahk | 51 ++++++++++++++++ .../LayerSource/WebServiceLayerSource.ahk | 59 ------------------- 2 files changed, 51 insertions(+), 59 deletions(-) create mode 100644 Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk delete mode 100644 Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk diff --git a/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk new file mode 100644 index 00000000..c4dab738 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceAdapterLayerSource.ahk @@ -0,0 +1,51 @@ +class WebServiceAdapterLayerSource extends LayerSourceBase { + adapter := "" + params := "" + + __New(adapter, params := "") { + if (!params) { + params := Map() + } + + this.adapter := adapter + this.params := params + } + + SaveData(data := "") { + if (this.HasData()) { + if (this.adapter.definition["updateAllow"]) { + this.adapter.UpdateData(data, this.params) + } + } else if (this.adapter.definition["createAllow"]) { + this.adapter.CreateData(data, this.params) + } + + return this + } + + LoadData() { + data := "" + + if (this.adapter.definition["readAllow"]) { + data := this.adapter.ReadData(this.params) + } + + if (!data) { + data := Map() + } + + return data + } + + HasData() { + return this.adapter.DataExists(this.params) + } + + DeleteData() { + if (this.adapter.definitions["deleteAllow"]) { + this.adapter.DeleteData(this.params) + } + + return this + } +} diff --git a/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk b/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk deleted file mode 100644 index 37c4a943..00000000 --- a/Lib/Shared/Modules/WebServices/LayerSource/WebServiceLayerSource.ahk +++ /dev/null @@ -1,59 +0,0 @@ -class WebServiceLayerSource extends LayerSourceBase { - webService := "" - path := "" - useAuthentication := false - - ; A map in the format oldKey => newKey - fieldMap := Map() - - __New(webService, path, useAuthentication := false) { - this.webService := webService - this.path := path - this.useAuthentication := useAuthentication - } - - SaveData(data := "") { - ; Web services don't supoport saving layer data yet. - return this - } - - LoadData() { - request := this.webService.Request(this.path, "GET", "", this.useAuthentication, true) - response := request.Send() - responseBody := response.GetResponseBody() - - data := "" - - if (responseBody) { - data := JsonData().FromString(&responseBody) - } else { - data := Map() - } - - for oldKey, newKey in this.fieldMap { - if data.Has(oldKey) { - data[newKey] := data[oldKey] - data.Delete(oldKey) - } - } - - return data - } - - HasData() { - request := this.webService.Request(this.path, "HEAD", "", this.useAuthentication, false) - response := request.Send() - exists := response.GetHttpStatusCode() - - if (!exists) { - request.cacheObj.SetNotFound(this.path) - } - - return exists - } - - DeleteData() { - ; Web services don't support deleting layer data yet. - return this - } -} From 4f1bf74db5e376486c22cc2a26df98dc10d29b7e Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:32:29 -0500 Subject: [PATCH 066/138] Missing change from last commit --- Lib/Shared/Includes.ahk | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 28258306..56c04e44 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -9,6 +9,7 @@ #Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk #Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk #Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk +#Include Modules\WebServices\LayerSource\WebServiceAdapterLayerSource.ahk #Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk #Include Modules\WebServices\WebServiceAdapter\JsonWebServiceAdapter.ahk #Include Modules\WebServices\WebServiceAdapter\WebServiceAdapterBase.ahk From 0cb1beda79a19396dcfdde42143786b4081d97d4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 04:36:43 -0500 Subject: [PATCH 067/138] Remove the concept of a "local" data source for now --- Launchpad.services.json | 6 +----- Lib/Launchpad/DataSource/LocalDataSource.ahk | 2 -- Lib/Launchpad/Includes.ahk | 1 - 3 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 Lib/Launchpad/DataSource/LocalDataSource.ahk diff --git a/Launchpad.services.json b/Launchpad.services.json index 704774b4..7871e55d 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -11,7 +11,7 @@ "config.clean_launchers_on_build": false, "config.clean_launchers_on_exit": true, "config.create_desktop_shortcuts": true, - "config.data_source_key": "local", + "config.data_source_key": "launchpad_api", "config.default_launcher_theme": "", "config.destination_dir": "@@{data_dir}\\Launchers", "config.launcher_double_click_action": "Edit", @@ -110,10 +110,6 @@ "class": "LaunchpadConfig", "arguments": ["@config_storage.app_config", "@{}", "@@config_key"] }, - "data_source.local": { - "class": "LocalDataSource", - "arguments": ["@manager.cache", "local"] - }, "ini_migrator": { "class": "LaunchpadIniMigrator", "arguments": ["@{App}", "@manager.gui"] diff --git a/Lib/Launchpad/DataSource/LocalDataSource.ahk b/Lib/Launchpad/DataSource/LocalDataSource.ahk deleted file mode 100644 index 7ac01421..00000000 --- a/Lib/Launchpad/DataSource/LocalDataSource.ahk +++ /dev/null @@ -1,2 +0,0 @@ -class LocalDataSource extends DataSourceBase { -} diff --git a/Lib/Launchpad/Includes.ahk b/Lib/Launchpad/Includes.ahk index cfacabc5..775a4966 100644 --- a/Lib/Launchpad/Includes.ahk +++ b/Lib/Launchpad/Includes.ahk @@ -21,7 +21,6 @@ #Include Config\LaunchpadConfig.ahk #Include Config\PlatformsConfig.ahk #Include ConfigMigrator\LaunchpadIniMigrator.ahk -#Include DataSource\LocalDataSource.ahk #Include DetectedGame\DetectedGame.ahk #Include Entity\LauncherEntity.ahk #Include Entity\ManagedEntityBase.ahk From a6f746c0b76d6c82f3bb27d862bbd4695d1891be Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 14:27:40 -0500 Subject: [PATCH 068/138] Remove old api_endpoint config value --- Launchpad.services.json | 1 - Lib/Launchpad/Gui/Form/SettingsWindow.ahk | 12 ------- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 1 - .../LaunchpadApi/DataSource/ApiDataSource.ahk | 35 ------------------- .../LaunchpadApi/LaunchpadApi.module.json | 3 +- .../Volantis.App/Gui/Form/FeedbackWindow.ahk | 2 -- 6 files changed, 1 insertion(+), 53 deletions(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index 7871e55d..4323ef7f 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -2,7 +2,6 @@ "parameters": { "backups_config": {}, "config.api_authentication": false, - "config.api_endpoint": "", "config.assets_dir": "@@{data_dir}\\Launcher Assets", "config.auto_backup_config_files": true, "config.backups_to_keep": 5, diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk index 5ffa8e5c..dd9e9941 100644 --- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk +++ b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk @@ -122,8 +122,6 @@ ctl := this.guiObj.AddDDL("vlogging_level xs y+m Choose" . chosen . " w200 c" . this.themeObj.GetColor("editText"), this.container.Get("logger").GetLogLevels()) ctl.OnEvent("Change", "OnLoggingLevelChange") - this.AddConfigLocationBlock("API Endpoint", "api_endpoint") - this.AddHeading("API Settings") ctl := this.AddConfigCheckBox("Enable API login for enhanced functionality", "api_authentication") ctl.ctl.NeedsRestart := true @@ -244,16 +242,6 @@ } } - OnApiEndpointMenuClick(btn) { - if (btn == "ChangeApiEndpoint") { - this.app.Service("manager.data_source").GetDefaultDataSource().ChangeApiEndpoint("", "") - this.SetText("ApiEndpoint", this.app.Config["api_endpoint"], "Bold") - this.needsRestart := true - } else if (btn == "OpenApiEndpoint") { - this.app.Service("manager.data_source").GetDefaultDataSource().Open() - } - } - OnCacheDirMenuClick(btn) { if (btn == "ChangeCacheDir") { this.app.Service("manager.cache").ChangeCacheDir() diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 95b11baa..36a1ec84 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -2,7 +2,6 @@ class LaunchpadBuilder extends AppBase { GetParameterDefinitions(config) { parameters := super.GetParameterDefinitions(config) parameters["config_path"] := this.appDir . "\Launchpad.build.json" - parameters["config.api_endpoint"] := "https://api.launchpad.games/v1" parameters["config.data_source_key"] := "" parameters["config.api_authentication"] := true parameters["config.dist_dir"] := this.appDir . "\Dist" diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk index 83747962..5e14fd36 100644 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk @@ -118,39 +118,4 @@ class ApiDataSource extends DataSourceBase { Open() { Run(this.endpointUrl) } - - ChangeApiEndpoint(existingEndpoint := "", owner := "", parent := "") { - if (existingEndpoint == "") { - existingEndpoint := this.app.Config["api_endpoint"] - } - - ownerOrParent := "" - - if (parent) { - ownerOrParent := parent - } else if (owner) { - ownerOrParent := owner - } - - apiEndpointUrl := this.app.Service("manager.gui").Dialog(Map( - "type", "SingleInputBox", - "title", "API Endpoint URL", - "text", "Enter the base URL of the API endpoint you would like Launchpad to connect to. Leave blank to revert to the default.", - "defaultValue", existingEndpoint, - "ownerOrParent", ownerOrParent, - "child", !!(parent) - )) - - if (apiEndpointUrl != existingEndpoint) { - this.app.Config["api_endpoint"] := apiEndpointUrl - apiEndpointUrl := this.app.Config["api_endpoint"] - - if (apiEndpointUrl != existingEndpoint) { - this.endpointUrl := apiEndpointUrl - this.cache.FlushCache() - } - } - - return apiEndpointUrl - } } diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index 81f1c228..ba9386f9 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -16,7 +16,6 @@ }, "parameters": { "config.data_source_key": "launchpad_api", - "config.api_endpoint": "https://api.launchpad.games/v1", "config.api_authentication": true, "web_services.providers.launchpad_api": { "name": "Launchpad API", @@ -115,7 +114,7 @@ "services": { "data_source.launchpad_api": { "class": "ApiDataSource", - "arguments": ["@{App}", "@manager.cache", "launchpad_api", "@@config.api_endpoint"] + "arguments": ["@{App}", "@manager.cache", "launchpad_api", "https://api.launchpad.games/v1"] }, "cache_state.launchpad_api": { "class": "CacheState", diff --git a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk b/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk index ba27dc1c..bcf5b290 100644 --- a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk @@ -1,11 +1,9 @@ class FeedbackWindow extends DialogBox { errorObj := "" notifierObj := "" - apiEndpointUrl := "" __New(container, themeObj, config) { this.notifierObj := container.Get("notifier").notifierObj - this.apiEndpointUrl := container.Get("config.app")["api_endpoint"] super.__New(container, themeObj, config) } From d4530c33ef6d2e842e59165cb40947eeb383e171 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 14:28:26 -0500 Subject: [PATCH 069/138] Allow generically making requests to web service adapters based on filtering --- .../WebServices/Entity/WebServiceEntity.ahk | 32 ++++++++++++++++++- .../WebServiceAdapterBase.ahk | 32 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 530c0603..dc6e135e 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -48,7 +48,33 @@ class WebServiceEntity extends AppEntityBase { ) } - GetAdapters(filters := "") { + AdapterRequest(params, adapterFilters, operation := "read", limit := false) { + if (!adapterFilters) { + adapterFilters := Map() + } + + if (!adapterFilters) { + adapterFilters := Map() + } + + if (Type(adapterFilters) == "String") { + adapterFilters := Map("adapterType", adapterFilters) + } + + results := Map() + + for adapterKey, adapter in this.GetAdapters(adapterFilters, operation) { + results[adapterKey] := adapter.SendRequest(operation, params) + + if (limit && results.Count >= limit) { + break + } + } + + return results + } + + GetAdapters(filters := "", operation := "") { if (!filters) { filters := Map() } @@ -70,6 +96,10 @@ class WebServiceEntity extends AppEntityBase { } } + if (include && operation) { + include := adapter.SupportsOperation(operation) + } + if (include) { adapters[key] := adapter } diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk index a7b3e7d6..f7002611 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -4,6 +4,7 @@ class WebServiceAdapterBase { definition := "" dataType := "" merger := "" + operationTypes := ["create", "read", "update", "delete"] __New(container, merger, webService, definition) { this.container := container @@ -56,6 +57,37 @@ class WebServiceAdapterBase { ) } + SupportsOperation(operation) { + supported := false + + if (this.operationTypes.Contains(operation)) { + supported := this.definition[operation + "Allow"] + } + + return supported + } + + SendRequest(operation, params := "") { + if (!this.SupportsOperation(operation)) { + throw AppException("The '" . operation . "' operation is not supported by this data adapter.") + } + + result := "" + data := params.Has("data") ? params["data"] : "" + + if (operation == "create") { + result := this.CreateData(data, params) + } else if (operation == "read") { + result := this.ReadData(params) + } else if (operation == "update") { + result := this.UpdateData(data, params) + } else if (operation == "delete") { + result := this.DeleteData(params) + } + + return result + } + CreateData(data, params := "") { if (!this.definition["createAllow"]) { throw AppException("The 'create' operation is not allowed on this data adapter.") From 744bd9b4e7f4e372b8a0c89f12bbb2eac946eac4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 17:27:41 -0500 Subject: [PATCH 070/138] Move main menu definitions to the main app class, and use a series of events to allow other code to populate it. Additionally, refactor a number of attributes to be parameters and simplify calls to services and parameters --- Launchpad.ahk | 1 - Launchpad.services.json | 9 + Lib/Launchpad/App/Launchpad.ahk | 90 +++-- .../Builder/BuildFile/CopyableBuildFile.ahk | 2 +- .../Builder/BuildFile/GameAhkFile.ahk | 2 +- Lib/Launchpad/Builder/BuildFile/IconFile.ahk | 4 +- .../LauncherBuilderOpBase.ahk | 2 +- .../LauncherGameOp/LauncherGameOpBase.ahk | 6 +- .../BulkOperation/LoadOp/LoadEntitiesOp.ahk | 4 +- Lib/Launchpad/Entity/LauncherEntity.ahk | 4 +- Lib/Launchpad/Entity/ManagedEntityBase.ahk | 2 +- .../GamePlatform/GamePlatformBase.ahk | 4 +- Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk | 2 +- Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk | 2 +- Lib/Launchpad/Gui/Form/LauncherWizard.ahk | 2 +- Lib/Launchpad/Gui/Form/SettingsWindow.ahk | 26 +- Lib/Launchpad/Gui/Form/SetupWindow.ahk | 10 +- .../Gui/ManageWindow/DetectedGamesWindow.ahk | 4 +- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 102 +----- .../Gui/ManageWindow/ManageBackupsWindow.ahk | 2 +- .../Gui/ManageWindow/ManageModulesWindow.ahk | 4 +- .../Gui/ManageWindow/PlatformsWindow.ahk | 2 +- .../GamePlatform/BlizzardPlatform.ahk | 2 +- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 8 +- .../AppBuilder/ChocoPkgBuilder.ahk | 2 +- .../BuildDeployer/ApiBuildDeployer.ahk | 2 +- .../App/LaunchpadLauncher.ahk | 6 +- .../Condition/SteamConditionBase.ahk | 2 +- Lib/LaunchpadLauncher/Game/BlizzardGame.ahk | 6 +- Lib/LaunchpadLauncher/Game/GameBase.ahk | 10 +- Lib/LaunchpadLauncher/Game/RiotGame.ahk | 2 +- Lib/Shared/Includes.ahk | 4 +- .../LaunchpadApi/DataSource/ApiDataSource.ahk | 6 +- .../WebServices/Entity/WebServiceEntity.ahk | 2 +- .../WebServicesEventSubscriber.ahk | 22 ++ .../WebServices}/Gui/Form/FeedbackWindow.ahk | 38 +- Lib/Shared/Volantis.App/App/AppBase.ahk | 332 +++++++++++++++--- Lib/Shared/Volantis.App/App/TestAppBase.ahk | 4 +- .../BulkOperation/BulkOperationBase.ahk | 8 +- .../BulkOperation/InstallOp/InstallOp.ahk | 2 +- .../Volantis.App/Entity/AppEntityBase.ahk | 4 +- .../Volantis.App/Entity/BackupEntity.ahk | 2 +- .../Volantis.App/Event/MenuItemsEvent.ahk | 16 + .../Volantis.App/Event/MenuResultEvent.ahk | 20 ++ Lib/Shared/Volantis.App/Events/Events.ahk | 8 + .../Gui/EntityEditor/EntityEditorBase.ahk | 2 +- Lib/Shared/Volantis.App/Gui/GuiBase.ahk | 20 +- .../Gui/ManageWindow/ManageEntitiesWindow.ahk | 2 +- Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk | 2 +- .../Volantis.App/GuiControl/LocationBlock.ahk | 2 +- Lib/Shared/Volantis.App/State/StateBase.ahk | 2 +- .../Volantis.Entity/Entity/EntityBase.ahk | 2 +- Lib/TestLib/Test/AppTestBase.ahk | 2 +- Scripts/Build.ahk | 1 - 54 files changed, 545 insertions(+), 284 deletions(-) rename Lib/Shared/{Volantis.App => Modules/WebServices}/Gui/Form/FeedbackWindow.ahk (61%) create mode 100644 Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk create mode 100644 Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk diff --git a/Launchpad.ahk b/Launchpad.ahk index 6e5d905f..d34cd5e1 100644 --- a/Launchpad.ahk +++ b/Launchpad.ahk @@ -13,7 +13,6 @@ appVersion := "{{VERSION}}" Launchpad(Map( "appName", "Launchpad", - "developer", "Volantis Development", "version", appVersion, "trayIcon", "Resources\Graphics\launchpad.ico", "console", true diff --git a/Launchpad.services.json b/Launchpad.services.json index 4323ef7f..f7c00607 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -1,5 +1,14 @@ { "parameters": { + "app.website_url": "https://launchpad.games", + "app.custom_tray_menu": true, + "app.developer": "Volantis Development", + "app.has_settings": true, + "app.settings_window": "SettingsWindow", + "app.supports_update_check": true, + "app.show_about_menu_item": true, + "app.about_window": "AboutWindow", + "app.show_website_menu_item": true, "backups_config": {}, "config.api_authentication": false, "config.assets_dir": "@@{data_dir}\\Launcher Assets", diff --git a/Lib/Launchpad/App/Launchpad.ahk b/Lib/Launchpad/App/Launchpad.ahk index b752ac6e..661c64f3 100644 --- a/Lib/Launchpad/App/Launchpad.ahk +++ b/Lib/Launchpad/App/Launchpad.ahk @@ -1,28 +1,26 @@ class Launchpad extends AppBase { - customTrayMenu := true detectGames := false - isSetup := false CheckForUpdates(notify := true) { updateAvailable := false - if (this.Version != "{{VERSION}}" && this.Service("manager.data_source").GetDefaultDataSource()) { - dataSource := this.Service("manager.data_source").GetDefaultDataSource() + if (this.Version != "{{VERSION}}" && this["manager.data_source"].GetDefaultDataSource()) { + dataSource := this["manager.data_source"].GetDefaultDataSource() releaseInfoStr := dataSource.ReadItem("release-info") if (releaseInfoStr) { data := JsonData() releaseInfo := data.FromString(&releaseInfoStr) - if (releaseInfo && releaseInfo["data"].Has("version") && releaseInfo["data"]["version"] && this.Service("version_checker").VersionIsOutdated(releaseInfo["data"]["version"], this.Version)) { + if (releaseInfo && releaseInfo["data"].Has("version") && releaseInfo["data"]["version"] && this["version_checker"].VersionIsOutdated(releaseInfo["data"]["version"], this.Version)) { updateAvailable := true - this.Service("manager.gui").Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) + this["manager.gui"].Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) } } } if (!updateAvailable && notify) { - this.Service("notifier").Info("You're running the latest version of Launchpad. Shiny!") + this["notifier"].Info("You're running the latest version of Launchpad. Shiny!") } } @@ -32,7 +30,7 @@ } InitializeApp(config) { - eventMgr := this.Service("manager.event") + eventMgr := this["manager.event"] eventMgr.Register(EntityEvents.ENTITY_CREATED, "LaunchpadEntityCreated", ObjBindMethod(this, "OnEntityCreated")) eventMgr.Register(EntityEvents.ENTITY_UPDATED, "LaunchpadEntityUpdated", ObjBindMethod(this, "OnEntityUpdated")) eventMgr.Register(EntityEvents.ENTITY_DELETED, "LaunchpadEntityDeleted", ObjBindMethod(this, "OnEntityDeleted")) @@ -112,28 +110,28 @@ super.RunApp(config) - this.Service("entity_manager.platform").LoadComponents() - this.Service("entity_manager.launcher").LoadComponents() - this.Service("entity_manager.backup").LoadComponents() + this["entity_manager.platform"].LoadComponents() + this["entity_manager.launcher"].LoadComponents() + this["entity_manager.backup"].LoadComponents() this.OpenApp() if (this.detectGames) { - this.Service("entity_manager.platform").DetectGames() + this["entity_manager.platform"].DetectGames() } } MigrateConfiguration() { - configFile := this.Parameter("previous_config_file") + configFile := this.Parameter["previous_config_file"] if (configFile && FileExist(configFile)) { - response := this.Service("manager.gui").Dialog(Map( + response := this["manager.gui"].Dialog(Map( "title", "Migrate settings?", "text", this.appName . " uses a new configuration file format, and has detected that you have a previous configuration file.`n`nWould you like to automatically migrate your settings?`n`nChoose Yes to migrate your previous configuration. Choose no to simply delete it and start from scratch." )) if (response == "Yes") { - this.Service("ini_migrator").Migrate(configFile, this.Config) + this["ini_migrator"].Migrate(configFile, this.Config) } else { FileDelete(configFile) } @@ -141,7 +139,7 @@ } InitialSetup(config) { - result := this.Service("manager.gui").Dialog(Map("type", "SetupWindow")) + result := this["manager.gui"].Dialog(Map("type", "SetupWindow")) if (result == "Exit") { this.ExitApp() @@ -149,19 +147,18 @@ this.detectGames := true } - this.isSetup := true super.InitialSetup(config) } UpdateStatusIndicators() { - if (this.Service("manager.gui").Has("MainWindow")) { + if (this["manager.gui"].Has("MainWindow")) { serviceMgr := this.container["entity_manager.web_service"] webServices := serviceMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) .Condition(IsTrueCondition(), "Enabled") .Condition(IsTrueCondition(), "StatusIndicator") .Execute() - windowObj := this.Service("manager.gui")["MainWindow"] + windowObj := this["manager.gui"]["MainWindow"] for serviceId, webService in webServices { windowObj.UpdateStatusIndicator(webService) @@ -171,33 +168,66 @@ ExitApp() { if (this.isSetup && this.Config["clean_launchers_on_exit"]) { - this.Service("manager.builder").CleanLaunchers() + this["manager.builder"].CleanLaunchers() } if (this.isSetup && this.Config["flush_cache_on_exit"]) { - this.Service("manager.cache").FlushCaches(false, false) + this["manager.cache"].FlushCaches(false, false) } super.ExitApp() } - OpenWebsite() { - Run("https://launchpad.games") + RestartApp() { + if (this.Services.Has("manager.gui")) { + guiMgr := this["manager.gui"] + + if (guiMgr.Has("MainWindow")) { + guiMgr.StoreWindowState(this["manager.gui"]["MainWindow"]) + } + } + + super.RestartApp() } - ProvideFeedback() { - this.Service("manager.gui").Dialog(Map("type", "FeedbackWindow")) + AddMainMenuEarlyItems(menuItems, showOpenItem := false) { + menuItems := super.AddMainMenuEarlyItems(menuItems, showOpenItem) + + launchersItems := [] + launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers")) + launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers")) + + menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems)) + + return menuItems } - RestartApp() { - if (this.Services.Has("manager.gui")) { - guiMgr := this.Service("manager.gui") + HandleMainMenuClick(result) { + if (result == "CleanLaunchers") { + this["manager.builder"].CleanLaunchers() + } else if (result == "ReloadLaunchers") { + this["entity_manager.launcher"].LoadComponents(true) + guiMgr := this["manager.gui"] if (guiMgr.Has("MainWindow")) { - guiMgr.StoreWindowState(this.Service("manager.gui")["MainWindow"]) + guiMgr["MainWindow"].UpdateListView() } + } else if (result == "ManageModules") { + this["manager.gui"].OpenWindow("ManageModulesWindow") + } else if (result == "FlushCache") { + this["manager.cache"].FlushCaches(true, true) + } else { + super.HandleMainMenuClick(result) } - super.RestartApp() + return result + } + + GetToolsMenuItems() { + toolsItems := super.GetToolsMenuItems() + toolsItems.Push(Map("label", "&Modules", "name", "ManageModules")) + toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache")) + + return toolsItems } } diff --git a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk b/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk index 2bd1146e..aaa77f98 100644 --- a/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk +++ b/Lib/Launchpad/Builder/BuildFile/CopyableBuildFile.ahk @@ -56,7 +56,7 @@ class CopyableBuildFile extends BuildFileBase { filePath := FileSelect(1,, this.launcherEntityObj.Id . ": " . this.RequestMessage, this.SelectFilter) if (filePath == "") { - this.app.Service("notifier").Warning("No file selected. Skipping build file.") + this.app["notifier"].Warning("No file selected. Skipping build file.") } return filePath diff --git a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk index ed0eaedc..230f51e6 100644 --- a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk +++ b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk @@ -35,7 +35,7 @@ class GameAhkFile extends ComposableBuildFile { GetPlatforms() { platforms := Map() - for key, platform in this.app.Service("entity_manager.platform").GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) { + for key, platform in this.app["entity_manager.platform"].GetActivePlatforms(EntityQuery.RESULT_TYPE_ENTITIES) { platforms[key] := platform.FieldData } diff --git a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk b/Lib/Launchpad/Builder/BuildFile/IconFile.ahk index 333333a4..729d4c01 100644 --- a/Lib/Launchpad/Builder/BuildFile/IconFile.ahk +++ b/Lib/Launchpad/Builder/BuildFile/IconFile.ahk @@ -53,7 +53,7 @@ class IconFile extends CopyableBuildFile { } if (iconsCount == 0) { - this.app.Service("notifier").Warning("No icons could be extracted from %exeFile%. Please try another file.") + this.app["notifier"].Warning("No icons could be extracted from %exeFile%. Please try another file.") iconFilePath := "" this.Cleanup() } else { @@ -61,7 +61,7 @@ class IconFile extends CopyableBuildFile { iconFilePath := FileSelect(, iconsDir, "Select the correct icon from the extracted files", "Icons (*.ico)") if (iconFilePath == "") { - this.app.Service("notifier").Warning("Canceled icon selection. Please try again.") + this.app["notifier"].Warning("Canceled icon selection. Please try again.") this.Cleanup() } } diff --git a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk b/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk index f1194602..f9f976b3 100644 --- a/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk +++ b/Lib/Launchpad/BulkOperation/LauncherBuilderOp/LauncherBuilderOpBase.ahk @@ -11,7 +11,7 @@ class LauncherBuilderOpBase extends LauncherGameOpBase { } if (Type(builder) == "String" && builder != "") { - builder := app.Service("manager.builder")[builder] + builder := app["manager.builder"][builder] } InvalidParameterException.CheckTypes("LauncherBuilderOpBase", "builder", builder, "BuilderBase") diff --git a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk b/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk index 558ee665..f9193b3d 100644 --- a/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk +++ b/Lib/Launchpad/BulkOperation/LauncherGameOp/LauncherGameOpBase.ahk @@ -14,7 +14,7 @@ class LauncherGameOpBase extends BulkOperationBase { itemFailedText := "Failed." __New(app, launcherEntities := "", owner := "") { - this.launcherManager := app.Service("entity_manager.launcher") + this.launcherManager := app["entity_manager.launcher"] if (launcherEntities == "") { launcherEntities := this.launcherManager.All() @@ -63,12 +63,12 @@ class LauncherGameOpBase extends BulkOperationBase { VerifyRequirements() { if (this.app.Config["destination_dir"] == "") { - this.app.Service("notifier").Error("Launcher directory is not set.") + this.app["notifier"].Error("Launcher directory is not set.") return false } if (this.app.Config["assets_dir"] == "") { - this.app.Service("notifier").Error("Assets directory is not set.") + this.app["notifier"].Error("Assets directory is not set.") return false } diff --git a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk b/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk index fab21b04..c51002ac 100644 --- a/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk +++ b/Lib/Launchpad/BulkOperation/LoadOp/LoadEntitiesOp.ahk @@ -10,7 +10,7 @@ class LoadEntitiesOp extends BulkOperationBase { failedMessage := "{n} launcher(s) could not be loaded due to errors." __New(app, launcherConfigObj := "", owner := "") { - this.launcherManager := app.Service("entity_manager.launcher") + this.launcherManager := app["entity_manager.launcher"] if (launcherConfigObj == "") { launcherConfigObj := this.launcherManager.GetConfig() @@ -29,7 +29,7 @@ class LoadEntitiesOp extends BulkOperationBase { } ; @todo replace this since EntityFactory is no longer used - factory := this.app.Service("EntityFactory") + factory := this.app["EntityFactory"] for key, config in this.launcherConfigObj { this.StartItem(key, key) diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index 1122a0ac..9477392c 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -320,7 +320,7 @@ class LauncherEntity extends AppEntityBase { if (filePath && FileExist(filePath)) { launcherVersion := FileGetVersion(this.GetLauncherFile(this.Id)) - if (launcherVersion && !this.app.Service("version_checker").VersionIsOutdated(this.app.Version, launcherVersion)) { + if (launcherVersion && !this.app["version_checker"].VersionIsOutdated(this.app.Version, launcherVersion)) { outdated := false } @@ -330,7 +330,7 @@ class LauncherEntity extends AppEntityBase { if (!buildInfo["Version"] || !buildInfo["Timestamp"]) { outdated := true } else { - if (configInfo["Version"] && this.app.Service("version_checker").VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) { + if (configInfo["Version"] && this.app["version_checker"].VersionIsOutdated(configInfo["Version"], buildInfo["Version"])) { outdated := true } else if (configInfo["Timestamp"] && DateDiff(configInfo["Timestamp"], buildInfo["Timestamp"], "S") > 0) { outdated := true diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/Lib/Launchpad/Entity/ManagedEntityBase.ahk index 976c6489..99926db4 100644 --- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk +++ b/Lib/Launchpad/Entity/ManagedEntityBase.ahk @@ -552,7 +552,7 @@ class ManagedEntityBase extends AppEntityBase { productCode := this.GetBlizzardProductKey() if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) { - path := this.app.Service("BlizzardProductDb").GetProductInstallPath(productCode) + path := this.app["BlizzardProductDb"].GetProductInstallPath(productCode) } return path diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk index d5b3f944..f9d674ef 100644 --- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk +++ b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk @@ -87,7 +87,7 @@ class GamePlatformBase { } NeedsUpdate() { - return this.app.Service("version_checker").VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion()) + return this.app["version_checker"].VersionIsOutdated(this.GetLatestVersion(), this.GetInstalledVersion()) } GetInstalledVersion() { @@ -179,7 +179,7 @@ class GamePlatformBase { } DetermineMainExe(key, possibleExes) { - dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() + dataSource := this.app["manager.data_source"].GetDefaultDataSource() dsData := this.GetDataSourceDefaults(dataSource, key) mainExe := "" diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 04119844..3513534a 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -22,7 +22,7 @@ Create() { super.Create() - this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() + this.dataSource := this.app["manager.data_source"].GetDefaultDataSource() this.knownPlatforms := this.dataSource.ReadListing("platforms") this.knownGames := this.dataSource.ReadListing("game-keys") this.launcherTypes := this.dataSource.ReadListing("launcher-types") diff --git a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk b/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk index 82c7b008..de956383 100644 --- a/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk +++ b/Lib/Launchpad/Gui/Form/ImportShortcutForm.ahk @@ -29,7 +29,7 @@ GetLauncherConfig() { platformKey := Trim(this.guiObj["Platform"].Text) config := Map("Platform", platformKey) - platform := this.app.Service("entity_manager.platform")[platformKey] + platform := this.app["entity_manager.platform"][platformKey] if (platform) { config["LauncherType"] := platform.Platform.launcherType diff --git a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk b/Lib/Launchpad/Gui/Form/LauncherWizard.ahk index 1f1889b7..97bcd11a 100644 --- a/Lib/Launchpad/Gui/Form/LauncherWizard.ahk +++ b/Lib/Launchpad/Gui/Form/LauncherWizard.ahk @@ -25,7 +25,7 @@ GetLauncherConfig() { platformKey := Trim(this.guiObj["Platform"].Text) config := Map("Platform", platformKey, "GameInstallDir", this.installDir, "GameExe", this.exe) - platform := this.app.Service("entity_manager.platform")[platformKey] + platform := this.app["entity_manager.platform"][platformKey] if (platform) { config["LauncherType"] := platform.Platform.launcherType diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk index dd9e9941..605f639f 100644 --- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk +++ b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk @@ -133,11 +133,11 @@ } OnManageBackups(btn, info) { - this.app.Service("entity_type.backup").OpenManageWindow() + this.app["entity_type.backup"].OpenManageWindow() } OnManagePlatforms(btn, info) { - this.app.Service("entity_type.platform").OpenManageWindow() + this.app["entity_type.platform"].OpenManageWindow() } AddConfigLocationBlock(heading, settingName, extraButton := "", helpText := "") { @@ -190,7 +190,7 @@ } else if (btn == "OpenLauncherFile") { this.app.Config.OpenLauncherFile() } else if (btn == "ReloadLauncherFile") { - this.app.Service("entity_manager.launcher").LoadComponents(true) + this.app["entity_manager.launcher"].LoadComponents(true) } } @@ -201,7 +201,7 @@ } else if (btn == "OpenBackupsFile") { this.app.Config.OpenBackupsFile() } else if (btn == "ReloadBackupsFile") { - this.app.Service("entity_manager.backup").LoadComponents(true) + this.app["entity_manager.backup"].LoadComponents(true) } } @@ -212,7 +212,7 @@ } else if (btn == "OpenPlatformsFile") { this.app.Config.OpenPlatformsFile() } else if (btn == "ReloadPlatformsFile") { - this.app.Service("entity_manager.platform").LoadComponents(true) + this.app["entity_manager.platform"].LoadComponents(true) } } @@ -244,22 +244,22 @@ OnCacheDirMenuClick(btn) { if (btn == "ChangeCacheDir") { - this.app.Service("manager.cache").ChangeCacheDir() + this.app["manager.cache"].ChangeCacheDir() this.SetText("CacheDir", this.app.Config["cache_dir"], "Bold") } else if (btn == "OpenCacheDir") { - this.app.Service("manager.cache").OpenCacheDir() + this.app["manager.cache"].OpenCacheDir() } else if (btn == "FlushCacheDir") { - this.app.Service("manager.cache").FlushCaches(true, true) + this.app["manager.cache"].FlushCaches(true, true) } } OnBackupDirMenuClick(btn) { if (btn == "ChangeBackupDir") { - this.app.Service("entity_manager.backup").ChangeBackupDir() + this.app["entity_manager.backup"].ChangeBackupDir() this.SetText("BackupDir", this.app.Config["backup_dir"], "Bold") this.needsRestart := true } else if (btn == "OpenBackupDir") { - this.app.Service("entity_manager.backup").OpenBackupDir() + this.app["entity_manager.backup"].OpenBackupDir() } } @@ -304,7 +304,7 @@ this.app.Config.SaveConfig() if (this.needsRestart) { - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Restart " . this.app.appName . "?", "text", "One or more settings that have been changed require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?" )) @@ -314,8 +314,8 @@ } } - if (this.app.Service("manager.gui").Has("MainWindow")) { - this.app.Service("manager.gui")["MainWindow"].UpdateListView() + if (this.app["manager.gui"].Has("MainWindow")) { + this.app["manager.gui"]["MainWindow"].UpdateListView() } return result diff --git a/Lib/Launchpad/Gui/Form/SetupWindow.ahk b/Lib/Launchpad/Gui/Form/SetupWindow.ahk index 1ebd5784..0be2ca76 100644 --- a/Lib/Launchpad/Gui/Form/SetupWindow.ahk +++ b/Lib/Launchpad/Gui/Form/SetupWindow.ahk @@ -16,7 +16,7 @@ Create() { super.Create() - this.availableThemes := this.app.Service("manager.theme").GetAvailableThemes() + this.availableThemes := this.app["manager.theme"].GetAvailableThemes() } AddDescription(text) { @@ -58,7 +58,7 @@ } GetInstalledPlatforms() { - platformMgr := this.app.Service("entity_manager.platform") + platformMgr := this.app["entity_manager.platform"] platformQuery := platformMgr.EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) .Condition(IsTrueCondition(), "IsInstalled") return platformQuery.Execute() @@ -80,7 +80,7 @@ this.guiObj.Submit(false) len := StrLen("PlatformToggle") name := SubStr(chk.Name, len + 1) - platformMgr := this.app.Service("entity_manager.platform") + platformMgr := this.app["entity_manager.platform"] if (platformMgr.Has(name)) { platform := platformMgr[name] @@ -115,8 +115,8 @@ ProcessResult(result, submittedData := "") { if (result == "Start") { - this.app.Service("config.app").SaveConfig() - this.app.Service("entity_manager.platform").SaveModifiedEntities() + this.app["config.app"].SaveConfig() + this.app["entity_manager.platform"].SaveModifiedEntities() if (submittedData.DetectGames) { result := "Detect" diff --git a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk index 5d9aed4f..98f10890 100644 --- a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk @@ -200,7 +200,7 @@ detectedGameObj := this.detectedGames[key] - result := this.app.Service("manager.gui").Dialog(Map( + result := this.app["manager.gui"].Dialog(Map( "type", "DetectedGameEditor", "ownerOrParent", this.guiId, "child", true @@ -233,7 +233,7 @@ menuItems := [] menuItems.Push(Map("label", "Edit", "name", "EditDetectedGame")) - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditDetectedGame") { this.EditDetectedGame(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index d5f1b6bc..81221664 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -91,104 +91,12 @@ } } - _getToolsMenuEntityTypes() { - entityTypes := Map() - - for key, entityType in this.container["manager.entity_type"] { - if (entityType.definition["manager_link_in_tools_menu"]) { - entityTypes[key] := entityType - } - } - - return entityTypes - } - ShowTitleMenu() { - menuEntityTypes := this._getToolsMenuEntityTypes() - toolsItems := [] - - for key, entityType in menuEntityTypes { - menuLinkText := entityType.definition["manager_menu_link_text"] - - if (!menuLinkText) { - menuLinkText := "&" . entityType.definition["name_plural"] - } - - toolsItems.Push(Map("label", menuLinkText, "name", "manage_" . key)) - } - - toolsItems.Push(Map("label", "&Modules", "name", "ManageModules")) - toolsItems.Push(Map("label", "&Flush Cache", "name", "FlushCache")) - - launchersItems := [] - launchersItems.Push(Map("label", "&Clean Launchers", "name", "CleanLaunchers")) - launchersItems.Push(Map("label", "&Reload Launchers", "name", "ReloadLaunchers")) - - aboutItems := [] - aboutItems.Push(Map("label", "&About Launchpad", "name", "About")) - aboutItems.Push(Map("label", "&Open Website", "name", "OpenWebsite")) - - menuItems := [] - menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems)) - menuItems.Push(Map("label", "&Launchers", "name", "LaunchersMenu", "childItems", launchersItems)) - menuItems.Push("") - menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems)) - menuItems.Push("") - menuItems.Push(Map("label", "&Settings", "name", "Settings")) - menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates")) - menuItems.Push("") - menuItems.Push(Map("label", "Provide &Feedback", "name", "ProvideFeedback")) - menuItems.Push("") - menuItems.Push(Map("label", "&Restart", "name", "Reload")) - menuItems.Push(Map("label", "E&xit", "name", "Exit")) - - result := this.container["manager.gui"].Menu(menuItems, this, this.guiObj["WindowTitleText"]) - - if (result == "ManageModules") { - this.container["manager.gui"].OpenWindow("ManageModulesWindow") - } else if (result == "FlushCache") { - this.container["manager.cache"].FlushCaches(true, true) - } else if (result == "CleanLaunchers") { - this.container["manager.builder"].CleanLaunchers() - } else if (result == "ReloadLaunchers") { - this.launcherManager.LoadComponents(true) - this.UpdateListView() - } else if (result == "About") { - this.container["manager.gui"].Dialog(Map("type", "AboutWindow")) - } else if (result == "OpenWebsite") { - this.app.OpenWebsite() - } else if (result == "ProvideFeedback") { - this.app.ProvideFeedback() - } else if (result == "Settings") { - this.container["manager.gui"].Dialog(Map("type", "SettingsWindow", "unique", false)) - } else if (result == "CheckForUpdates") { - this.app.CheckForUpdates() - } else if (result == "Reload") { - this.app.restartApp() - } else if (result == "Exit") { - this.app.ExitApp() - } else { - for key, entityType in menuEntityTypes { - if (result == "manage_" . key) { - this.container["entity_type." . key].OpenManageWindow() - break - } - } - } - } - - _getApiWebService() { - webService := "" - - if (this.app.Services.Has("entity_manager.web_service")) { - entityMgr := this.app.Services["entity_manager.web_service"] - - if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { - webService := entityMgr["launchpad_api"] - } - } - - return webService + this.app.MainMenu( + this, + this.guiObj["WindowTitleText"], + false + ) } FormatDate(timestamp) { diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk index 3ceb36f4..be595c3e 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/ManageBackupsWindow.ahk @@ -109,7 +109,7 @@ class ManageBackupsWindow extends ManageWindowBase { menuItems.Push(Map("label", "Restore", "name", "RestoreBackup")) menuItems.Push(Map("label", "Delete", "name", "DeleteBackup")) - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditBackup") { this.EditBackup(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk index ec0e10b3..7597c34b 100644 --- a/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/ManageModulesWindow.ahk @@ -84,7 +84,7 @@ class ManageModulesWindow extends ManageWindowBase { this.Submit(false) } - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Restart " . this.app.appName . "?", "text", "One or more module changes require restarting " . this.app.appName . " to fully take effect.`n`nWould you like to restart " . this.app.appName . " now?" )) @@ -153,7 +153,7 @@ class ManageModulesWindow extends ManageWindowBase { menuItems.Push(Map("label", "Delete", "name", "DeleteModule")) } - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EnableModule") { this.EnableModule(key) diff --git a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk index 845423b6..b3b54fe7 100644 --- a/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/PlatformsWindow.ahk @@ -115,7 +115,7 @@ class PlatformsWindow extends ManageWindowBase { menuItems.Push(Map("label", "Install", "name", "InstallPlatform")) } - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) if (result == "EditPlatform") { this.EditPlatform(key) diff --git a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk index 5d326086..51c997e3 100644 --- a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk +++ b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk @@ -22,7 +22,7 @@ class BlizzardPlatform extends RegistryLookupGamePlatformBase { productInstalls := [] if (this.app.Services.Has("BlizzardProductDb")) { - productInstalls := this.app.Service("BlizzardProductDb").GetProductInstalls() + productInstalls := this.app["BlizzardProductDb"].GetProductInstalls() } games := [] diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 36a1ec84..13f7d8a8 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -49,8 +49,8 @@ class LaunchpadBuilder extends AppBase { RunApp(config) { super.RunApp(config) - version := this.Service("GitTagVersionIdentifier").IdentifyVersion() - buildInfo := this.Service("manager.gui").Dialog(Map( + version := this["GitTagVersionIdentifier"].IdentifyVersion() + buildInfo := this["manager.gui"].Dialog(Map( "type", "BuildSettingsForm", "version", version )) @@ -83,7 +83,7 @@ class LaunchpadBuilder extends AppBase { } if (buildInfo.DeployToGitHub || buildInfo.DeployToApi || buildInfo.DeployToChocolatey) { - releaseInfo := this.Service("manager.gui").Dialog(Map("type", "ReleaseInfoForm")) + releaseInfo := this["manager.gui"].Dialog(Map("type", "ReleaseInfoForm")) if (!releaseInfo) { this.ExitApp() @@ -148,7 +148,7 @@ class LaunchpadBuilder extends AppBase { if (!this.GetCmdOutput("git show-ref " . version)) { RunWait("git tag " . version, this.appDir) - response := this.Service("manager.gui").Dialog(Map( + response := this["manager.gui"].Dialog(Map( "title", "Push git tag?", "text", "Would you like to push the git tag that was just created (" . version . ") to origin?" )) diff --git a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk b/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk index 8ea2644b..a855d436 100644 --- a/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk +++ b/Lib/LaunchpadBuilder/AppBuilder/ChocoPkgBuilder.ahk @@ -9,7 +9,7 @@ class ChocoPkgBuilder extends AppBuilderBase { throw AppException("Installer file doesn't exist, cannot build chocolatey package.") } - hash := this.app.Service("FileHasher").Hash(installer, FileHasher.HASH_TYPE_SHA256) + hash := this.app["FileHasher"].Hash(installer, FileHasher.HASH_TYPE_SHA256) if (!hash) { throw AppException("Failed to create an SHA256 hash of the installer file.") diff --git a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk b/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk index 330209b9..5173dc95 100644 --- a/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk +++ b/Lib/LaunchpadBuilder/BuildDeployer/ApiBuildDeployer.ahk @@ -2,7 +2,7 @@ class ApiBuildDeployer extends BuildDeployerBase { Deploy(deployInfo) { apiUrl := "https://api.launchpad.games/v1/release-info" - this.app.Service("manager.gui").Dialog(Map( + this.app["manager.gui"].Dialog(Map( "title", "Not Yet Available", "text", "Release info pushing is not yet available. Please update release info manually." )) diff --git a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk index ec563367..747feefa 100644 --- a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk +++ b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk @@ -64,11 +64,11 @@ class LaunchpadLauncher extends AppBase { RunApp(config) { super.RunApp(config) - this.Service("Launcher").LaunchGame() + this["Launcher"].LaunchGame() } RestartApp() { - game := this.Service("Game") + game := this["Game"] if (game) { game.StopOverlay() @@ -78,7 +78,7 @@ class LaunchpadLauncher extends AppBase { } ExitApp() { - game := this.Service("Game") + game := this["Game"] if (game) { game.StopOverlay() diff --git a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk b/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk index 43f0ebbe..765a5369 100644 --- a/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk +++ b/Lib/LaunchpadLauncher/Condition/SteamConditionBase.ahk @@ -11,7 +11,7 @@ class SteamConditionBase extends ConditionBase { GetSteamPath(app) { steamPath := "" - platforms := app.Parameter("platforms") + platforms := app.Parameter["platforms"] if (platforms.Has("Steam") && platforms["Steam"].Has("InstallDir")) { steamPath := platforms["Steam"]["InstallDir"] diff --git a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk index 15a0fc83..5eafec37 100644 --- a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk +++ b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk @@ -4,7 +4,7 @@ class BlizzardGame extends SimpleGame { playButtonColors := ["0074E0", "148EFF"] GetRunCmd() { - launcherPath := this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"] + launcherPath := this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] if (launcherPath != "") { gameKey := this.config["GameLauncherSpecificId"] @@ -16,7 +16,7 @@ class BlizzardGame extends SimpleGame { RunGameRun() { pid := super.RunGameRun() - winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"] + winTitle := this.app["Launcher"].config["LauncherWindowTitle"] if (!WinExist(winTitle)) { WinWait(winTitle) @@ -53,7 +53,7 @@ class BlizzardGame extends SimpleGame { } CleanupAfterRun(progress := "") { - winTitle := this.app.Service("Launcher").config["LauncherWindowTitle"] + winTitle := this.app["Launcher"].config["LauncherWindowTitle"] if (WinExist(winTitle)) { WinClose("ahk_id" . WinGetID(winTitle)) } diff --git a/Lib/LaunchpadLauncher/Game/GameBase.ahk b/Lib/LaunchpadLauncher/Game/GameBase.ahk index f50dd053..e943ab16 100644 --- a/Lib/LaunchpadLauncher/Game/GameBase.ahk +++ b/Lib/LaunchpadLauncher/Game/GameBase.ahk @@ -23,7 +23,7 @@ class GameBase { config := Map() } - this.launcherConfig := app.Service("config.app") + this.launcherConfig := app["config.app"] InvalidParameterException.CheckTypes("GameBase", "app", app, "AppBase", "key", key, "", "config", config, "Map") this.app := app this.key := key @@ -33,7 +33,7 @@ class GameBase { Log(message, level := "Debug") { if (this.app.Services.Has("logger") && this.launcherConfig["LoggingLevel"] != "None") { - this.app.Service("logger").Log(this.key . ": " . message, level) + this.app["logger"].Log(this.key . ": " . message, level) } } @@ -180,14 +180,14 @@ class GameBase { StartOverlay() { SetTimer(this.overlayCallbackObj, 0) this.Log("Starting Launchpad Overlay...") - this.app.Service("manager.overlay").Start(this.launcherConfig["OverlayHotkey"]) + this.app["manager.overlay"].Start(this.launcherConfig["OverlayHotkey"]) this.overlayStarted := true } StopOverlay() { if (this.overlayStarted) { this.Log("Shutting down Launchpad Overlay...") - this.app.Service("manager.overlay").Close() + this.app["manager.overlay"].Close() } } @@ -198,7 +198,7 @@ class GameBase { } this.Log("Closing overlay if running...") - this.app.Service("manager.overlay").Close() + this.app["manager.overlay"].Close() this.Log("Cleaning up scheduled task(s)...") this.CleanupScheduledTask() } diff --git a/Lib/LaunchpadLauncher/Game/RiotGame.ahk b/Lib/LaunchpadLauncher/Game/RiotGame.ahk index 0a63e8ec..e4edda9b 100644 --- a/Lib/LaunchpadLauncher/Game/RiotGame.ahk +++ b/Lib/LaunchpadLauncher/Game/RiotGame.ahk @@ -1,6 +1,6 @@ class RiotGame extends SimpleGame { GetRunCmd() { - launcherPath := "`"" . this.app.Service("Launcher").config["LauncherInstallDir"] . "\" . this.app.Service("Launcher").config["LauncherExe"] . "`"" + launcherPath := "`"" . this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] . "`"" if (launcherPath != "") { gameKey := this.config["GameLauncherSpecificId"] diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 56c04e44..392be1b7 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -8,6 +8,7 @@ #Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk #Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk #Include Modules\WebServices\Gui\AuthenticationGui\LaunchpadLoginWindow.ahk +#Include Modules\WebServices\Gui\Form\FeedbackWindow.ahk #Include Modules\WebServices\Gui\ManageWindow\ManageWebServicesWindow.ahk #Include Modules\WebServices\LayerSource\WebServiceAdapterLayerSource.ahk #Include Modules\WebServices\WebServiceAdapter\FileWebServiceAdapter.ahk @@ -53,6 +54,8 @@ #Include Volantis.App\Event\ComponentInfoEvent.ahk #Include Volantis.App\Event\DefineComponentsEvent.ahk #Include Volantis.App\Event\LoadComponentEvent.ahk +#Include Volantis.App\Event\MenuItemsEvent.ahk +#Include Volantis.App\Event\MenuResultEvent.ahk #Include Volantis.App\Event\RegisterComponentsEvent.ahk #Include Volantis.App\Event\ServiceDefinitionsEvent.ahk #Include Volantis.App\Events\Events.ahk @@ -68,7 +71,6 @@ #Include Volantis.App\Gui\Dialog\UpdateAvailableWindow.ahk #Include Volantis.App\Gui\EntityEditor\EntityEditorBase.ahk #Include Volantis.App\Gui\EntityEditor\SimpleEntityEditor.ahk -#Include Volantis.App\Gui\Form\FeedbackWindow.ahk #Include Volantis.App\Gui\Form\FormGuiBase.ahk #Include Volantis.App\Gui\Form\IconSelector.ahk #Include Volantis.App\Gui\ManageWindow\ManageEntitiesWindow.ahk diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk index 5e14fd36..1f84afbd 100644 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk @@ -36,10 +36,10 @@ class ApiDataSource extends DataSourceBase { request.requestHeaders["Cache-Control"] := "no-cache" if (this.app.Config["api_authentication"]) { - entityMgr := webService := this.app.Service("entity_manager.web_service") + entityMgr := webService := this.app["entity_manager.web_service"] if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { - webService := this.app.Service("entity_manager.web_service")["launchpad_api"] + webService := this.app["entity_manager.web_service"]["launchpad_api"] webService["Provider"]["Authenticator"].AlterRequest(webService, request) } @@ -92,7 +92,7 @@ class ApiDataSource extends DataSourceBase { status := Map("authenticated", false, "account", "", "photo", "") if (this.app.Config["api_authentication"]) { - entityMgr := webService := this.app.Service("entity_manager.web_service") + entityMgr := webService := this.app["entity_manager.web_service"] if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"] && entityMgr["launchpad_api"]["Authenticated"]) { statusResult := this.ReadItem(path, true) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index dc6e135e..85a1b0ab 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -354,7 +354,7 @@ class WebServiceEntity extends AppEntityBase { if (SubStr(imgPath, 1, 4) == "http") { cachePath := "account--profile.jpg" - imgPath := this.app.Service("manager.cache")["file"].GetCachedDownload(cachePath, imgPath) + imgPath := this.app["manager.cache"]["file"].GetCachedDownload(cachePath, imgPath) } } diff --git a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk index 66ea089b..0924015c 100644 --- a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk +++ b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk @@ -3,6 +3,12 @@ class WebServicesEventSubscriber extends EventSubscriberBase { return Map( Events.APP_POST_STARTUP, [ ObjBindMethod(this, "OnPostStartup") + ], + Events.APP_MENU_ITEMS_LATE, [ + ObjBindMethod(this, "OnMenuItemsLate") + ], + Events.APP_MENU_PROCESS_RESULT, [ + ObjBindMethod(this, "OnMenuProcessResult") ] ) } @@ -18,4 +24,20 @@ class WebServicesEventSubscriber extends EventSubscriberBase { webService.Login() } } + + OnMenuItemsLate(event, extra, eventName, hwnd) { + event.MenuItems.Push(Map( + "label", "Provide &Feedback", + "name", "ProvideFeedback" + )) + } + + OnMenuProcessResult(event, extra, eventName, hwnd) { + if (!event.IsFinished) { + if (event.Result == "ProvideFeedback") { + this.container["manager.gui"].Dialog(Map("type", "FeedbackWindow")) + event.IsFinished := true + } + } + } } diff --git a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk similarity index 61% rename from Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk rename to Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk index bcf5b290..c508904b 100644 --- a/Lib/Shared/Volantis.App/Gui/Form/FeedbackWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk @@ -36,20 +36,44 @@ class FeedbackWindow extends DialogBox { SendFeedback() { global appVersion - if (this.apiEndpointUrl) { - endpoint := this.apiEndpointUrl . "/submit-feedback" + webServiceId := "launchpad_api" + entityMgr := this.container["entity_manager.web_service"] + + results := Map() + success := false + + if (entityMgr.Has(webServiceId) && entityMgr[webServiceId]["Enabled"]) { + webService := entityMgr[webServiceId] body := Map() body["email"] := this.guiObj["Email"].Text body["version"] := appVersion body["feedback"] := this.guiObj["Feedback"].Text - request := WinHttpReq(endpoint) - response := request.Send("POST", body) - success := !!(request.GetStatusCode() == 200) + results := webService.AdapterRequest(Map("data", body), "feedback_submission", "create") + } - notification := success ? "Successfully sent feedback to Volantis Development" : "Failed to send feedback to Volantis Development" - this.notifierObj.Notify(notification, "Feedback Sent", success ? "info" : "error") + for key, result in results { + if (result) { + success := true + break + } } + + message := "" + + if (success) { + message := "Successfully sent feedback" + } else if (results.Count) { + message := "Failed to send feedback" + } else { + message := "No feedback adapters are enabled" + } + + this.notifierObj.Notify( + message, + "Feedback Submission", + success ? "info" : "error" + ) } } diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/Lib/Shared/Volantis.App/App/AppBase.ahk index 082990c8..5b75f081 100644 --- a/Lib/Shared/Volantis.App/App/AppBase.ahk +++ b/Lib/Shared/Volantis.App/App/AppBase.ahk @@ -1,5 +1,4 @@ class AppBase { - developer := "" versionStr := "" appName := "" appDir := "" @@ -8,9 +7,9 @@ class AppBase { configObj := "" stateObj := "" serviceContainerObj := "" - customTrayMenu := false themeReady := false startConfig := "" + isSetup := false static Instance := "" @@ -25,11 +24,20 @@ class AppBase { } Config { - get => this.Service("config.app") + get => this["config.app"] } State { - get => this.Service("state.app") + get => this["state.app"] + } + + Parameter[key] { + get => this.Services.GetParameter(key) + set => this.Services.SetParameter(key, value) + } + + __Item[serviceId] { + get => this.Service(serviceId) } __New(config := "", autoStart := true) { @@ -57,6 +65,16 @@ class AppBase { "app_dir", this.appDir, "data_dir", this.dataDir, "tmp_dir", this.tmpDir, + "app.website_url", "", + "app.custom_tray_menu", false, + "app.developer", "", + "app.has_settings", false, + "app.settings_window", "", + "app.show_restart_menu_item", true, + "app.supports_update_check", false, + "app.show_about_menu_item", false, + "app.about_window", "", + "app.show_website_menu_item", false, "resources_dir", "@@{app_dir}\Resources", "config_path", "@@{app_dir}\" . this.appName . ".json", "config_key", "config", @@ -374,10 +392,6 @@ class AppBase { config["appName"] := appBaseName } - if (!config.Has("developer")) { - config["developer"] := "" - } - if (!config.Has("appDir") || !config["appDir"]) { config["appDir"] := A_ScriptDir } @@ -396,7 +410,6 @@ class AppBase { this.appName := config["appName"] this.versionStr := config["version"] - this.developer := config["developer"] this.appDir := config["appDir"] this.tmpDir := config["tmpDir"] this.dataDir := config["dataDir"] @@ -412,26 +425,26 @@ class AppBase { this.LoadServices(config) if (!config.Has("useShell") || config("useShell")) { - this.Service("shell") + this["shell"] } OnError(ObjBindMethod(this, "OnException")) event := AppRunEvent(Events.APP_PRE_INITIALIZE, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) this.InitializeApp(config) event := AppRunEvent(Events.APP_POST_INITIALIZE, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) event := AppRunEvent(Events.APP_PRE_RUN, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) this.RunApp(config) event := AppRunEvent(Events.APP_POST_STARTUP, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) } LoadServices(config) { @@ -441,18 +454,18 @@ class AppBase { )) this.Services.LoadDefinitions(MapDefinitionLoader(config)) - sdFactory := this.Service("factory.structured_data") - serviceFile := this.Services.GetParameter("service_files.app") + sdFactory := this["factory.structured_data"] + serviceFile := this.Parameter["service_files.app"] if (FileExist(serviceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile)) } - this.Service("config.app") + this["config.app"] this.InitializeTheme() this.InitializeModules(config) - for index, moduleServiceFile in this.Service("manager.module").GetModuleServiceFiles() { + for index, moduleServiceFile in this["manager.module"].GetModuleServiceFiles() { if (FileExist(moduleServiceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, moduleServiceFile)) } else { @@ -461,49 +474,49 @@ class AppBase { } ; Reload user config files to ensure they are the active values - this.Service("config.app").LoadConfig(true) + this["config.app"].LoadConfig(true) ; Register early event subscribers (e.g. modules) - this.Service("manager.event").RegisterServiceSubscribers(this.Services) + this["manager.event"].RegisterServiceSubscribers(this.Services) - this.Service("manager.event").Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded")) + this["manager.event"].Register(Events.APP_SERVICES_LOADED, "AppServices", ObjBindMethod(this, "OnServicesLoaded")) event := ServiceDefinitionsEvent(Events.APP_SERVICE_DEFINITIONS, "", "", config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (event.Services.Count || event.Parameters.Count) { this.Services.LoadDefinitions(SimpleDefinitionLoader(event.Services, event.Parameters)) } - serviceFile := this.Services.GetParameter("service_files.user") + serviceFile := this.Parameter["service_files.user"] if (FileExist(serviceFile)) { this.Services.LoadDefinitions(FileDefinitionLoader(sdFactory, serviceFile)) } ; Register any missing late-loading event subscribers - this.Service("manager.event").RegisterServiceSubscribers(this.Services) + this["manager.event"].RegisterServiceSubscribers(this.Services) event := AppRunEvent(Events.APP_SERVICES_LOADED, this, config) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) } OnServicesLoaded(event, extra, eventName, hwnd) { - this.Service("manager.cache") - this.Service("manager.entity_type").All() - this.Service("manager.installer").RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT) + this["manager.cache"] + this["manager.entity_type"].All() + this["manager.installer"].RunInstallers(InstallerBase.INSTALLER_TYPE_REQUIREMENT) } InitializeModules(config) { - includeFiles := this.Services.GetParameter("include_files") - updated := this.Service("manager.module").UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"]) + includeFiles := this.Parameter["include_files"] + updated := this["manager.module"].UpdateModuleIncludes(includeFiles["modules"], includeFiles["module_tests"]) if (updated) { message := A_IsCompiled ? "Your modules have been updated. Currently, you must recompile " this.appName . " yourself for the changes to take effect. Would you like to exit now (highly recommended)?" : "Your modules have been updated, and " this.appName . " must be restarted for the changes to take effect. Would you like to restart now?" - response := this.app.Service("manager.gui").Dialog(Map( + response := this.app["manager.gui"].Dialog(Map( "title", "Module Includes Updated", "text", message )) @@ -519,16 +532,16 @@ class AppBase { } InitializeTheme() { - this.Service("gdip", "manager.gui", "manager.theme") + this[["gdip", "manager.gui", "manager.theme"]] this.themeReady := true } InitializeApp(config) { A_AllowMainWindow := false - if (this.customTrayMenu) { + if (this.Parameter["app.custom_tray_menu"]) { A_TrayMenu.Delete() - this.Service("manager.event").Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1) + this["manager.event"].Register(Events.AHK_NOTIFYICON, "TrayClick", ObjBindMethod(this, "OnTrayIconRightClick"), 1) } } @@ -537,19 +550,19 @@ class AppBase { this.CheckForUpdates(false) } - if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter("config_path"))) { + if (this.Services.HasParameter("config_path") && !FileExist(this.Parameter["config_path"])) { this.InitialSetup(config) } } OpenApp() { - mainWin := this.Parameter("config.main_window") + mainWin := this.Parameter["config.main_window"] if (mainWin) { - if (this.Service("manager.gui").Has(mainWin)) { - WinActivate("ahk_id " . this.Service("manager.gui")[mainWin].GetHwnd()) + if (this["manager.gui"].Has(mainWin)) { + WinActivate("ahk_id " . this["manager.gui"][mainWin].GetHwnd()) } else { - this.Service("manager.gui").OpenWindow(Map( + this["manager.gui"].OpenWindow(Map( "type", mainWin, "title", this.appName )) @@ -559,7 +572,7 @@ class AppBase { ExitApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (this.Services.Has("gdip")) { Gdip_Shutdown(this.Services["gdip"].GetHandle()) @@ -570,7 +583,7 @@ class AppBase { RestartApp() { event := AppRunEvent(Events.APP_RESTART, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) if (this.Services.Has("gdip")) { Gdip_Shutdown(this.Services["gdip"].GetHandle()) @@ -586,7 +599,7 @@ class AppBase { throw AppException("The shell is disabled, so shell commands cannot currently be run.") } - result := this.Service("shell").Exec(A_ComSpec . " /C " . command).StdOut.ReadAll() + result := this["shell"].Exec(A_ComSpec . " /C " . command).StdOut.ReadAll() if (trimOutput) { result := Trim(result, " `r`n`t") @@ -606,12 +619,12 @@ class AppBase { } for index, arrName in name { - results[arrName] := this.Service(arrName) + results[arrName] := this[arrName] } if (params && params.Length) { for index, arrName in params { - results[arrName] := this.Service(arrName) + results[arrName] := this[arrName] } } @@ -621,14 +634,15 @@ class AppBase { return this.Services.Get(name) } - Parameter(name) { - return this.Services.GetParameter(name) - } - OnException(e, mode) { extra := (e.HasProp("Extra") && e.Extra != "") ? "`n`nExtra information:`n" . e.Extra : "" occurredIn := e.What ? " in " . e.What : "" - developer := this.developer ? this.developer : "the developer(s)" + + developer := this.Parameter["app.developer"] + + if (!developer) { + developer := "the developer(s)" + } errorText := this.appName . " has experienced an unhandled exception. You can find the details below." errorText .= "`n`n" . e.Message . extra @@ -646,7 +660,7 @@ class AppBase { } if (this.Services.Has("logger")) { - this.Service("logger").Error(errorText) + this["logger"].Error(errorText) } errorText .= "`n" @@ -659,7 +673,7 @@ class AppBase { if (this.themeReady) { btns := allowContinue ? "*&Continue|&Reload|&Exit" : "*&Reload|&Exit" - this.Service("manager.gui").Dialog(Map( + this["manager.gui"].Dialog(Map( "type", "ErrorDialog", "title", "Unhandled Exception", "text", errorText, @@ -683,7 +697,7 @@ class AppBase { OnTrayIconRightClick(wParam, lParam, msg, hwnd) { if (lParam == Events.MOUSE_RIGHT_UP) { - if (this.customTrayMenu) { + if (this.Parameter["app.custom_tray_menu"]) { this.ShowTrayMenu() return 0 } @@ -691,7 +705,7 @@ class AppBase { } InitialSetup(config) { - ; Override this to set config values as needed + this.isSetup := true } CheckForUpdates(notify := true) { @@ -706,7 +720,7 @@ class AppBase { menuItems.Push(Map("label", "Restart", "name", "RestartApp")) menuItems.Push(Map("label", "Exit", "name", "ExitApp")) - result := this.Service("manager.gui").Menu(menuItems, this) + result := this["manager.gui"].Menu(menuItems, this) this.HandleTrayMenuClick(result) } @@ -730,4 +744,214 @@ class AppBase { this.ExitApp() super.__Delete() } + + MainMenu(parentGui, parentCtl, showOpenAppItem := false) { + menuItems := this.GetMainMenuItems(showOpenAppItem) + + if (menuItems.Length) { + this.HandleMainMenuClick(this["manager.gui"].Menu( + menuItems, + parentGui, + parentCtl + )) + } + } + + GetMainMenuItems(showOpenAppItem := false) { + menuItems := [] + menuItems := this.AddMainMenuEarlyItems(menuItems, showOpenAppItem) + + if (menuItems.Length) { + menuItems.Push("") + } + + length := menuItems.Length + + toolsItems := this.GetToolsMenuItems() + + if (toolsItems.Length) { + menuItems.Push(Map("label", "&Tools", "name", "ToolsMenu", "childItems", toolsItems)) + } + + aboutItems := this.GetAboutMenuItems() + + if (aboutItems.Length) { + menuItems.Push(Map("label", "&About", "name", "About", "childItems", aboutItems)) + } + + menuItems := this.AddMainMenuMiddleItems(menuItems) + + if (menuItems.Length > length) { + menuItems.Push("") + } + + length := menuItems.Length + menuItems := this.AddMainMenuLateItems(menuItems) + + if (menuItems.Length > length) { + menuItems.Push("") + } + + if (this.Parameter["app.show_restart_menu_item"]) { + menuItems.Push(Map("label", "&Restart", "name", "Reload")) + } + + menuItems.Push(Map("label", "E&xit", "name", "Exit")) + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_ALTER, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + GetAboutMenuItems() { + aboutItems := [] + + if (this.Parameter["app.show_about_menu_item"]) { + aboutItems.Push(Map("label", "&About " . this.appName, "name", "About")) + } + + if (this.Parameter["app.show_website_menu_item"]) { + aboutItems.Push(Map("label", "Open &Website", "name", "OpenWebsite")) + } + + event := MenuItemsEvent(Events.APP_MENU_ABOUT_ITEMS_ALTER, aboutItems) + this.Dispatch(event) + aboutItems := event.MenuItems + + return aboutItems + } + + GetToolsMenuItems() { + toolsItems := this.AddEntityManagerMenuLinks([]) + event := MenuItemsEvent(Events.APP_MENU_TOOLS_ITEMS_ALTER, toolsItems) + this.Dispatch(event) + toolsItems := event.MenuItems + + return toolsItems + } + + AddMainMenuEarlyItems(menuItems, showOpenAppItem := false) { + if (showOpenAppItem) { + menuItems.Push(Map("label", "Open " . this.appName, "name", "OpenApp")) + menuItems.Push("") + } + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_EARLY, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + AddMainMenuMiddleItems(menuItems) { + event := MenuItemsEvent(Events.APP_MENU_ITEMS_MIDDLE, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + return menuItems + } + + AddMainMenuLateItems(menuItems) { + if (this.Parameter["app.has_settings"]) { + menuItems.Push(Map("label", "&Settings", "name", "Settings")) + } + + if (this.Parameter["app.supports_update_check"]) { + menuItems.Push(Map("label", "Check for &Updates", "name", "CheckForUpdates")) + } + + event := MenuItemsEvent(Events.APP_MENU_ITEMS_LATE, menuItems) + this.Dispatch(event) + menuItems := event.MenuItems + + return menuItems + } + + AddEntityManagerMenuLinks(menuItems) { + menuEntityTypes := this._getToolsMenuEntityTypes() + + for key, entityType in menuEntityTypes { + menuLinkText := entityType.definition["manager_menu_link_text"] + + if (!menuLinkText) { + menuLinkText := "&" . entityType.definition["name_plural"] + } + + menuItems.Push(Map("label", menuLinkText, "name", "manage_" . key)) + } + + return menuItems + } + + Dispatch(event) { + this["manager.event"].DispatchEvent(event) + } + + _getToolsMenuEntityTypes() { + entityTypes := Map() + + for key, entityType in this["manager.entity_type"] { + if (entityType.definition["manager_link_in_tools_menu"]) { + entityTypes[key] := entityType + } + } + + return entityTypes + } + + HandleMainMenuClick(result) { + event := MenuResultEvent(Events.APP_MENU_PROCESS_RESULT, result) + this.Dispatch(event) + result := event.Result + + if (!event.IsFinished) { + if (result == "About") { + this.ShowAbout() + } else if (result == "OpenWebsite") { + this.OpenWebsite() + } else if (result == "Settings") { + this.ShowSettings() + } else if (result == "CheckForUpdates") { + this.CheckForUpdates() + } else if (result == "Reload") { + this.restartApp() + } else if (result == "Exit") { + this.ExitApp() + } else { + for key, entityType in this._getToolsMenuEntityTypes() { + if (result == "manage_" . key) { + this["entity_type." . key].OpenManageWindow() + break + } + } + } + } + + return result + } + + ShowSettings() { + windowName := this.Parameter["app.settings_window"] + + if (windowName) { + this["manager.gui"].Dialog(Map("type", windowName, "unique", false)) + } + } + + ShowAbout() { + windowName := this.Parameter["app.about_window"] + + if (windowName) { + this["manager.gui"].Dialog(Map("type", windowName)) + } + } + + OpenWebsite() { + websiteUrl := this.Parameter["app.website_url"] + + if (websiteUrl) { + Run(websiteUrl) + } + } } diff --git a/Lib/Shared/Volantis.App/App/TestAppBase.ahk b/Lib/Shared/Volantis.App/App/TestAppBase.ahk index a5263d94..25120777 100644 --- a/Lib/Shared/Volantis.App/App/TestAppBase.ahk +++ b/Lib/Shared/Volantis.App/App/TestAppBase.ahk @@ -1,13 +1,13 @@ class TestAppBase extends AppBase { ExitApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) ; Don't actually exit } RestartApp() { event := AppRunEvent(Events.APP_SHUTDOWN, this) - this.Service("manager.event").DispatchEvent(event) + this["manager.event"].DispatchEvent(event) ; Don't actually restart } } diff --git a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk b/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk index a9406e35..6f85723d 100644 --- a/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk +++ b/Lib/Shared/Volantis.App/BulkOperation/BulkOperationBase.ahk @@ -40,7 +40,7 @@ class BulkOperationBase { } if (this.app.Services.Has("logger")) { - this.app.Service("logger").Debug(Type(this) . ": Starting bulk operation...") + this.app["logger"].Debug(Type(this) . ": Starting bulk operation...") } this.running := true @@ -56,7 +56,7 @@ class BulkOperationBase { LogResults() { if (this.app.Services.Has("logger")) { - this.app.Service("logger").Info(Type(this) . " Results: " . this.GetResultMessage()) + this.app["logger"].Info(Type(this) . " Results: " . this.GetResultMessage()) } } @@ -89,7 +89,7 @@ class BulkOperationBase { ownerOrParent := this.parent } - this.progress := this.app.Service("manager.gui").OpenWindow(Map( + this.progress := this.app["manager.gui"].OpenWindow(Map( "type", "ProgressIndicator", "title", this.progressTitle, "text", this.progressText, @@ -115,7 +115,7 @@ class BulkOperationBase { Notify() { if (this.shouldNotify && this.app.Services.Has("notifier")) { - this.app.Service("notifier").Info(this.GetResultMessage()) + this.app["notifier"].Info(this.GetResultMessage()) } } diff --git a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk b/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk index 6c4d0aad..b093857a 100644 --- a/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk +++ b/Lib/Shared/Volantis.App/BulkOperation/InstallOp/InstallOp.ahk @@ -17,7 +17,7 @@ class InstallOp extends BulkOperationBase { for index, name in this.installers { name := "installer." . name - installer := this.app.Service(name) + installer := this.app[name] if (!HasBase(installer, InstallerBase.Prototype)) { throw AppException("Provided installer is not valid: " . name) diff --git a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk index 803fd056..b1be4fd7 100644 --- a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk +++ b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk @@ -146,8 +146,8 @@ class AppEntityBase extends FieldableEntity { } for index, dataSourceKey in dataSourceKeys { - if (this.app.Service("manager.data_source").Has(dataSourceKey)) { - dataSource := this.app.Service("manager.data_source")[dataSourceKey] + if (this.app["manager.data_source"].Has(dataSourceKey)) { + dataSource := this.app["manager.data_source"][dataSourceKey] if (dataSource) { dataSources[dataSourceKey] := dataSource diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk index 8d25858b..c02ae818 100644 --- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk +++ b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk @@ -25,7 +25,7 @@ class BackupEntity extends AppEntityBase { definitions["IconSrc"] := Map( "type", "icon_file", "description", "The path to this an icon (.ico or .exe).", - "default", this.app.Service("manager.theme")[].GetIconPath("backup") + "default", this.app["manager.theme"][].GetIconPath("backup") ) definitions["Source"] := Map( diff --git a/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk b/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk new file mode 100644 index 00000000..23c1f89f --- /dev/null +++ b/Lib/Shared/Volantis.App/Event/MenuItemsEvent.ahk @@ -0,0 +1,16 @@ +class MenuItemsEvent extends EventBase { + menuItemsObj := "" + + MenuItems { + get => this.menuItemsObj + } + + __New(eventName, menuItems := "") { + if (!menuItems) { + menuItems := [] + } + + this.menuItemsObj := menuItems + super.__New(eventName) + } +} diff --git a/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk b/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk new file mode 100644 index 00000000..8d9011d6 --- /dev/null +++ b/Lib/Shared/Volantis.App/Event/MenuResultEvent.ahk @@ -0,0 +1,20 @@ +class MenuResultEvent extends EventBase { + resultItem := "" + finished := false + + Result { + get => this.resultItem + set => this.resultItem := value + } + + IsFinished { + get => this.finished + set => !!(value) + } + + __New(eventName, result) { + this.resultItem := result + + super.__New(eventName) + } +} diff --git a/Lib/Shared/Volantis.App/Events/Events.ahk b/Lib/Shared/Volantis.App/Events/Events.ahk index 35992f39..0ba53063 100644 --- a/Lib/Shared/Volantis.App/Events/Events.ahk +++ b/Lib/Shared/Volantis.App/Events/Events.ahk @@ -19,5 +19,13 @@ class Events { static APP_SHUTDOWN := 0x1020 static APP_RESTART := 0x1025 + static APP_MENU_ITEMS_EARLY := 0x1030 + static APP_MENU_ITEMS_MIDDLE := 0x1032 + static APP_MENU_ITEMS_LATE := 0x1034 + static APP_MENU_ITEMS_ALTER := 0x1036 + static APP_MENU_TOOLS_ITEMS_ALTER := 0x1038 + static APP_MENU_ABOUT_ITEMS_ALTER := 0x1040 + static APP_MENU_PROCESS_RESULT := 0x1042 + static AHK_NOTIFYICON := 0x404 } diff --git a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk index 7ad3903b..467bd165 100644 --- a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk @@ -78,6 +78,6 @@ class EntityEditorBase extends FormGuiBase { Create() { super.Create() - this.dataSource := this.app.Service("manager.data_source").GetDefaultDataSource() + this.dataSource := this.app["manager.data_source"].GetDefaultDataSource() } } diff --git a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk index 1c59feaf..412fb7eb 100644 --- a/Lib/Shared/Volantis.App/Gui/GuiBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/GuiBase.ahk @@ -129,7 +129,7 @@ class GuiBase { RegisterCallbacks() { guiId := "Gui" . this.guiId - this.app.Service("manager.event") + this.app["manager.event"] .Register(Events.MOUSE_MOVE, guiId, ObjBindMethod(this, "OnMouseMove")) .Register(Events.WM_NCCALCSIZE, guiId, ObjBindMethod(this, "OnCalcSize")) .Register(Events.WM_NCACTIVATE, guiId, ObjBindMethod(this, "OnActivate")) @@ -142,10 +142,10 @@ class GuiBase { __Delete() { if (this.app) { - this.app.Service("manager.event").Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId) - this.app.Service("manager.event").Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.MOUSE_MOVE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCCALCSIZE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCACTIVATE, "Gui" . this.guiId) + this.app["manager.event"].Unregister(Events.WM_NCHITTEST, "Gui" . this.guiId) } if (this.activeTooltip) { @@ -567,22 +567,22 @@ class GuiBase { } if (!this.isClosed && WinExist("ahk_id " . this.guiObj.Hwnd)) { - this.app.Service("manager.gui").StoreWindowState(this) + this.app["manager.gui"].StoreWindowState(this) WinClose("ahk_id " . this.guiObj.Hwnd) } else { this.Destroy() } - this.app.Service("manager.gui").CleanupWindow(this.guiId) + this.app["manager.gui"].CleanupWindow(this.guiId) } Destroy() { if (!this.isClosed && this.config["saveWindowState"]) { - this.app.Service("manager.gui").StoreWindowState(this) + this.app["manager.gui"].StoreWindowState(this) } if (this.owner) { - this.app.Service("manager.gui").ReleaseFromParent(this.guiId) + this.app["manager.gui"].ReleaseFromParent(this.guiId) } this.Cleanup() @@ -594,7 +594,7 @@ class GuiBase { } Cleanup() { - this.app.Service("manager.gui").UnloadComponent(this.guiId) + this.app["manager.gui"].UnloadComponent(this.guiId) ; Extend to clear any global variables used } diff --git a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk index 3c3950d0..1a4b3c09 100644 --- a/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/ManageWindow/ManageEntitiesWindow.ahk @@ -188,7 +188,7 @@ class ManageEntitiesWindow extends ManageWindowBase { entityObj := this.entityMgr[key] menuItems := this.GetContextMenuItems(entityObj) - result := this.app.Service("manager.gui").Menu(menuItems, this) + result := this.app["manager.gui"].Menu(menuItems, this) this.ProcessContextMenuResult(result, key) } diff --git a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk b/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk index 13f2c878..a7b5d689 100644 --- a/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk +++ b/Lib/Shared/Volantis.App/Gui/Menu/MenuGui.ahk @@ -133,7 +133,7 @@ if (btn.ChildItems) { this.childOpen := true - this.result := this.app.Service("manager.gui").Menu(Map( + this.result := this.app["manager.gui"].Menu(Map( "parent", this, "child", true, "openAtCtlSide", "right" diff --git a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk b/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk index 40f371d8..3f535567 100644 --- a/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/LocationBlock.ahk @@ -83,7 +83,7 @@ class LocationBlock extends GuiControlBase { } OnLocationOptions(btn, info) { - result := this.app.Service("manager.gui").Menu(Map( + result := this.app["manager.gui"].Menu(Map( "parent", this.guiObj, "child", true ), btn.MenuItems, btn) diff --git a/Lib/Shared/Volantis.App/State/StateBase.ahk b/Lib/Shared/Volantis.App/State/StateBase.ahk index 65f0c2b4..c19f07e9 100644 --- a/Lib/Shared/Volantis.App/State/StateBase.ahk +++ b/Lib/Shared/Volantis.App/State/StateBase.ahk @@ -15,7 +15,7 @@ class StateBase { } IsStateOutdated() { - return this.app.Service("version_checker").VersionIsOutdated(this.app.Version, this.Version) + return this.app["version_checker"].VersionIsOutdated(this.app.Version, this.Version) } __New(app, state := "", autoLoad := false) { diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 826e1d58..c428f0b1 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -393,7 +393,7 @@ class EntityBase { result := "Cancel" while (mode) { - result := this.app.Service("manager.gui").Dialog(Map( + result := this.app["manager.gui"].Dialog(Map( "type", "SimpleEntityEditor", "mode", mode, "child", !!(ownerOrParent), diff --git a/Lib/TestLib/Test/AppTestBase.ahk b/Lib/TestLib/Test/AppTestBase.ahk index 028d322c..4414cacf 100644 --- a/Lib/TestLib/Test/AppTestBase.ahk +++ b/Lib/TestLib/Test/AppTestBase.ahk @@ -6,12 +6,12 @@ class AppTestBase extends TestBase { GetTestAppConfig() { config := Map( "appName", "Test App", - "developer", "Test Developer", "appDir", A_ScriptDir, "tmpDir", this.testDir . "\Temp", "dataDir", this.testDir . "\Data", "version", this.testAppVersion, "parameters", Map( + "app.developer", "Test Developer", "config.flush_cache_on_exit", false, "config.logging_level", "none", "config.module_dirs", [], diff --git a/Scripts/Build.ahk b/Scripts/Build.ahk index 998e9099..09656b22 100644 --- a/Scripts/Build.ahk +++ b/Scripts/Build.ahk @@ -9,7 +9,6 @@ appVersion := "{{VERSION}}" LaunchpadBuilder(Map( "appDir", appDir, "appName", "Launchpad", - "developer", "Volantis Development", "version", appVersion, "trayIcon", appDir . "\Resources\Graphics\launchpad.ico", "console", true, From 7e45a12cb1e1574d49f1ba7de2734073fe8f4a3f Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 18:49:02 -0500 Subject: [PATCH 071/138] Turn update checks into an event which is subscribed from the Launchpad API module --- Lib/Launchpad/App/Launchpad.ahk | 23 --------------- Lib/Shared/Includes.ahk | 2 ++ .../LaunchpadApiSubscriber.ahk | 27 ++++++++++++++++++ Lib/Shared/Volantis.App/App/AppBase.ahk | 28 ++++++++++++++++--- .../Volantis.App/Event/ReleaseInfoEvent.ahk | 23 +++++++++++++++ Lib/Shared/Volantis.App/Events/Events.ahk | 2 ++ 6 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk create mode 100644 Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk diff --git a/Lib/Launchpad/App/Launchpad.ahk b/Lib/Launchpad/App/Launchpad.ahk index 661c64f3..8ab4a97d 100644 --- a/Lib/Launchpad/App/Launchpad.ahk +++ b/Lib/Launchpad/App/Launchpad.ahk @@ -1,29 +1,6 @@ class Launchpad extends AppBase { detectGames := false - CheckForUpdates(notify := true) { - updateAvailable := false - - if (this.Version != "{{VERSION}}" && this["manager.data_source"].GetDefaultDataSource()) { - dataSource := this["manager.data_source"].GetDefaultDataSource() - releaseInfoStr := dataSource.ReadItem("release-info") - - if (releaseInfoStr) { - data := JsonData() - releaseInfo := data.FromString(&releaseInfoStr) - - if (releaseInfo && releaseInfo["data"].Has("version") && releaseInfo["data"]["version"] && this["version_checker"].VersionIsOutdated(releaseInfo["data"]["version"], this.Version)) { - updateAvailable := true - this["manager.gui"].Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) - } - } - } - - if (!updateAvailable && notify) { - this["notifier"].Info("You're running the latest version of Launchpad. Shiny!") - } - } - UpdateIncludes() { this.RunAhkScript(this.appDir . "\Scripts\UpdateIncludes.ahk") this.RestartApp() diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 392be1b7..1dad575c 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -1,5 +1,6 @@ ; Automatically-generated file. Manual edits will be overwritten. #Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk +#Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk #Include Modules\WebServices\Entity\WebServiceEntity.ahk #Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk #Include Modules\WebServices\Event\WebServiceRequestEvent.ahk @@ -57,6 +58,7 @@ #Include Volantis.App\Event\MenuItemsEvent.ahk #Include Volantis.App\Event\MenuResultEvent.ahk #Include Volantis.App\Event\RegisterComponentsEvent.ahk +#Include Volantis.App\Event\ReleaseInfoEvent.ahk #Include Volantis.App\Event\ServiceDefinitionsEvent.ahk #Include Volantis.App\Events\Events.ahk #Include Volantis.App\Exception\AppException.ahk diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk new file mode 100644 index 00000000..5d47081a --- /dev/null +++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -0,0 +1,27 @@ +class LaunchpadApiSubscriber extends EventSubscriberBase { + GetEventSubscribers() { + return Map( + Events.APP_GET_RELEASE_INFO, [ + ObjBindMethod(this, "GetReleaseInfo") + ], + ) + } + + GetReleaseInfo(event, extra, eventName, hwnd) { + releaseInfo := event.ReleaseInfo + + if (!event.ReleaseInfo.Count && this.App.Version != "{{VERSION}}") { + webService := this.App["entity_manager.web_service"]["launchpad_api"] + + if (webService["Enabled"]) { + releaseInfo := webService.AdapterRequest("", "release_info", "read", 1) + + if (releaseInfo && releaseInfo.Has("data")) { + for key, val in releaseInfo["data"] { + event.ReleaseInfo[key] = val + } + } + } + } + } +} diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/Lib/Shared/Volantis.App/App/AppBase.ahk index 5b75f081..07f03fa2 100644 --- a/Lib/Shared/Volantis.App/App/AppBase.ahk +++ b/Lib/Shared/Volantis.App/App/AppBase.ahk @@ -708,10 +708,6 @@ class AppBase { this.isSetup := true } - CheckForUpdates(notify := true) { - ; Optional method to override - } - ShowTrayMenu() { menuItems := [] menuItems.Push(Map("label", "Open " . this.appName, "name", "OpenApp")) @@ -954,4 +950,28 @@ class AppBase { Run(websiteUrl) } } + + CheckForUpdates(notify := true) { + if (this.Parameter["app.supports_update_check"]) { + updateAvailable := false + + event := ReleaseInfoEvent(Events.APP_GET_RELEASE_INFO, this) + this["manager.event"].DispatchEvent(event) + releaseInfo := event.ReleaseInfo + + if ( + releaseInfo + && releaseInfo.Has("version") + && releaseInfo["version"] + && this["version_checker"].VersionIsOutdated(releaseInfo["version"], this.Version) + ) { + updateAvailable := true + this["manager.gui"].Dialog(Map("type", "UpdateAvailableWindow"), releaseInfo) + } + + if (!updateAvailable && notify) { + this["notifier"].Info("You're running the latest version of " . this.appName . ". Shiny!") + } + } + } } diff --git a/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk b/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk new file mode 100644 index 00000000..84af7d5c --- /dev/null +++ b/Lib/Shared/Volantis.App/Event/ReleaseInfoEvent.ahk @@ -0,0 +1,23 @@ +class ReleaseInfoEvent extends EventBase { + appObj := "" + releaseInfoObj := Map() + + App { + get => this.appObj + } + + ReleaseInfo { + get => this.releaseInfoObj + set => this.releaseInfoObj := value + } + + __New(eventName, app, releaseInfo := "") { + this.appObj := app + + if (releaseInfo) { + this.releaseInfoObj := releaseInfo + } + + super.__New(eventName) + } +} diff --git a/Lib/Shared/Volantis.App/Events/Events.ahk b/Lib/Shared/Volantis.App/Events/Events.ahk index 0ba53063..74ee9b65 100644 --- a/Lib/Shared/Volantis.App/Events/Events.ahk +++ b/Lib/Shared/Volantis.App/Events/Events.ahk @@ -27,5 +27,7 @@ class Events { static APP_MENU_ABOUT_ITEMS_ALTER := 0x1040 static APP_MENU_PROCESS_RESULT := 0x1042 + static APP_GET_RELEASE_INFO := 0x1050 + static AHK_NOTIFYICON := 0x404 } From b31c437d5dc3b749a0d88d171f9c930f36a83e23 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Tue, 13 Dec 2022 18:50:20 -0500 Subject: [PATCH 072/138] Only enable app.supports_update_check if the LaunchpadApi module is enabled --- Launchpad.services.json | 1 - Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index f7c00607..a4fff85b 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -5,7 +5,6 @@ "app.developer": "Volantis Development", "app.has_settings": true, "app.settings_window": "SettingsWindow", - "app.supports_update_check": true, "app.show_about_menu_item": true, "app.about_window": "AboutWindow", "app.show_website_menu_item": true, diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index ba9386f9..2830ad34 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -15,6 +15,7 @@ "dependencies": ["WebServices"] }, "parameters": { + "app.supports_update_check": true, "config.data_source_key": "launchpad_api", "config.api_authentication": true, "web_services.providers.launchpad_api": { From ecc6e2e82bf27c16e35696653a9267b45e5057ba Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 02:50:32 -0500 Subject: [PATCH 073/138] Rename and add to entity data events --- Lib/Shared/Includes.ahk | 4 ++- .../Event/EntityDetectValuesEvent.ahk | 17 ++++++++++ .../Event/EntityLayerSourcesEvent.ahk | 13 ++++++++ .../Volantis.Entity/Event/EntityListEvent.ahk | 32 +++++++++++++++++++ .../Event/EntityStorageEvent.ahk | 13 -------- .../Volantis.Entity/Events/EntityEvents.ahk | 7 ++-- .../LayeredData/EntityData.ahk | 14 ++++---- 7 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk create mode 100644 Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk create mode 100644 Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk delete mode 100644 Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 1dad575c..4f961270 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -232,13 +232,15 @@ #Include Volantis.Entity\EntityType\BasicEntityType.ahk #Include Volantis.Entity\EntityType\EntityTypeBase.ahk #Include Volantis.Entity\Event\EntityDataProcessorsEvent.ahk +#Include Volantis.Entity\Event\EntityDetectValuesEvent.ahk #Include Volantis.Entity\Event\EntityEvent.ahk #Include Volantis.Entity\Event\EntityFieldDefinitionsEvent.ahk #Include Volantis.Entity\Event\EntityFieldGroupsEvent.ahk #Include Volantis.Entity\Event\EntityLayersEvent.ahk +#Include Volantis.Entity\Event\EntityLayerSourcesEvent.ahk +#Include Volantis.Entity\Event\EntityListEvent.ahk #Include Volantis.Entity\Event\EntityReferenceEvent.ahk #Include Volantis.Entity\Event\EntityRefreshEvent.ahk -#Include Volantis.Entity\Event\EntityStorageEvent.ahk #Include Volantis.Entity\Event\EntityValidateEvent.ahk #Include Volantis.Entity\Events\EntityEvents.ahk #Include Volantis.Entity\Exception\EntityException.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk new file mode 100644 index 00000000..2a5bf92c --- /dev/null +++ b/Lib/Shared/Volantis.Entity/Event/EntityDetectValuesEvent.ahk @@ -0,0 +1,17 @@ +class EntityDetectValuesEvent extends EntityEvent { + _valuesMap := "" + + Values { + get => this._valuesMap + } + + __New(eventName, entityTypeId, entityObj, values := "") { + if (!values) { + values := Map() + } + + this._valuesMap := values + + super.__New(eventName, entityTypeId, entityObj) + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk new file mode 100644 index 00000000..f8431925 --- /dev/null +++ b/Lib/Shared/Volantis.Entity/Event/EntityLayerSourcesEvent.ahk @@ -0,0 +1,13 @@ +class EntityLayerSourcesEvent extends EntityEvent { + _layerSourcesObj := "" + + LayerSources { + get => this._layerSourcesObj + } + + __New(eventName, entityTypeId, entityObj, layerSourcesObj) { + this._layerSourcesObj := layerSourcesObj + + super.__New(eventName, entityTypeId, entityObj) + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk new file mode 100644 index 00000000..9782b0be --- /dev/null +++ b/Lib/Shared/Volantis.Entity/Event/EntityListEvent.ahk @@ -0,0 +1,32 @@ +class EntityListEvent extends EventBase { + _entityTypeId := "" + _entityList := [] + _includeManaged := false + _includeExtended := false + + __New(eventName, entityTypeId, entityList, includeManaged, includeExtended) { + this._entityTypeId := entityTypeId + this._entityList := entityList + this._includeManaged := includeManaged + this._includeExtended := includeExtended + + super.__New(eventName) + } + + EntityTypeId { + get => this._entityTypeId + } + + EntityList { + get => this._entityList + set => this._entityList := value + } + + IncludeManaged { + get => this._includeManaged + } + + IncludeExtended { + get => this._includeExtended + } +} diff --git a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk deleted file mode 100644 index 643d20fe..00000000 --- a/Lib/Shared/Volantis.Entity/Event/EntityStorageEvent.ahk +++ /dev/null @@ -1,13 +0,0 @@ -class EntityStorageEvent extends EntityEvent { - _storageObj := "" - - Storage { - get => this._storageObj - } - - __New(eventName, entityTypeId, entityObj, storageObj) { - this._storageObj := storageObj - - super.__New(eventName, entityTypeId, entityObj) - } -} diff --git a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk index 1076b822..3e0a883b 100644 --- a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk +++ b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk @@ -1,6 +1,6 @@ class EntityEvents { - static ENTITY_STORAGE_OBJECTS := 0x4020 - static ENTITY_STORAGE_OBJECTS_ALTER := 0x4022 + static ENTITY_LAYER_SOURCES := 0x4020 + static ENTITY_LAYER_SOURCES_ALTER := 0x4022 static ENTITY_DATA_PROCESSORS := 0x4030 static ENTITY_DATA_PROCESSORS_ALTER := 0x4032 static ENTITY_PREPARE := 0x4035 @@ -15,10 +15,13 @@ class EntityEvents { static ENTITY_REFRESH := 0x4065 static ENTITY_DATA_LAYERS := 0x4070 static ENTITY_DATA_LAYERS_ALTER := 0x4071 + static ENTITY_DETECT_VALUES := 0x4075 + static ENTITY_DETECT_VALUES_ALTER := 0x4076 static ENTITY_VALIDATE := 0x4080 static ENTITY_FIELD_DEFINITIONS := 0x4085 static ENTITY_FIELD_DEFINITIONS_ALTER := 0x4087 static ENTITY_FIELD_GROUPS := 0x4090 static ENTITY_FIELD_GROUPS_ALTER := 0x4092 static ENTITY_REFERENCE_ENTITY_SAVED := 0x4095 + static ENTITY_LIST_ENTITIES := 0x4098 } diff --git a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk index 2a3c041d..3086142b 100644 --- a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk +++ b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk @@ -11,23 +11,23 @@ class EntityData extends LayeredDataBase { super.__New( entity.cloner, this._createProcessors(), - this._getLayerNames(layerNames), - this._collectEntityStorage(layerSources) + this._collectLayerNames(layerNames), + this._collectSources(layerSources) ) } - _collectEntityStorage(layerSources) { + _collectSources(layerSources) { if (!layerSources.Has("defaults")) { layerSources["defaults"] := ObjBindMethod(this.entity, "InitializeDefaults") } - event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS, this.entityTypeId, this.entity, layerSources) + event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES, this.entityTypeId, this.entity, layerSources) this.eventMgr.DispatchEvent(event) - event := EntityStorageEvent(EntityEvents.ENTITY_STORAGE_OBJECTS_ALTER, this.entityTypeId, this.entity, event.Storage) + event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES_ALTER, this.entityTypeId, this.entity, event.LayerSources) this.eventMgr.DispatchEvent(event) - return event.Storage + return event.LayerSources } _createProcessors() { @@ -45,7 +45,7 @@ class EntityData extends LayeredDataBase { return event.Processors } - _getLayerNames(layerNames) { + _collectLayerNames(layerNames) { if (!layerNames) { layerNames := [] } From e4d0db897ad0ce8b05a8d12d9af8a3d24e17f8d4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 02:50:53 -0500 Subject: [PATCH 074/138] wording change --- Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index 56d06d51..423b9889 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -8,7 +8,7 @@ * * Example: * - Layer 1: Initial defaults - * - Layer 2: Defaults from datasource + * - Layer 2: Defaults from external sources * - Layer 3: Auto-detected defaults * - Layer 4: User configuration values * - Processor 1: Token expander From 3eb32cc5ad686b640515de83b67c80c45f4e0954 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 03:02:09 -0500 Subject: [PATCH 075/138] Remove api_authentication and data_source_key config parameters --- Launchpad.services.json | 2 -- Lib/Launchpad/Gui/Form/SettingsWindow.ahk | 4 ---- Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 2 -- Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json | 2 -- 4 files changed, 10 deletions(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index a4fff85b..f4f23a8d 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -9,7 +9,6 @@ "app.about_window": "AboutWindow", "app.show_website_menu_item": true, "backups_config": {}, - "config.api_authentication": false, "config.assets_dir": "@@{data_dir}\\Launcher Assets", "config.auto_backup_config_files": true, "config.backups_to_keep": 5, @@ -18,7 +17,6 @@ "config.clean_launchers_on_build": false, "config.clean_launchers_on_exit": true, "config.create_desktop_shortcuts": true, - "config.data_source_key": "launchpad_api", "config.default_launcher_theme": "", "config.destination_dir": "@@{data_dir}\\Launchers", "config.launcher_double_click_action": "Edit", diff --git a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk index 605f639f..ecf2c0cd 100644 --- a/Lib/Launchpad/Gui/Form/SettingsWindow.ahk +++ b/Lib/Launchpad/Gui/Form/SettingsWindow.ahk @@ -122,10 +122,6 @@ ctl := this.guiObj.AddDDL("vlogging_level xs y+m Choose" . chosen . " w200 c" . this.themeObj.GetColor("editText"), this.container.Get("logger").GetLogLevels()) ctl.OnEvent("Change", "OnLoggingLevelChange") - this.AddHeading("API Settings") - ctl := this.AddConfigCheckBox("Enable API login for enhanced functionality", "api_authentication") - ctl.ctl.NeedsRestart := true - tabs.UseTab() closeW := 100 diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 13f7d8a8..4b82e62d 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -2,8 +2,6 @@ class LaunchpadBuilder extends AppBase { GetParameterDefinitions(config) { parameters := super.GetParameterDefinitions(config) parameters["config_path"] := this.appDir . "\Launchpad.build.json" - parameters["config.data_source_key"] := "" - parameters["config.api_authentication"] := true parameters["config.dist_dir"] := this.appDir . "\Dist" parameters["config.build_dir"] := this.appDir . "\Build" parameters["config.icon_file"] := this.appDir . "\Resources\Graphics\launchpad.ico" diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index 2830ad34..ce2d9c31 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -16,8 +16,6 @@ }, "parameters": { "app.supports_update_check": true, - "config.data_source_key": "launchpad_api", - "config.api_authentication": true, "web_services.providers.launchpad_api": { "name": "Launchpad API", "EndpointUrl": "https://api.launchpad.games/v1", From db232b16d9dbdaf68a7d593b362be2a104a9e72f Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 03:07:12 -0500 Subject: [PATCH 076/138] Remove unused dataSource assignments --- Lib/Launchpad/Gui/Dialog/LoginWindow.ahk | 1 - .../WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk | 1 - Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk | 1 - 3 files changed, 3 deletions(-) diff --git a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk b/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk index 66b256e0..7edc0d12 100644 --- a/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk +++ b/Lib/Launchpad/Gui/Dialog/LoginWindow.ahk @@ -2,7 +2,6 @@ entityObj := "" entityManager := "" missingFields := Map() - dataSource := "" GetDefaultConfig(container, config) { defaults := super.GetDefaultConfig(container, config) diff --git a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk index d6ef8ff1..0145f0cb 100644 --- a/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/AuthenticationGui/LaunchpadLoginWindow.ahk @@ -2,7 +2,6 @@ entityObj := "" entityManager := "" missingFields := Map() - dataSource := "" GetDefaultConfig(container, config) { defaults := super.GetDefaultConfig(container, config) diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk index 73d274dc..f5a6a6cd 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/Dialog/EntityDeleteWindow.ahk @@ -2,7 +2,6 @@ entityObj := "" entityManager := "" missingFields := Map() - dataSource := "" __New(container, themeObj, config, entityObj, entityManager) { this.entityObj := entityObj From 484b200f47d9715295b8e54fb6c2ce05ebc3fb35 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 03:30:45 -0500 Subject: [PATCH 077/138] Add entity_lookup data type to WebServices module --- Lib/Shared/Modules/WebServices/WebServices.module.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index ec745348..00854c63 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -66,6 +66,10 @@ "name": "Entity List", "description": "A listing of entities from a web service." }, + "web_services.data_types.entity_lookup": { + "name": "Entity Lookup", + "description": "Searches for the ID of a remote entity from the provided data." + }, "web_services.data_types.entity_data": { "name": "Entity Data", "description": "Data to be imported into an entity within the application." From 8cc332f2fe1b6917f6b54b9075fb2552df618723 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 03:33:44 -0500 Subject: [PATCH 078/138] Lots of interconnected changes to replace DataSource and DSItem concepts with WebServices --- Launchpad.services.json | 4 - Lib/Launchpad/Entity/LauncherEntity.ahk | 99 +--- Lib/Launchpad/Entity/ManagedEntityBase.ahk | 39 +- Lib/Launchpad/Entity/ManagedGameEntity.ahk | 3 +- .../Entity/ManagedLauncherEntity.ahk | 1 - Lib/Launchpad/Entity/ManagedProcessEntity.ahk | 533 ++++++++++++++++++ Lib/Launchpad/Entity/PlatformEntity.ahk | 3 +- .../GamePlatform/GamePlatformBase.ahk | 60 +- Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk | 54 +- .../Gui/Form/LauncherCreateFormBase.ahk | 10 +- .../Gui/ManageWindow/DetectedGamesWindow.ahk | 4 +- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 12 +- Lib/Launchpad/Includes.ahk | 1 + Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk | 5 - Lib/Shared/Includes.ahk | 9 - .../LaunchpadApi/DataSource/ApiDataSource.ahk | 121 ---- .../LaunchpadApiSubscriber.ahk | 234 +++++++- .../LaunchpadApi/LaunchpadApi.module.json | 19 +- .../WebServices/Entity/WebServiceEntity.ahk | 25 +- .../Entity/WebServiceProviderEntity.ahk | 4 +- .../WebServices/Gui/Form/FeedbackWindow.ahk | 2 +- .../WebServiceAdapterBase.ahk | 103 +++- .../WebServiceRequestBase.ahk | 4 + .../DataSource/DataSourceBase.ahk | 70 --- .../DataSourceItem/DSAssetFile.ahk | 3 - .../Volantis.App/DataSourceItem/DSFile.ahk | 7 - .../Volantis.App/DataSourceItem/DSJson.ahk | 17 - .../Volantis.App/DataSourceItem/DSListing.ahk | 7 - .../DataSourceItem/DataSourceItemBase.ahk | 53 -- .../Volantis.App/Entity/AppEntityBase.ahk | 204 ------- .../Volantis.App/Entity/BackupEntity.ahk | 13 +- Lib/Shared/Volantis.App/Entity/TaskEntity.ahk | 2 +- .../Volantis.App/Gui/Dialog/ErrorDialog.ahk | 32 +- .../Gui/Dialog/UpdateAvailableWindow.ahk | 8 +- .../Gui/EntityEditor/EntityEditorBase.ahk | 6 - .../Volantis.App/GuiControl/EntityControl.ahk | 4 +- .../ComponentManager/DataSourceManager.ahk | 61 -- .../Volantis.Entity/Entity/EntityBase.ahk | 71 ++- .../Entity/FieldableEntity.ahk | 23 +- .../EntityManager/EntityManagerBase.ahk | 26 + .../EntityStorage/EntityStorageBase.ahk | 2 +- 41 files changed, 1143 insertions(+), 815 deletions(-) create mode 100644 Lib/Launchpad/Entity/ManagedProcessEntity.ahk delete mode 100644 Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk delete mode 100644 Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk delete mode 100644 Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk delete mode 100644 Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk diff --git a/Launchpad.services.json b/Launchpad.services.json index f4f23a8d..bf32f18e 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -131,10 +131,6 @@ "class": "BuilderManager", "arguments": ["@entity_manager.launcher", "@{}", "@manager.event", "@notifier"] }, - "manager.data_source": { - "class": "DataSourceManager", - "arguments": ["@{}", "@manager.event", "@notifier", "@@config.data_source_key"] - }, "state.app": { "class": "LaunchpadAppState", "arguments": ["@{App}", "@@state_path"] diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index 9477392c..ab663653 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -1,5 +1,4 @@ -class LauncherEntity extends AppEntityBase { - dataSourcePath := "games" +class LauncherEntity extends FieldableEntity { configPrefix := "Launcher" additionalManagedLauncherDefaults := Map() @@ -29,6 +28,11 @@ class LauncherEntity extends AppEntityBase { "weight", 80 ) + groups["advanced"] := Map( + "name", "Advanced", + "weight", 100 + ) + return groups } @@ -47,18 +51,12 @@ class LauncherEntity extends AppEntityBase { definitions["id"]["modes"]["wizard"]["formField"] := true definitions["id"]["modes"]["wizard"]["widget"] := "combo" - definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListKnownGames") + definitions["id"]["modes"]["wizard"]["selectOptionsCallback"] := ObjBindMethod(this, "ListEntities", false, true) definitions["id"]["modes"]["wizard"]["description"] := "Select an existing game from the API, or enter a custom game key to create your own." definitions["name"]["description"] := "You can change the display name of the game if it differs from the key." definitions["name"]["help"] := "The launcher filename will still be created using the key." - if (definitions.Has("DataSourceItemKey")) { - definitions["DataSourceItemKey"]["default"] := "" - definitions["DataSourceItemKey"]["description"] := "The key to use when looking this item up in its data source(s)." - definitions["DataSourceItemKey"]["help"] := "By default, this is the same as the main key." - } - definitions["Platform"] := Map( "type", "entity_reference", "entityType", "platform", @@ -295,6 +293,15 @@ class LauncherEntity extends AppEntityBase { "help", "If the Steam Overlay attaches within this time, and the Force option is not active, then the Launchpad Overlay will not be used." ) + definitions["AssetsDir"] := Map( + "type", "directory", + "description", "The directory where any required assets for this launcher will be saved.", + "default", this.app.Config["assets_dir"] . "\" . this.Id, + "group", "advanced", + "formField", false, + "modes", Map("simple", Map("formField", false)) + ) + return definitions } @@ -305,13 +312,6 @@ class LauncherEntity extends AppEntityBase { return (FileExist(this.GetLauncherFile(this.Id, checkSourceFile)) != "") } - ListKnownGames() { - return this.container - .Get("manager.data_source") - .GetDefaultDataSource() - .ReadListing("game-keys") - } - LauncherIsOutdated() { outdated := true @@ -384,71 +384,12 @@ class LauncherEntity extends AppEntityBase { this.app.State.SetLauncherConfigInfo(this.Id) } - DiscoverDataSourceItemKey() { - if (!this["DataSourceItemKey"]) { - dataSources := this.GetAllDataSources() - - for index, dataSource in dataSources { - platform := this["Platform"] ? this["Platform"]["id"] : "" - apiPath := "lookup/" this.Id - - if (platform) { - apiPath .= "/" . platform - } - - dsData := dataSource.ReadJson(apiPath) - - if (dsData != "" && dsData.Has("id") && dsData["id"]) { - this["DataSourceItemKey"] := dsData["id"] - break - } - } - } - - if (this["DataSourceItemKey"]) { - return this["DataSourceItemKey"] - } else { - return "" - } - } - IconFileExists() { - iconSrc := this["IconSrc"] != "" ? this["IconSrc"] : this.GetAssetPath(this.Id . ".ico") - return FileExist(iconSrc) - } - - MergeAdditionalDataSourceDefaults(defaults, dataSourceData) { - launcherType := this.DetectLauncherType(defaults, dataSourceData) - - checkType := (launcherType == "") ? "Default" : launcherType - if (dataSourceData.Has("Launchers") && dataSourceData["Launchers"].Has(checkType) && HasBase(dataSourceData["Launchers"][checkType], Map.Prototype)) { - this.additionalManagedLauncherDefaults := this.merger.Merge(dataSourceData["Launchers"][checkType], this.additionalManagedLauncherDefaults) - defaults := this.merger.Merge(defaults, dataSourceData["Launchers"][checkType]) - } - - defaults["ManagedLauncher"] := launcherType + iconSrc := (this["IconSrc"] != "") + ? this["IconSrc"] + : this["AssetsDir"] . "\" . this.Id . ".ico" - return defaults - } - - DetectLauncherType(defaults, dataSourceData := "") { - launcherType := "" - - if (this.UnmergedFieldData.Has("LauncherType")) { - launcherType := this.UnmergedFieldData["LauncherType"] - } else if (defaults.Has("LauncherType")) { - launcherType := defaults["LauncherType"] - } - - if (launcherType == "") { - launcherType := "Default" - } - - if (dataSourceData != "" && dataSourceData.Has("Launchers")) { - launcherType := this._dereferenceKey(launcherType, dataSourceData["Launchers"]) - } - - return launcherType + return FileExist(iconSrc) } _dereferenceKey(key, map) { diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/Lib/Launchpad/Entity/ManagedEntityBase.ahk index 99926db4..b7be2261 100644 --- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk +++ b/Lib/Launchpad/Entity/ManagedEntityBase.ahk @@ -1,4 +1,4 @@ -class ManagedEntityBase extends AppEntityBase { +class ManagedEntityBase extends FieldableEntity { defaultType := "Default" defaultClass := "Default" @@ -54,7 +54,7 @@ class ManagedEntityBase extends AppEntityBase { "required", true, "storageKey", this.configPrefix . "Type", "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListEntityTypes"), + "selectOptionsCallback", ObjBindMethod(this, "ListEntities", false, true), "group", "general" ) @@ -316,10 +316,6 @@ class ManagedEntityBase extends AppEntityBase { return "" } - DiscoverDataSourceItemKey() { - return this["EntityType"] - } - AutoDetectValues(recurse := true) { detectedValues := super.AutoDetectValues(recurse) processId := "" @@ -347,31 +343,6 @@ class ManagedEntityBase extends AppEntityBase { return detectedValues } - ListEntityTypes() { - types := [] - dataSources := this.GetAllDataSources() - dsPath := this.GetDataSourceItemPath() - - for index, dataSource in dataSources { - for listingIndex, listingItem in dataSource.ReadListing(dsPath) { - exists := false - - for index, item in types { - if (item == listingItem) { - exists := true - break - } - } - - if (!exists) { - types.Push(listingItem) - } - } - } - - return types - } - ListRunTypes() { return [ "Command", @@ -428,12 +399,14 @@ class ManagedEntityBase extends AppEntityBase { } ShortcutFileExists() { - shortcutSrc := this["ShortcutSrc"] != "" ? this["ShortcutSrc"] : this.GetAssetPath(this.Id . ".lnk") + shortcutSrc := (this["ShortcutSrc"] != "") + ? this["ShortcutSrc"] + : this["AssetsDir"] . "\" . this.Id . ".lnk" exists := FileExist(shortcutSrc) if (!exists) { - shortcutSrc := this.GetAssetPath(this.Id . ".url") + shortcutSrc := this["AssetsDir"] . "\" . this.Id . ".url" exists := FileExist(shortcutSrc) } diff --git a/Lib/Launchpad/Entity/ManagedGameEntity.ahk b/Lib/Launchpad/Entity/ManagedGameEntity.ahk index 907991e6..235010cc 100644 --- a/Lib/Launchpad/Entity/ManagedGameEntity.ahk +++ b/Lib/Launchpad/Entity/ManagedGameEntity.ahk @@ -2,7 +2,6 @@ class ManagedGameEntity extends ManagedEntityBase { configPrefix := "Game" defaultType := "Default" defaultClass := "SimpleGame" - dataSourcePath := "game-types" BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() @@ -99,7 +98,7 @@ class ManagedGameEntity extends ManagedEntityBase { } if (this.ShouldDetectShortcutSrc(detectedValues)) { - basePath := this["AssetsDir"] . "\" . this.Id + basePath := this.ParentEntity["AssetsDir"] . "\" . this.Id shortcutSrc := "" if (FileExist(basePath . ".lnk")) { diff --git a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk b/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk index c060cb91..12d5eb03 100644 --- a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk +++ b/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk @@ -2,7 +2,6 @@ class ManagedLauncherEntity extends ManagedEntityBase { configPrefix := "Launcher" defaultType := "Default" defaultClass := "SimpleLauncher" - dataSourcePath := "launcher-types" BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() diff --git a/Lib/Launchpad/Entity/ManagedProcessEntity.ahk b/Lib/Launchpad/Entity/ManagedProcessEntity.ahk new file mode 100644 index 00000000..4ff6778c --- /dev/null +++ b/Lib/Launchpad/Entity/ManagedProcessEntity.ahk @@ -0,0 +1,533 @@ +class ManagedProcessEntity extends FieldableEntity { + defaultType := "Default" + defaultClass := "Default" + + DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { + return container.Get("entity_manager.launcher")[id] + } + + GetDefaultFieldGroups() { + groups := super.GetDefaultFieldGroups() + + groups["locations"] := Map( + "name", "Locations", + "weight", 100 + ) + + groups["registry"] := Map( + "name", "Registry", + "weight", 125 + ) + + groups["process"] := Map( + "name", "Process", + "weight", 150 + ) + + return groups + } + + BaseFieldDefinitions() { + definitions := super.BaseFieldDefinitions() + + definitions["name"]["formField"] := false + + definitions["Launcher"] := Map( + "storageKey", "", + "type", "entity_reference", + "entityType", "launcher", + "required", true, + "formField", false, + "callbacks", Map( + "GetValue", ObjBindMethod(this, "GetId"), + "SetValue", ObjBindMethod(this, "SetId"), + "HasValue", ObjBindMethod(this, "HasId"), + "HasOverride", ObjBindMethod(this, "HasId"), + "IsEmpty", ObjBindMethod(this, "HasId", true), + "DeleteValue", "" + ) + ) + + definitions["EntityType"] := Map( + "default", this.defaultType, + "description", "The key of the managed type to load settings and defaults from.", + "required", true, + "storageKey", this.configPrefix . "Type", + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListEntities", false, true), + "group", "general" + ) + + definitions["EntityClass"] := Map( + "default", this.defaultClass, + "description", "The name of the AHK class that will be used to control the managed entity.", + "formField", false, + "storageKey", this.configPrefix . "Class", + "required", true, + "group", "advanced", + "modes", Map( + "simple", Map("formField", false) + ), + ) + + definitions["SearchDirs"] := Map( + "type", "directory", + "mustExist", false, + "storageKey", this.configPrefix . "SearchDirs", + "default", [A_ProgramFiles], + "description", "Possible parent directories where the game's launcher might exist, to be used for auto-detection.", + "help", "These should be as specific as possible to reduce detection time.", + "multiple", true, + "group", "locations", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["InstallDir"] := Map( + "type", "directory", + "mustExist", false, + "storageKey", this.configPrefix . "InstallDir", + "group", "locations", + "modes", Map( + "simple", Map("group", "general") + ), + "description", "Select the installation folder, or use default for auto-detection." + ) + + definitions["Exe"] := Map( + "type", "file", + "fileMask", "*.exe", + "mustExist", false, + "storageKey", this.configPrefix . "Exe", + "description", "This can be the full path on the system to the launcher's .exe file, or simply the name of the .exe file itself.", + "help", "If the .exe doesn't include the absolute path, auto-detection will be used by searching the DestinationDirs.", + "group", "locations", + "modes", Map( + "simple", Map("group", "general") + ) + ) + + ; Options include: + ; - Search (will search through each directory in SearchDirs until a match is found) + ; - BlizzardProductDb (will search Battle.net's product.db file if it can be located for the installation directory, and the file will be found from there + ; - Registry (will get a directory from the registry key specified by LocateRegKey and search for the file within it) + definitions["LocateMethod"] := Map( + "storageKey", this.configPrefix . "LocateMethod", + "default", "SearchDirs", + "description", "How to search for the .exe if it isn't a full path already", + "group", "general", + "modes", Map( + "simple", Map("formField", false) + ), + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"), + "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for LauncherSpecificId within the Blizzard product.db file if present" + ) + + definitions["WindowTitle"] := Map( + "storageKey", this.configPrefix . "WindowTitle", + "group", "process" + ) + + definitions["LocateRegView"] := Map( + "storageKey", this.configPrefix . "LocateRegView", + "default", 64, + "group", "registry", + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListRegViews"), + "description", "The registry view to use when locating the install dir.", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LocateRegKey"] := Map( + "storageKey", this.configPrefix . "LocateRegKey", + "group", "registry", + "description", "The registry key to look up the install dir within.", + "help", "Path parts should be separated with backslashes and must start with one of: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, or the abbreviation of one of those. To read from a remote registry, prefix the root path with two backslashes and the computer name.`n`nSimple example: HKLM\Path\To\Key`nRemote example: \\OTHERPC\HKLM\Path\To\Key", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LocateRegValue"] := Map( + "storageKey", this.configPrefix . "LocateRegValue", + "group", "registry", + "description", "The name of the registry value to look up within the specified key.", + "help", "Example: InstallPath", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LocateRegRemovePrefix"] := Map( + "storageKey", this.configPrefix . "LocateRegRemovePrefix", + "group", "registry", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LocateRegRemoveSuffix"] := Map( + "storageKey", this.configPrefix . "LocateRegRemoveSuffix", + "group", "registry", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LocateRegStripQuotes"] := Map( + "storageKey", this.configPrefix . "LocateRegStripQuotes", + "default", false, + "group", "registry", + "description", "Strip quotes from registry value", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["LauncherSpecificId"] := Map( + "storageKey", this.configPrefix . "LauncherSpecificId", + "description", "If the item is known to the launcher by a specific ID, it should be stored here.", + "group", "general" + ) + + definitions["WorkingDir"] := Map( + "type", "directory", + "description", "The directory that the launcher should be run from.", + "help", "If not set, it will be run without setting an explicit working directory, which is usually sufficient.", + "storageKey", this.configPrefix . "WorkingDir", + "group", "locations", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + ; - Shortcut (Run a shortcut file) + ; - Command (Run a command directly, the default if required) + definitions["RunType"] := Map( + "description", "Which method to use for launching this item.", + "help", "This is only needed for launchers that have to manage their own process.", + "storageKey", this.configPrefix . "RunType", + "default", "Command", + "group", "process", + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListRunTypes") + ) + + definitions["UsesShortcut"] := Map( + "type", "boolean", + "description", "Whether a shortcut file will be used when starting the internally-managed game launcher", + "formField", false, + "storageKey", this.configPrefix . "UsesShortcut" + ) + + definitions["ReplaceProcess"] := Map( + "type", "boolean", + "description", "Kill and re-launch the game process immediately after it is detected.", + "help", "This can be used to force Launchpad to own the game process, but won't for for every game.", + "storageKey", this.configPrefix . "ReplaceProcess", + "default", false, + "group", "process" + ) + + ; - The filename of an existing shortcut (.url or .lnk file, or even another .exe) that will be used to run the game. + ; - The path of another shortcut file (.url or .lnk) on the system, which will be copied to the AssetsDir if it doesn't already exist + ; - The path of an .exe file on the system to which a shortcut will be created in AssetsDir if it doesn't already exist. Using this option + ; is usually not necessary, since you can run the .exe directly instead. + definitions["ShortcutSrc"] := Map( + "description", "The shortcut file used to launch the game launcher itself.", + "help", "This is typically only needed if the Shortcut LauncherRunType is selected.", + "storageKey", this.configPrefix . "ShortcutSrc", + "group", "locations", + "modes", Map( + "simple", Map("group", "general") + ) + ) + + ; - RunWait (the default, uses RunWait to both run a process and wait until it completes in one step. This is most efficient if it works.) + ; - Run (Uses Run, then watches for the game window and waits until the window opens (if needed) and then closes) + ; - Scheduled (Creates an immediate scheduled task that runs the game, then waits until the window opens (if needed) and then closes) + definitions["RunMethod"] := Map( + "description", "Which method to use to run the RunCmd", + "storageKey", this.configPrefix . "RunMethod", + "default", "Run", + "group", "process", + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListRunMethods") + ) + + ; - "Exe" (Waits for the game's .exe process to start if it hasn't already, and then waits for it to stop again. This is the default if the game type is not RunWait) + ; - "Title" (Waits for the game's window title to open if it isn't already, and then waits for it to close again) + ; - "Class" (Wait's for the game's window class to open if it isn't already, and then waits for it to close again) + definitions["ProcessType"] := Map( + "description", "Which method to use to wait for the game to close.", + "help", "This is not needed if the GameRunType is RunWait", + "storageKey", this.configPrefix . "ProcessType", + "default", "Exe", + "group", "process", + "widget", "select", + "selectOptionsCallback", ObjBindMethod(this, "ListProcessTypes") + ) + + ; - Exe - This value will default to the GameExe unless overridden + ; - Title - This value will default to the game's Key unless overridden + ; - Class - This value should be set to the game's window class + definitions["ProcessId"] := Map( + "help", "This value's type is dependent on the ProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", + "storageKey", this.configPrefix . "ProcessId", + "group", "process", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["ProcessTimeout"] := Map( + "description", "The number of seconds to wait before giving up when waiting for a process.", + "storageKey", this.configPrefix . "ProcessTimeout", + "default", 30, + "group", "process", + "modes", Map( + "simple", Map("formField", false) + ) + ) + + definitions["RunCmd"] := Map( + "description", "The command that will be used to run the game's launcher.", + "help", "Typically only used if LauncherRunType is Command.", + "storageKey", this.configPrefix . "RunCmd", + "group", "process" + ) + + return definitions + } + + GetData() { + if (!this.ParentEntity) { + throw EntityException("A parent entity is required on type " . Type(this)) + } + + return this.ParentEntity.GetData() + } + + _createEntityData() { + return "" + } + + AutoDetectValues(recurse := true) { + detectedValues := super.AutoDetectValues(recurse) + processId := "" + usesShortcut := false + + if (this.GetData().HasValue(this.configPrefix . "UsesShortcut")) { + usesShortcut := this.GetData().GetValue(this.configPrefix . "UsesShortcut") + } else { + usesShortcut := (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") + } + + detectedValues[this.configPrefix . "UsesShortcut"] := usesShortcut + detectedValues[this.configPrefix . "RunType"] := usesShortcut ? "Shortcut" : "Command" + detectedValues[this.configPrefix . "InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir + + if (this["ProcessType"] == "Exe") { + SplitPath(this["Exe"], &processId) + } else if (this["ProcessType"] == "Title") { + processId := this["WindowTitle"] ? this["WindowTitle"] : this.Id + } + + detectedValues[this.configPrefix . "ProcessId"] := processId + detectedValues[this.configPrefix . "WorkingDir"] := this["InstallDir"] + + return detectedValues + } + + ListRunTypes() { + return [ + "Command", + "Shortcut" + ] + } + + ListProcessTypes() { + return [ + "Exe", "Title", "Class" + ] + } + + ListRunMethods() { + return [ + "Run", "Scheduled", "RunWait" + ] + } + + ListLocateMethods() { + return [ + "Search", "Registry", "BlizzardProductDb" + ] + } + + ListRegViews() { + regViews := [ + "32" + ] + + if (A_Is64bitOS) { + regViews.Push("64") + } + + return regViews + } + + Validate() { + validateResult := super.Validate() + + if (((this["UsesShortcut"] && this["RunCmd"] == "") && this["ShortcutSrc"] == "") && !this.ShortcutFileExists()) { + validateResult["success"] := false + validateResult["invalidFields"].push("ShortcutSrc") + } + + if (this["ShortcutSrc"] == "" && this["RunCmd"] == "") { + validateResult["success"] := false + validateResult["invalidFields"].push("RunCmd") + } + + ; TODO: Perform more launcher and game type validation here + + return validateResult + } + + ShortcutFileExists() { + shortcutSrc := (this["ShortcutSrc"] != "") + ? this["ShortcutSrc"] + : this["AssetsDir"] . "\" . this.Id . ".lnk" + + exists := FileExist(shortcutSrc) + + if (!exists) { + shortcutSrc := this["AssetsDir"] . "\" . this.Id . ".url" + exists := FileExist(shortcutSrc) + } + + return exists + } + + LocateInstallDir() { + installDir := "" + + ; TODO: Add additional methods to detect the install dir + + if (this["LocateMethod"] == "BlizzardProductDb") { + blizzardDir := this.GetBlizzardProductDir() + + if (blizzardDir != "") { + installDir := blizzardDir + } + } + + return installDir + } + + LocateExe() { + return this.LocateFile(this["Exe"]) + } + + LocateFile(filePattern) { + filePath := "" + + if (filePattern != "") { + SplitPath(filePattern,,,,, &fileDrive) + + if (fileDrive != "") { + filePath := filePattern + } else { + searchDirs := [] + + if (this["InstallDir"] != "") { + searchDirs.Push(this["InstallDir"]) + } else if (this["LocateMethod"] == "SearchDirs") { + if (HasBase(this["SearchDirs"], Array.Prototype) && this["SearchDirs"].Length > 0) { + for index, dir in this["SearchDirs"] { + searchDirs.Push(dir) + } + } + } else if (this["LocateMethod"] == "Registry") { + regKey := this["LocateRegKey"] + + if (regKey != "") { + SetRegView(this["LocateRegView"]) + regDir := RegRead(this["LocateRegKey"], this["LocateRegValue"]) + SetRegView("Default") + + if (regDir != "") { + if (this["LocateRegStripQuotes"]) { + regDir := StrReplace(regDir, "`"", "") + } + + if (this["LocateRegRemovePrefix"] && SubStr(regDir, 1, StrLen(this["LocateRegRemovePrefix"])) == this["LocateRegRemovePrefix"]) { + regDir := SubStr(regDir, StrLen(this["LocateRegRemovePrefix"]) + 1) + } + + if (this["LocateRegRemoveSuffix"] && SubStr(regDir, 1, StrLen(this["LocateRegRemoveSuffix"])) == this["LocateRegRemoveSuffix"]) { + regDir := StrReplace(regDir, StrLen(this["LocateRegRemoveSuffix"]) + 1) + } + + searchDirs.Push(regDir) + } + } + } else if (this["LocateMethod"] == "BlizzardProductDb") { + blizzardDir := this.GetBlizzardProductDir() + + if (blizzardDir != "") { + searchDirs.Push(blizzardDir) + } + } + + filePath := this.LocateFileInSearchDirs(filePattern, searchDirs) + } + } + + return filePath + } + + LocateFileInSearchDirs(filePattern, searchDirs := "") { + path := "" + + if (searchDirs == "") { + searchDirs := this["SearchDirs"].Clone() + } + + if (!HasBase(searchDirs, Array.Prototype)) { + searchDirs := [searchDirs] + } + + for index, searchDir in searchDirs { + Loop Files, searchDir . "\" . filePattern, "R" { + path := A_LoopFileFullPath + break + } + + if (path != "") { + break + } + } + + return path + } + + GetBlizzardProductKey() { + return "bna" ; Default to the Battle.net client itself + } + + GetBlizzardProductDir() { + path := "" + productCode := this.GetBlizzardProductKey() + + if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) { + path := this.app["BlizzardProductDb"].GetProductInstallPath(productCode) + } + + return path + } +} diff --git a/Lib/Launchpad/Entity/PlatformEntity.ahk b/Lib/Launchpad/Entity/PlatformEntity.ahk index 43a4a1b8..5eacd08a 100644 --- a/Lib/Launchpad/Entity/PlatformEntity.ahk +++ b/Lib/Launchpad/Entity/PlatformEntity.ahk @@ -1,7 +1,6 @@ -class PlatformEntity extends AppEntityBase { +class PlatformEntity extends FieldableEntity { platformObj := "" configPrefix := "" - dataSourcePath := "platforms" Platform { get => this.GetPlatform() diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk index f9d674ef..0476cd6d 100644 --- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk +++ b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk @@ -179,35 +179,53 @@ class GamePlatformBase { } DetermineMainExe(key, possibleExes) { - dataSource := this.app["manager.data_source"].GetDefaultDataSource() - dsData := this.GetDataSourceDefaults(dataSource, key) - mainExe := "" if (possibleExes.Length == 1) { mainExe := possibleExes[1] - } else if (possibleExes.Length > 1 && dsData.Has("GameExe")) { - for index, possibleExe in possibleExes { - SplitPath(possibleExe, &fileName) - - if (dsData["GameExe"] == fileName) { - mainExe := possibleExe - break + } else if (possibleExes.Length > 1) { + ; @todo move the API functionality into a module that depends on WebServices + if (this.app.Services.Has("entity_manager.web_service")) { + mgr := this.app["entity_manager.web_service"] + + if (mgr.Has("launchpad_api")) { + webService := mgr["launchpad_api"] + + resultData := webService.AdapterRequest( + Map("id", key), + Map( + "adapterType", "entity_data", + "entityType", "launcher" + ), + "read", + true + ) + + for key, data in resultData { + if ( + data + && HasBase(data, Map.Prototype) + && data.Has("defaults") + && data["defaults"] + && data["defaults"].Has("GameExe") + && data["defaults"]["GameExe"] + ) { + for index, possibleExe in possibleExes { + SplitPath(possibleExe, &fileName) + + if (data["defaults"]["GameExe"] == fileName) { + mainExe := possibleExe + break 2 + } + } + } + } } } - } - return mainExe - } - - GetDataSourceDefaults(dataSource, key) { - defaults := Map() - dsData := dataSource.ReadJson(key, "Games") - - if (dsData != "" && dsData.Has("data") && dsData["data"].Has("defaults")) { - defaults := this.merger.Merge(dsData["data"]["defaults"], defaults) + } - return defaults + return mainExe } } diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 3513534a..518c04c5 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -2,7 +2,6 @@ detectedGameObj := "" newValues := Map() missingFields := Map() - dataSource := "" knownGames := "" launcherTypes := "" gameTypes := "" @@ -22,11 +21,54 @@ Create() { super.Create() - this.dataSource := this.app["manager.data_source"].GetDefaultDataSource() - this.knownPlatforms := this.dataSource.ReadListing("platforms") - this.knownGames := this.dataSource.ReadListing("game-keys") - this.launcherTypes := this.dataSource.ReadListing("launcher-types") - this.gameTypes := this.dataSource.ReadListing("game-types") + + this.knownPlatforms := [] + this.knownGames := [] + this.launcherTypes := [] + this.gameTypes := [] + + ; @todo replace this, or at least refactor it to live somewhere else + if (this.container.Has("entity_manager.web_service")) { + mgr := this.container["entity_manager.web_service"] + + if (mgr.Has("launchpad_api") && mgr["launchpad_api"]["Enabled"]) { + webService := mgr["launchpad_api"] + knownMap := Map( + "platform", "knownPlatforms", + "game", "knownGames", + "managed_launcher", "launcherTypes", + "managed_game", "gameTypes" + ) + + for entityTypeId, varName in knownMap { + results := webService.AdapterRequest("", Map( + "adapterType", "entity_list", + "entityType", entityTypeId + ), "read", true) + + if (results) { + for , idList in results { + if (idList) { + for , id in idList { + exists := false + + for , item in %varName% { + if (item == id) { + exists := true + break + } + } + + if (!exists) { + this.%varName%.Push(id) + } + } + } + } + } + } + } + } } GetTitle() { diff --git a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk b/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk index dccde664..78d55ef9 100644 --- a/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk +++ b/Lib/Launchpad/Gui/Form/LauncherCreateFormBase.ahk @@ -1,10 +1,12 @@ class LauncherCreateFormBase extends FormGuiBase { knownGames := "" knownPlatforms := "" - dataSource := "" + launcherMgr := "" + platformMgr := "" __New(container, themeObj, config) { - this.dataSource := container.Get("manager.data_source").GetDefaultDataSource() + this.launcherMgr := container.Get("entity_manager.launcher") + this.platformMgr := container.Get("entity_manager.platform") super.__New(container, themeObj, config) } @@ -17,8 +19,8 @@ Create() { super.Create() - this.knownGames := this.dataSource.ReadListing("game-keys") - this.knownPlatforms := this.dataSource.ReadListing("platforms") + this.knownGames := this.launcherMgr.ListEntities(false, true) + this.knownPlatforms := this.platformMgr.ListEntities(false, true) } ProcessResult(result, submittedData := "") { diff --git a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk index 98f10890..6f8aa4ba 100644 --- a/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/DetectedGamesWindow.ahk @@ -12,9 +12,7 @@ this.detectedGames := detectedGames this.state := container.Get("state.app") this.launcherManager := container.Get("entity_manager.launcher") - this.knownGames := container.Get("manager.data_source") - .GetDefaultDataSource() - .ReadListing("game-keys") + this.knownGames := this.launcherManager.ListEntities(false, true) super.__New(container, themeObj, config) } diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 81221664..8137e949 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -18,7 +18,7 @@ defaults["child"] := false defaults["title"] := container.GetApp().appName defaults["titleIsMenu"] := true - defaults["showStatusIndicator"] := !!(container.Get("config.app").Has("api_authentication") && container.Get("config.app")["api_authentication"]) + defaults["showStatusIndicator"] := container.Has("entity_manager.web_service") return defaults } @@ -131,7 +131,9 @@ } status := launcher.GetStatus() - apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked" + + ; @todo Move the API data to an event in the LaunchpadApi module + apiStatus := (launcher.HasField["DataLookupKey"] && launcher["DataLookupKey"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo("Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo("Build")["Timestamp"]) @@ -307,7 +309,9 @@ } status := launcher.GetStatus() - apiStatus := launcher["DataSourceItemKey"] ? "Linked" : "Not linked" + + ; @todo Move the API code to the LaunchpadApi module + apiStatus := (launcher.HasField("DataLookupKey") && launcher["DataLookupKey"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo(key, "Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo(key, "Build")["Timestamp"]) @@ -450,7 +454,7 @@ } entity.SaveEntity() - entity.UpdateDataSourceDefaults() + entity.UpdateDefaults() this.UpdateListView() } } diff --git a/Lib/Launchpad/Includes.ahk b/Lib/Launchpad/Includes.ahk index 775a4966..cec1c809 100644 --- a/Lib/Launchpad/Includes.ahk +++ b/Lib/Launchpad/Includes.ahk @@ -26,6 +26,7 @@ #Include Entity\ManagedEntityBase.ahk #Include Entity\ManagedGameEntity.ahk #Include Entity\ManagedLauncherEntity.ahk +#Include Entity\ManagedProcessEntity.ahk #Include Entity\PlatformEntity.ahk #Include GamePlatform\BasicPlatform.ahk #Include GamePlatform\GamePlatformBase.ahk diff --git a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk index 4b82e62d..6dffed31 100644 --- a/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk +++ b/Lib/LaunchpadBuilder/App/LaunchpadBuilder.ahk @@ -30,11 +30,6 @@ class LaunchpadBuilder extends AppBase { "arguments", [AppRef(), this.appDir . "\" . this.appName . ".ini"] ) - services["manager.data_source"] := Map( - "class", "DataSourceManager", - "arguments", [ContainerRef(), ServiceRef("manager.event"), ServiceRef("notifier"), ParameterRef("config.data_source_key")] - ) - services["FileHasher"] := "FileHasher" services["GitTagVersionIdentifier"] := Map( diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 4f961270..d33ef65f 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -1,5 +1,4 @@ ; Automatically-generated file. Manual edits will be overwritten. -#Include Modules\LaunchpadApi\DataSource\ApiDataSource.ahk #Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk #Include Modules\WebServices\Entity\WebServiceEntity.ahk #Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk @@ -40,13 +39,6 @@ #Include Volantis.App\Config\AppConfig.ahk #Include Volantis.App\Container\ServiceComponentContainer.ahk #Include Volantis.App\Container\WindowContainer.ahk -#Include Volantis.App\DataSource\DataSourceBase.ahk -#Include Volantis.App\DataSourceItem\DataSourceItemBase.ahk -#Include Volantis.App\DataSourceItem\DSAssetFile.ahk -#Include Volantis.App\DataSourceItem\DSFile.ahk -#Include Volantis.App\DataSourceItem\DSJson.ahk -#Include Volantis.App\DataSourceItem\DSListing.ahk -#Include Volantis.App\Entity\AppEntityBase.ahk #Include Volantis.App\Entity\BackupEntity.ahk #Include Volantis.App\Entity\TaskEntity.ahk #Include Volantis.App\Event\AlterComponentsEvent.ahk @@ -106,7 +98,6 @@ #Include Volantis.App\Service\LoggerService.ahk #Include Volantis.App\Service\NotificationService.ahk #Include Volantis.App\Service\ComponentManager\CacheManager.ahk -#Include Volantis.App\Service\ComponentManager\DataSourceManager.ahk #Include Volantis.App\Service\ComponentManager\GuiManager.ahk #Include Volantis.App\Service\ComponentManager\InstallerManager.ahk #Include Volantis.App\Service\ComponentManager\ThemeManager.ahk diff --git a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk b/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk deleted file mode 100644 index 1f84afbd..00000000 --- a/Lib/Shared/Modules/LaunchpadApi/DataSource/ApiDataSource.ahk +++ /dev/null @@ -1,121 +0,0 @@ -class ApiDataSource extends DataSourceBase { - endpointUrl := "" - app := "" - - __New(app, cacheManager, cacheName, endpointUrl) { - this.app := app - InvalidParameterException.CheckTypes("ApiDataSource", "endpointUrl", endpointUrl, "", "cacheManager", cacheManager, "CacheManager") - this.endpointUrl := endpointUrl - super.__New(cacheManager, cacheName) - } - - ItemExists(path) { - return super.ItemExists(path) || this.ItemExistsInApi(path) - } - - ItemExistsInApi(path) { - exists := (this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path)) - - if (!exists) { - request := this.SendHttpReq(path, "HEAD") - - exists := (request.GetStatusCode() == 200) - - if (!exists) { - this.cache.SetNotFound(path) - } - } - - return exists - } - - GetHttpReq(path, private := false) { - request := WinHttpReq(this.GetRemoteLocation(path)) - - if (private) { - request.requestHeaders["Cache-Control"] := "no-cache" - - if (this.app.Config["api_authentication"]) { - entityMgr := webService := this.app["entity_manager.web_service"] - - if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"]) { - webService := this.app["entity_manager.web_service"]["launchpad_api"] - webService["Provider"]["Authenticator"].AlterRequest(webService, request) - } - - } - } - - return request - } - - SendHttpReq(path, method := "GET", data := "", private := false) { - request := this.GetHttpReq(path, private) - returnCode := request.Send(method, data) - return request - } - - GetRemoteLocation(path) { - return this.endpointUrl . "/" . path - } - - RetrieveItem(path, private := false, maxCacheAge := "") { - if (maxCacheAge == "") { - maxCacheAge := this.maxCacheAge - } - - exists := (!private && this.cache.ItemExists(path) && !this.cache.ItemNeedsUpdate(path, maxCacheAge)) - - if (!exists) { - request := this.SendHttpReq(path, "GET", "", private) - - if (request.GetStatusCode() != 200) { - return "" - } - - responseBody := Trim(request.GetResponseData()) - - if (responseBody == "") { - return "" - } - - this.cache.WriteItem(path, responseBody) - } - - return this.cache.ItemExists(path) ? this.cache.ReadItem(path) : "" - } - - GetStatus() { - path := "status" - statusExpire := 5 ;60 - - status := Map("authenticated", false, "account", "", "photo", "") - - if (this.app.Config["api_authentication"]) { - entityMgr := webService := this.app["entity_manager.web_service"] - - if (entityMgr.Has("launchpad_api") && entityMgr["launchpad_api"]["Enabled"] && entityMgr["launchpad_api"]["Authenticated"]) { - statusResult := this.ReadItem(path, true) - - if (statusResult) { - status := JsonData().FromString(&statusResult) - - if (status.Has("email")) { - status["account"] := status["email"] - status.Delete("email") - } - } - } - } - - return status - } - - GetExt(path) { - - } - - Open() { - Run(this.endpointUrl) - } -} diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk index 5d47081a..bc9da846 100644 --- a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -4,24 +4,248 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { Events.APP_GET_RELEASE_INFO, [ ObjBindMethod(this, "GetReleaseInfo") ], + EntityEvents.ENTITY_DATA_LAYERS, [ + ObjBindMethod(this, "EntityDataLayers") + ], + EntityEvents.ENTITY_LAYER_SOURCES, [ + ObjBindMethod(this, "EntityLayerSources") + ], + EntityEvents.ENTITY_FIELD_GROUPS, [ + ObjBindMethod(this, "EntityFieldGroups") + ], + EntityEvents.ENTITY_FIELD_DEFINITIONS, [ + ObjBindMethod(this, "EntityFieldDefinitions") + ], + EntityEvents.ENTITY_DETECT_VALUES, [ + ObjBindMethod(this, "EntityDetectValues") + ], + EntityEvents.ENTITY_LIST_ENTITIES, [ + ObjBindMethod(this, "ListEntities") + ], ) } GetReleaseInfo(event, extra, eventName, hwnd) { releaseInfo := event.ReleaseInfo - if (!event.ReleaseInfo.Count && this.App.Version != "{{VERSION}}") { - webService := this.App["entity_manager.web_service"]["launchpad_api"] + if (!event.ReleaseInfo.Count && this.container.GetApp().Version != "{{VERSION}}") { + webService := this.container["entity_manager.web_service"]["launchpad_api"] if (webService["Enabled"]) { - releaseInfo := webService.AdapterRequest("", "release_info", "read", 1) + releaseInfo := webService.AdapterRequest("", "release_info") - if (releaseInfo && releaseInfo.Has("data")) { - for key, val in releaseInfo["data"] { + if (releaseInfo) { + for key, val in releaseInfo { event.ReleaseInfo[key] = val } } } } } + + EntityDataLayers(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + webService := this.container["entity_manager.web_service"]["launchpad_api"] + + layers := event.Layers + + if (WebService["Enabled"]) { + entity := event.Entity + + adapters := webService.GetAdapters([ + "adapterType", "entity_data", + "entityType", event.EntityTypeId + ]) + + for key, adapter in adapters { + layerExists := false + layerKey := webService["id"] . "." . event.EntityTypeId . "." . key + + for index, layerName in layers { + if (layerName == layerKey) { + layerExists := true + break + } + } + + if (!layerExists) { + layers.Push(layerKey) + } + } + } + } + + EntityLayerSources(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + webService := this.container["entity_manager.web_service"]["launchpad_api"] + + layerData := event.LayerSources + + if (WebService["Enabled"]) { + adapters := webService.GetAdapters([ + "adapterType", "entity_data", + "entityType", event.EntityTypeId + ]) + + for key, adapter in adapters { + layerKey := webService["id"] . "." . event.EntityTypeId . "." . key + + if (!layerData.Has(layerKey)) { + layerData[layerKey] := WebServiceAdapterLayerSource(adapter) + } + } + } + } + + EntityFieldGroups(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + webService := this.container["entity_manager.web_service"]["launchpad_api"] + + if (WebService["Enabled"]) { + fieldGroups := event.FieldGroups + + if (!fieldGroups.Has("api")) { + adapters := webService.GetAdapters([ + "adapterType", "entity_data", + "entityType", event.EntityTypeId + ]) + + if (adapters.Count) { + fieldGroups["api"] := Map( + "name", "API", + "weight", 150 + ) + } + } + } + } + + EntityFieldDefinitions(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + webService := this.container["entity_manager.web_service"]["launchpad_api"] + + if (WebService["Enabled"]) { + fieldDefinitions := event.FieldDefinitions + + adapters := webService.GetAdapters([ + "adapterType", "entity_data", + "entityType", event.EntityTypeId + ]) + + if (adapters.Count) { + fieldDefinitions["DataLookupKey"] := Map( + "description", "The key that is used to look up the entity's data from configured external data sources.", + "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataLookupKey", + "group", "api", + "processValue", false, + "modes", Map("simple", Map("formField", false)) + ) + } + } + } + + EntityDetectValues(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + webService := this.container["entity_manager.web_service"]["launchpad_api"] + values := event.Values + entity := event.Entity + + if ( + webService["Enabled"] + && (!values.Has("DataLookupKey") || !values["DataLookupKey"]) + && entity.HasField("DataLookupKey") + && (!entity.RawData.Has["DataLookupKey"] || !entity.RawData["DataLookupKey"]) + ) { + result := "" + + if (event.EntityTypeId == "Launcher") { + platform := entity["Platform"] ? entity["Platform"]["id"] : "" + + result := webService.AdapterRequest( + Map("id", entity["id"], "platform", platform), + Map( + "adapterType", "entity_list", + "entityType", event.EntityTypeId + ) + ) + } else if (HasBase(entity, ManagedEntityBase.Prototype)) { + result := entity["EntityType"] + } else { + result := entity["id"] + } + + if (result) { + values["DataLookupKey"] := result + } + } + } + + ListEntities(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + if (event.includeExtended) { + webService := this.container["entity_manager.web_service"]["launchpad_api"] + entityMgr := this.container["entity_manager." . event.EntityTypeId] + + managedIds := event.includeManaged + ? [] + : entityMgr.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() + + if (webService["Enabled"]) { + results := webService.AdapterRequest( + "", + Map( + "adapterType", "entity_list", + "entityType", event.EntityTypeId + ), + "read", + true + ) + + if (results && HasBase(results, Array.Prototype)) { + for index, id in results { + exists := false + + for , existingId in event.EntityList { + if (existingId == id) { + exists := true + break + } + } + + if (!exists && !event.includeManaged) { + for , managedId in managedIds { + if (managedId == id) { + exists := true + break + } + } + } + + if (!exists) { + event.EntityList.Push(id) + } + } + } + + } + } + } } diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index ce2d9c31..78ac16ec 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -77,6 +77,7 @@ "web_services.adapters.launchpad_api.platform_data": { "dataType": "entity_data", "requestPath": "/game-platforms/{id}", + "dataSelector": "data.defaults", "entityType": "platform" }, "web_services.adapters.launchpad_api.game_type_list": { @@ -87,6 +88,7 @@ "web_services.adapters.launchpad_api.game_type_data": { "dataType": "entity_data", "requestPath": "/game-types/{id}", + "dataSelector": "data.defaults", "entityType": "managed_game" }, "web_services.adapters.launchpad_api.launcher_list": { @@ -97,6 +99,13 @@ "web_services.adapters.launchpad_api.launcher_data": { "dataType": "entity_data", "requestPath": "/games/{id}", + "dataSelector": "data.defaults", + "entityType": "launcher" + }, + "web_services.adapters.launchpad_api.launcher_lookup": { + "dataType": "entity_lookup", + "requestPath": "/lookup/{id}/{platform}", + "dataSelector": "id", "entityType": "launcher" }, "web_services.adapters.launchpad_api.launcher_type_list": { @@ -107,14 +116,11 @@ "web_services.adapters.launchpad_api.launcher_type_data": { "dataType": "entity_data", "requestPath": "/launcher-types/{id}", + "dataSelector": "data.defaults", "entityType": "managed_launcher" } }, "services": { - "data_source.launchpad_api": { - "class": "ApiDataSource", - "arguments": ["@{App}", "@manager.cache", "launchpad_api", "https://api.launchpad.games/v1"] - }, "cache_state.launchpad_api": { "class": "CacheState", "arguments": ["@{App}", "@@config.cache_dir", "API.json"] @@ -122,6 +128,11 @@ "cache.launchpad_api": { "class": "FileCache", "arguments": ["@{App}", "@cache_state.launchpad_api", "@@config.cache_dir", "API"] + }, + "event_subscriber.launchpad_api": { + "class": "LaunchpadApiSubscriber", + "arguments": ["@{}"], + "tags": ["event_subscriber"] } } } diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 85a1b0ab..dd10622c 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -1,8 +1,7 @@ -class WebServiceEntity extends AppEntityBase { +class WebServiceEntity extends FieldableEntity { cacheObj := "" stateObj := "" persistentStateObj := "" - mergeDataFromApi := false statusIndicators := [] adapters := Map() adapterFactory := "" @@ -20,20 +19,19 @@ class WebServiceEntity extends AppEntityBase { set => this.SetAuthData(key, value) } - __New(app, id, entityTypeId, container, adapterFactory, cacheObj, stateObj, persistentStateObj, eventMgr, storageObj, idSanitizer, parentEntity := "") { + __New(id, entityTypeId, container, adapterFactory, cacheObj, stateObj, persistentStateObj, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, parentEntity := "") { this.cacheObj := cacheObj this.stateObj := stateObj this.persistentStateObj := persistentStateObj this.adapterFactory := adapterFactory - super.__New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity) + super.__New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, parentEntity) } static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { className := this.Prototype.__Class return %className%( - container.GetApp(), id, entityTypeId, container, @@ -41,6 +39,8 @@ class WebServiceEntity extends AppEntityBase { container.Get("cache.web_services"), container.Get("state.web_services_tmp"), container.Get("state.web_services"), + container.Get("entity_field_factory." . entityTypeId), + container.Get("entity_widget_factory." . entityTypeId), eventMgr, storageObj, idSanitizer, @@ -48,7 +48,7 @@ class WebServiceEntity extends AppEntityBase { ) } - AdapterRequest(params, adapterFilters, operation := "read", limit := false) { + AdapterRequest(params, adapterFilters, operation := "read", multiple := false) { if (!adapterFilters) { adapterFilters := Map() } @@ -64,9 +64,18 @@ class WebServiceEntity extends AppEntityBase { results := Map() for adapterKey, adapter in this.GetAdapters(adapterFilters, operation) { - results[adapterKey] := adapter.SendRequest(operation, params) + result := adapter.SendRequest(operation, params) - if (limit && results.Count >= limit) { + if (result) { + if (!multiple) { + results := result + break + } + + results[adapterKey] := result + } + + if (IsNumber(multiple) && results.Count >= multiple) { break } } diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk index c8b02d43..573120f3 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk @@ -1,6 +1,4 @@ -class WebServiceProviderEntity extends AppEntityBase { - mergeDataFromApi := false - +class WebServiceProviderEntity extends FieldableEntity { BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() diff --git a/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk index c508904b..fda1e07a 100644 --- a/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk @@ -50,7 +50,7 @@ class FeedbackWindow extends DialogBox { body["version"] := appVersion body["feedback"] := this.guiObj["Feedback"].Text - results := webService.AdapterRequest(Map("data", body), "feedback_submission", "create") + results := webService.AdapterRequest(Map("data", body), "feedback_submission", "create", true) } for key, result in results { diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk index f7002611..b34ea21d 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -5,6 +5,10 @@ class WebServiceAdapterBase { dataType := "" merger := "" operationTypes := ["create", "read", "update", "delete"] + + static ADAPTER_RESULT_DATA := "data" + static ADAPTER_RESULT_HTTP_STATUS := "httpStatus" + static ADAPTER_RESULT_SUCCESS := "success" __New(container, merger, webService, definition) { this.container := container @@ -53,7 +57,8 @@ class WebServiceAdapterBase { "deleteAllow", false, "deleteMethod", "PUT", "deleteAuth", true, - "dataMap", Map() + "dataMap", Map(), + "dataSelector", [] ) } @@ -93,13 +98,50 @@ class WebServiceAdapterBase { throw AppException("The 'create' operation is not allowed on this data adapter.") } - return this._request( + response := this._request( params, this.definition["createMethod"], data ? data : this._getData(params), this.definition["createAuth"], false - ).Send().IsSuccessful() + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) + } + + _getResultType(params, default) { + resultType := default + + if (params.Has("resultType") && params["resultType"]) { + resultType := params["resultType"] + } + + return resultType + } + + _getResult(params, response, resultType) { + result := "" + + if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_DATA) { + if (response.IsSuccessful()) { + data := response.GetResponseBody() + + if (data) { + result := this._mapData(this._parseData(data, params), params) + } + + } + } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_HTTP_STATUS) { + result := response.GetHttpStatusCode() + } else if (resultType == WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) { + result := response.IsSuccessful() + } + + return result } DataExists(params := "") { @@ -129,15 +171,11 @@ class WebServiceAdapterBase { this.definition["cacheResponse"] ).Send() - data := "" - - if (response.IsSuccessful()) { - data := response.GetResponseBody() - data := this._parseData(data, params) - this._mapData(data, params) - } - - return data + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_DATA) + ) } UpdateData(data, params := "") { @@ -145,13 +183,19 @@ class WebServiceAdapterBase { throw AppException("The 'update' operation is not allowed on this data adapter.") } - return this._request( + response := this._request( params, this.definition["updateMethod"], data ? data : this._getData(params), this.definition["updateAuth"], false - ).Send().IsSuccessful() + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) } DeleteData(params := "") { @@ -159,13 +203,19 @@ class WebServiceAdapterBase { throw AppException("The 'delete' operation is not allowed on this data adapter.") } - return this._request( + response := this._request( params, this.definition["deleteMethod"], this._getData(params), this.definition["deleteAuth"], false - ).Send().IsSuccessful() + ).Send() + + return this._getResult( + params, + response, + this._getResultType(params, WebServiceAdapterBase.ADAPTER_RESULT_SUCCESS) + ) } _requestPath(params) { @@ -220,9 +270,26 @@ class WebServiceAdapterBase { } _parseData(data, params) { - if (data && this.dataType) { - dataType := this.dataType + if (data && this.definition["dataType"]) { + dataType := this.definition["dataType"] data := %dataType%().FromString(data) + + if (this.definition["dataSelector"]) { + dataSelector := this.definition["dataSelector"] + + if (Type(dataSelector) == "String") { + dataSelector := StrSplit(dataSelector, ".") + } + + for index, pathPart in dataSelector { + if (data.Has(pathPart)) { + data := data[pathPart] + } else { + data := "" + break + } + } + } } return data diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk index 754981b0..8e7f56d8 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -132,6 +132,10 @@ class WebServiceRequestBase { authenticator.AlterRequest(this.webServiceEnt, httpReqObj) } + if (!this.cacheResponse) { + httpReqObj.requestHeaders["Cache-Control"] := "no-cache" + } + event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_HTTP_REQ_ALTER, this) this.eventMgr.DispatchEvent(event) diff --git a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk b/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk deleted file mode 100644 index 91c1fdce..00000000 --- a/Lib/Shared/Volantis.App/DataSource/DataSourceBase.ahk +++ /dev/null @@ -1,70 +0,0 @@ -class DataSourceBase { - cache := "" - useCache := false - maxCacheAge := 86400 - - __New(cacheManager := "", cacheName := "") { - if (cacheManager != "" && cacheName != "") { - InvalidParameterException.CheckTypes("DataSourceBase", "cacheManager", cacheManager, "CacheManager") - this.useCache := true - this.cache := cacheManager[cacheName] - } - } - - ItemExists(path) { - return this.useCache ? this.cache.ItemExists(path) : false - } - - ReadItem(path, private := false, maxCacheAge := "") { - if (maxCacheAge == "") { - maxCacheAge := this.maxCacheAge - } - - item := "" - - if (this.ItemNeedsRetrieval(path)) { - item := this.RetrieveItem(path, private, maxCacheAge) - } else if (this.useCache) { - item := this.cache.ReadItem(path) - } - - return item - } - - ItemNeedsRetrieval(path) { - return (!this.useCache || this.cache.ItemNeedsUpdate(path)) - } - - RetrieveItem(path, private := false, maxCacheAge := "") { - return "" - } - - CopyItem(path, destination) { - if (this.ItemNeedsRetrieval(path)) { - this.RetrieveItem(path) - } - - return this.useCache ? this.cache.CopyItem(path, destination) : destination - } - - GetRemoteLocation(path) { - return path - } - - ReadListing(path) { - listingInstance := DSListing(path, this) - - listing := [] - - if (listingInstance.Exists()) { - listing := listingInstance.Read() - } - - return listing - } - - ReadJson(key, path := "") { - dsItem := DSJson(key, path, this) - return dsItem.Read() - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk deleted file mode 100644 index a5165b1f..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSAssetFile.ahk +++ /dev/null @@ -1,3 +0,0 @@ -class DSAssetFile extends DSFile { - allowRead := false -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk deleted file mode 100644 index 187d931b..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSFile.ahk +++ /dev/null @@ -1,7 +0,0 @@ -class DSFile extends DataSourceItemBase { - allowRead := true ; Some files are only meant to be copied - - Read() { - return this.allowRead ? super.Read() : "" - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk deleted file mode 100644 index 991c41dc..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSJson.ahk +++ /dev/null @@ -1,17 +0,0 @@ -class DSJson extends DSFile { - itemSuffix := "" - dataType := "Map" - - Read() { - content := super.Read() - dataType := this.dataType - obj := %dataType%() - - if (content) { - data := JsonData() - obj := data.FromString(&content) - } - - return obj - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk deleted file mode 100644 index 1d48a12c..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DSListing.ahk +++ /dev/null @@ -1,7 +0,0 @@ -class DSListing extends DSJson { - dataType := "Array" - - __New(path, dataSourceKey := "") { - super.__New(path, "", dataSourceKey) - } -} diff --git a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk b/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk deleted file mode 100644 index ef0ff3ea..00000000 --- a/Lib/Shared/Volantis.App/DataSourceItem/DataSourceItemBase.ahk +++ /dev/null @@ -1,53 +0,0 @@ -class DataSourceItemBase { - endpoint := "" - basePath := "" - itemSuffix := "" - path := "" - key := "" - - __New(key, path := "", dataSource := "") { - InvalidParameterException.CheckTypes("DataSourceItemBase", "key", key, "", "path", path, "") - InvalidParameterException.CheckEmpty("DataSourceItemBase", "key", key) - InvalidParameterException.CheckTypes("DataSourceItemBase", "dataSource", dataSource, "DataSourceBase") - - this.endpoint := dataSource - this.key := key - this.path := path - } - - GetPath(includeFilename := true) { - path := this.basePath - - if (path != "" && this.path != "") { - path .= "/" - } - - path .= this.path - - if (includeFilename) { - if (path) { - path .= "/" - } - - path .= this.key . this.itemSuffix - } - - return path - } - - GetRemoteLocation() { - return this.endpoint.GetRemoteLocation(this.GetPath()) - } - - Exists() { - return this.endpoint.ItemExists(this.GetPath()) - } - - Read() { - return this.endpoint.ReadItem(this.GetPath()) - } - - Copy(destination) { - return this.endpoint.CopyItem(this.GetPath(), destination) - } -} diff --git a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk b/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk deleted file mode 100644 index b1be4fd7..00000000 --- a/Lib/Shared/Volantis.App/Entity/AppEntityBase.ahk +++ /dev/null @@ -1,204 +0,0 @@ -class AppEntityBase extends FieldableEntity { - app := "" - dataSourcePath := "" - existsInDataSource := false - mergeDataFromApi := true - - __New(app, id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity := "") { - this.app := app - - super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, parentEntity) - } - - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { - className := this.Prototype.__Class - - return %className%( - container.GetApp(), - id, - entityTypeId, - container, - eventMgr, - storageObj, - idSanitizer, - parentEntity - ) - } - - GetDefaultFieldGroups() { - groups := super.GetDefaultFieldGroups() - - groups["advanced"] := Map( - "name", "Advanced", - "weight", 100 - ) - - if (this.mergeDataFromApi) { - groups["api"] := Map( - "name", "API", - "weight", 150 - ) - } - - return groups - } - - BaseFieldDefinitions() { - definitions := super.BaseFieldDefinitions() - - if (this.mergeDataFromApi) { - definitions["DataSourceKeys"] := Map( - "description", "The data source keys to load defaults from, in order.", - "help", "The default data source is 'api' which connects to the default api endpoint (Which can be any HTTP location compatible with Launchpad's API format)", - "default", [this.app.Config["data_source_key"]], - "multiple", true, - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["DataSourceItemKey"] := Map( - "description", "The key that is used to look up the entity's data from configured external data sources.", - "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataSourceKey", - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - } - - definitions["AssetsDir"] := Map( - "type", "directory", - "description", "The directory where any required assets for this entity will be saved.", - "default", this.app.Config["assets_dir"] . "\" . this.Id, - "group", "advanced", - "formField", false, - "modes", Map("simple", Map("formField", false)) - ) - - definitions["DependenciesDir"] := Map( - "type", "directory", - "description", "The directory where dependencies which have been installed for this entity can be accessed.", - "default", this.app.appDir . "\Vendor", - "group", "advanced", - "required", true, - "formField", false, - "modes", Map("simple", Map("formField", false)) - ) - - return definitions - } - - _getLayerNames() { - layerNames := super._getLayerNames() - layerNames.Push("ds") - - return layerNames - } - - _getLayerSources() { - layerSources := super._getLayerSources() - layerSources["ds"] := ObjBindMethod(this, "AggregateDataSourceDefaults") - - return layerSources - } - - UpdateDataSourceDefaults(recurse := true) { - ; @todo Move this to a module - this.GetData().UnloadLayer("ds") - - if (recurse) { - for key, child in this.GetReferencedEntities(true) { - child.UpdateDataSourceDefaults(recurse) - } - } - } - - AggregateDataSourceDefaults(includeParentData := true, includeChildData := true) { - defaults := Map() - - if (this.mergeDataFromApi) { - defaults := (this.parentEntity != "" && includeParentData) - ? this.parentEntity.AggregateDataSourceDefaults(includeParentData, false) - : defaults - - for index, dataSource in this.GetAllDataSources() { - defaults := this.merger.Merge(this.GetDataSourceDefaults(dataSource), defaults) - } - - if (includeChildData) { - for key, child in this.GetReferencedEntities(true) { - defaults := this.merger.Merge(child.AggregateDataSourceDefaults(false, includeChildData), defaults) - } - } - } - - return defaults - } - - GetAllDataSources() { - dataSources := Map() - - if (this.mergeDataFromApi && this.Has("DataSourceKeys", false)) { - dataSourceKeys := this["DataSourceKeys"] - - if (!HasBase(dataSourceKeys, Array.Prototype)) { - dataSourceKeys := [dataSourceKeys] - } - - for index, dataSourceKey in dataSourceKeys { - if (this.app["manager.data_source"].Has(dataSourceKey)) { - dataSource := this.app["manager.data_source"][dataSourceKey] - - if (dataSource) { - dataSources[dataSourceKey] := dataSource - } - } - } - } - - return dataSources - } - - GetDataSourceDefaults(dataSource) { - defaults := Map() - - if (this.mergeDataFromApi) { - itemKey := this.DiscoverDataSourceItemKey() - - if (itemKey) { - dsData := dataSource.ReadJson(itemKey, this.GetDataSourceItemPath()) - - if (dsData) { - this.existsInDataSource := true - - if (dsData.Has("data")) { - dsData := dsData["data"] - } - - if (dsData.Has("defaults")) { - defaults := this.merger.Merge(dsData["defaults"], defaults) - defaults := this.MergeAdditionalDataSourceDefaults(defaults, dsData) - } - } - } - } - - return defaults - } - - DiscoverDataSourceItemKey() { - return this.Id - } - - GetDataSourceItemPath() { - return this.dataSourcePath - } - - MergeAdditionalDataSourceDefaults(defaults, dataSourceData) { - return defaults - } - - GetAssetPath(filePath) { - return this["AssetsDir"] . "\" . filePath - } -} diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk index c02ae818..9d2c32fa 100644 --- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk +++ b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk @@ -1,11 +1,12 @@ -class BackupEntity extends AppEntityBase { +class BackupEntity extends FieldableEntity { backup := "" - __New(app, key, config, parentEntity := "", requiredConfigKeys := "") { - super.__New(app, key, config, parentEntity, requiredConfigKeys) - backupClass := config.Has("BackupClass") ? config["BackupClass"] : "FileBackup" + SetupEntity() { + super.SetupEntity() if (!this.backup) { + backupClass := this.config.Has("BackupClass") ? this.config["BackupClass"] : "FileBackup" + this.CreateBackupObject(backupClass) } } @@ -13,10 +14,6 @@ class BackupEntity extends AppEntityBase { BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - if (definitions.Has("DataSourceKeys")) { - definitions["DataSourceKeys"]["default"] := [] - } - definitions["IsEditable"] := Map( "type", "boolean", "default", true diff --git a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk b/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk index c8a5b9b7..3f294960 100644 --- a/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk +++ b/Lib/Shared/Volantis.App/Entity/TaskEntity.ahk @@ -1,3 +1,3 @@ -class TaskEntity extends AppEntityBase { +class TaskEntity extends FieldableEntity { } diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk index 566e0964..672baa1a 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk +++ b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk @@ -1,7 +1,6 @@ class ErrorDialog extends DialogBox { errorObj := "" notifierObj := "" - apiEndpoint := "" formShown := false formH := 0 guiH := 0 @@ -9,15 +8,6 @@ class ErrorDialog extends DialogBox { __New(container, themeObj, config, errorObj) { this.errorObj := errorObj this.notifierObj := container.Get("notifier").notifierObj - - if (container.Has("manager.data_source")) { - dsManager := container.Get("manager.data_source") - - if (dsManager.GetDefaultDataSource()) { - this.apiEndpoint := container.Get("manager.data_source").GetDefaultDataSource() - } - } - this.formShown := config.Has("submitError") ? config["submitError"] : false super.__New(container, themeObj, config) @@ -118,8 +108,14 @@ class ErrorDialog extends DialogBox { SendError() { global appVersion - if (this.apiEndpoint) { - endpoint := this.apiEndpoint.endpointUrl . "/submit-error" + ; @todo Move the API connection stuff into the LaunchpadApi module + + if ( + this.container.Has("entity_manager.web_service") + && this.container["entity_manager.web_service"].Has("launchpad_api") + && this.container["entity_manager.web_service"]["launchpad_api"]["Enabled"] + ) { + webService := this.container["entity_manager.web_service"]["launchpad_api"] body := Map() body["message"] := this.errorObj.Message @@ -132,12 +128,14 @@ class ErrorDialog extends DialogBox { body["version"] := appVersion ? appVersion : "" body["details"] := this.guiObj["ErrorDetails"].Text - request := WinHttpReq(endpoint) - response := request.Send("POST", body) - success := !!(request.GetStatusCode() == 200) + success := webService.AdapterRequest( + Map("data", body), + Map("adapterType", "error_submission"), + "create" + ) - notification := success ? "Successfully sent error to Volantis Development" : "Failed to send error to Volantis Development" - this.notifierObj.Notify(notification, "Error Sent", success ? "info" : "error") + notification := success ? "Successfully sent error details for further investigation" : "Failed to send error details" + this.notifierObj.Notify(notification, "Error Submission", success ? "info" : "error") } } } diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk index 8e0a2a83..d0d8c4a1 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk +++ b/Lib/Shared/Volantis.App/Gui/Dialog/UpdateAvailableWindow.ahk @@ -21,9 +21,9 @@ class UpdateAvailableWindow extends FormGuiBase { super.Controls() this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Current version: " . appVersion) this.SetFont("normal", "Bold") - this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["data"]["version"]) + this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), "Latest version: " . this.releaseInfo["version"]) this.SetFont() - this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes') + this.guiObj.AddLink("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin), 'View release notes') this.guiObj.AddText("w" . this.windowSettings["contentWidth"] . " y+" . (this.margin*2), "Would you like to update " . this.app.appName . " now?") } @@ -36,14 +36,14 @@ class UpdateAvailableWindow extends FormGuiBase { } ApplyUpdate() { - downloadUrl := this.releaseInfo["data"].Has("installer") ? this.releaseInfo["data"]["installer"] : "" + downloadUrl := this.releaseInfo.Has("installer") ? this.releaseInfo["installer"] : "" if (!DirExist(this.app.tmpDir . "\Installers")) { DirCreate(this.app.tmpDir . "\Installers") } if (downloadUrl) { - localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["data"]["version"] . ".exe" + localFile := this.app.tmpDir . "\Installers\" . this.app.appName . "-" . this.releaseInfo["version"] . ".exe" FileDelete(this.app.tmpDir . "\Installers\" . this.app.appName . "-*") Download(downloadUrl, localFile) Run(localFile) diff --git a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk index 467bd165..fd681036 100644 --- a/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk +++ b/Lib/Shared/Volantis.App/Gui/EntityEditor/EntityEditorBase.ahk @@ -9,7 +9,6 @@ class EntityEditorBase extends FormGuiBase { entityObj := "" missingFields := Map() - dataSource := "" entityFormFactory := "" entityForm := "" @@ -75,9 +74,4 @@ class EntityEditorBase extends FormGuiBase { AddEntityCtl(heading, fieldName, showDefaultCheckbox, params*) { return this.Add("EntityControl", "", heading, this.entityObj, fieldName, showDefaultCheckbox, params*) } - - Create() { - super.Create() - this.dataSource := this.app["manager.data_source"].GetDefaultDataSource() - } } diff --git a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk b/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk index fb3688c8..7c159b01 100644 --- a/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk +++ b/Lib/Shared/Volantis.App/GuiControl/EntityControl.ahk @@ -66,7 +66,7 @@ class EntityControl extends GuiControlBase { this.widget.WriteValueToEntity() if (this.refreshDataOnChange && (!this.dependentFields || this.dependentFields.Length == 0)) { - this.entityObj.UpdateDataSourceDefaults() + this.entityObj.UpdateDefaults() } this.SetDependentFieldValues() @@ -117,7 +117,7 @@ class EntityControl extends GuiControlBase { SetDependentFieldValues() { if (this.dependentFields && this.dependentFields.Length > 0) { - this.entityObj.UpdateDataSourceDefaults() + this.entityObj.UpdateDefaults() for index, field in this.dependentFields { this.guiObj.guiObj[field].Value := this.entityObj.GetField(field).GetRawValue() diff --git a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk b/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk deleted file mode 100644 index 2b688991..00000000 --- a/Lib/Shared/Volantis.App/Service/ComponentManager/DataSourceManager.ahk +++ /dev/null @@ -1,61 +0,0 @@ -class DataSourceManager extends ComponentManagerBase { - primaryKey := "" - - __New(container, eventMgr, notifierObj, primaryKey := "") { - if (primaryKey) { - this.primaryKey := primaryKey - } - - super.__New(container, "data_source.", eventMgr, notifierObj, DataSourceBase) - } - - GetDefaultDataSource() { - if (!this.primaryKey) { - throw ComponentException("There is no default data source set") - } - - if (!this.Has(this.primaryKey)) { - throw ComponentException("Primary data source key " . this.primaryKey . " does not exist") - } - - return this[this.primaryKey] - } - - GetDefaultComponentId() { - return this.primaryKey - } - - GetItem(key := "") { - if (key == "") { - key := this.primaryKey - } - - return super.GetItem(key) - } - - ReadListing(path, dataSourceKey := "") { - if (dataSourceKey == "") { - dataSourceKey := this.primaryKey - } - - if (!this.Has(dataSourceKey)) { - throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager") - } - - dataSource := this[dataSourceKey] - return dataSource.ReadListing(path) - } - - ReadJson(key, path := "", dataSourceKey := "") { - if (dataSourceKey == "") { - dataSourceKey := this.primaryKey - } - - if (!this.Has(dataSourceKey)) { - throw ComponentException("Component " . dataSourceKey . " does not exist in the data source manager") - } - - dataSource := this[dataSourceKey] - return dataSource.ReadJson(key, path) - } -} diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index c428f0b1..cc2562ea 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -3,6 +3,7 @@ class EntityBase { entityTypeIdVal := "" parentEntityObj := "" container := "" + app := "" eventMgr := "" dataObj := "" storageObj := "" @@ -37,7 +38,7 @@ class EntityBase { set => this.SetValue("name", value) } - UnmergedFieldData { + RawData { get => this.GetData().GetLayer(this.dataLayer) set => this.GetData().SetLayer(this.dataLayer, value) } @@ -57,6 +58,7 @@ class EntityBase { } __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "") { + this.app := container.GetApp() this.idSanitizer := idSanitizer if (this.sanitizeId && this.idSanitizer) { @@ -87,6 +89,20 @@ class EntityBase { } } + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + className := this.Prototype.__Class + + return %className%( + id, + entityTypeId, + container, + eventMgr, + storageObj, + idSanitizer, + parentEntity + ) + } + _createEntityData() { this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources()) } @@ -104,18 +120,14 @@ class EntityBase { ) } - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { - className := this.Prototype.__Class - - return %className%( - id, - entityTypeId, - container, - eventMgr, - storageObj, - idSanitizer, - parentEntity - ) + /** + * Get an array of all IDs + * + * List managed IDs and give modules a chance to add others. + */ + ListEntities(includeManaged := true, includeExtended := true) { + return this.container["entity_manager." . this.EntityTypeId] + .ListEntities(includeManaged, includeExtended) } DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { @@ -139,10 +151,6 @@ class EntityBase { return this.GetData().GetMergedData(!raw) } - GetEntityTypeId() { - return this.entityTypeId - } - GetEntityType() { ; @todo Inject entity type manager service return this.container.Get("manager.entity_type")[this.EntityTypeId] @@ -224,6 +232,15 @@ class EntityBase { RestoreSnapshot(name, recurse := true) { this.GetData().RestoreSnapshot(name) + + if (recurse) { + for index, entityObj in this.GetReferencedEntities(true) { + if (entityObj.HasOwnDataStorage()) { + entityObj.GetData().RestoreSnapshot(name, recurse) + } + } + } + return this } @@ -275,7 +292,13 @@ class EntityBase { } } - return values + event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES, this.EntityTypeId, this, values) + this.eventMgr.DispatchEvent(event) + + event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES_ALTER, this.EntityTypeId, this, event.Values) + this.eventMgr.DispatchEvent(event) + + return event.Values } SaveEntity(recurse := true) { @@ -437,4 +460,16 @@ class EntityBase { return text } + + UpdateDefaults(recurse := true) { + if (this.HasOwnDataStorage()) { + this.GetData().UnloadAllLayers(false) + } + + if (recurse) { + for key, child in this.GetReferencedEntities(true) { + child.UpdateDefaults(recurse) + } + } + } } diff --git a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk index 2776d249..e4e43d60 100644 --- a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk @@ -11,12 +11,29 @@ class FieldableEntity extends EntityBase { get => this.GetFieldDefinitions() } - __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true) { - this.entityFieldFactory := container.Get("entity_field_factory." . entityTypeId) - this.entityWidgetFactory := container.Get("entity_widget_factory." . entityTypeId) + __New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer := "", autoLoad := true) { + this.entityFieldFactory := fieldFactory + this.entityWidgetFactory := widgetFactory + super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad) } + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + className := this.Prototype.__Class + + return %className%( + id, + entityTypeId, + container, + container.Get("entity_field_factory." . entityTypeId), + container.Get("entity_widget_factory." . entityTypeId), + eventMgr, + storageObj, + idSanitizer, + parentEntity + ) + } + GetDefaultFieldGroups() { return Map( "general", Map( diff --git a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk b/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk index c84eacc2..00399489 100644 --- a/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk +++ b/Lib/Shared/Volantis.Entity/EntityManager/EntityManagerBase.ahk @@ -94,4 +94,30 @@ class EntityManagerBase extends ComponentManagerBase { childManager.LoadComponents(reloadComponents) } } + + /** + * Get an array of all IDs + * + * List managed IDs and give modules a chance to add others. + */ + ListEntities(includeManaged := true, includeExtended := true) { + entities := includeManaged + ? this.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() + : [] + + if (includeExtended) { + event := EntityListEvent( + EntityEvents.ENTITY_LIST_ENTITIES, + this.entityTypeId, + entities, + includeManaged, + includeExtended + ) + this.eventMgr.DispatchEvent(event) + + entities := event.EntityList + } + + return entities + } } diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk index b471ec6f..bd366064 100644 --- a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk +++ b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk @@ -64,7 +64,7 @@ class EntityStorageBase { _dereferenceData(idOrObj, data := "") { if (HasBase(idOrObj, EntityBase.Prototype) && !data) { - data := idOrObj.UnmergedFieldData + data := idOrObj.RawData } return data From ca26128c075abdc04a9b440ae4896765600ab350 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 16:57:41 -0500 Subject: [PATCH 079/138] Rename DataLookupKey to LaunchpadApiRef --- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 4 ++-- .../EventSubscriber/LaunchpadApiSubscriber.ahk | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index 8137e949..d8833ccd 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -133,7 +133,7 @@ status := launcher.GetStatus() ; @todo Move the API data to an event in the LaunchpadApi module - apiStatus := (launcher.HasField["DataLookupKey"] && launcher["DataLookupKey"]) ? "Linked" : "Not linked" + apiStatus := (launcher.HasField["LaunchpadApiRef"] && launcher["LaunchpadApiRef"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo("Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo("Build")["Timestamp"]) @@ -311,7 +311,7 @@ status := launcher.GetStatus() ; @todo Move the API code to the LaunchpadApi module - apiStatus := (launcher.HasField("DataLookupKey") && launcher["DataLookupKey"]) ? "Linked" : "Not linked" + apiStatus := (launcher.HasField("LaunchpadApiRef") && launcher["LaunchpadApiRef"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo(key, "Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo(key, "Build")["Timestamp"]) diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk index bc9da846..699cbb5d 100644 --- a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -145,9 +145,9 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { ]) if (adapters.Count) { - fieldDefinitions["DataLookupKey"] := Map( + fieldDefinitions["LaunchpadApiRef"] := Map( "description", "The key that is used to look up the entity's data from configured external data sources.", - "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same DataLookupKey", + "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same LaunchpadApiRef", "group", "api", "processValue", false, "modes", Map("simple", Map("formField", false)) @@ -167,9 +167,9 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { if ( webService["Enabled"] - && (!values.Has("DataLookupKey") || !values["DataLookupKey"]) - && entity.HasField("DataLookupKey") - && (!entity.RawData.Has["DataLookupKey"] || !entity.RawData["DataLookupKey"]) + && (!values.Has("LaunchpadApiRef") || !values["LaunchpadApiRef"]) + && entity.HasField("LaunchpadApiRef") + && (!entity.RawData.Has["LaunchpadApiRef"] || !entity.RawData["LaunchpadApiRef"]) ) { result := "" @@ -190,7 +190,7 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { } if (result) { - values["DataLookupKey"] := result + values["LaunchpadApiRef"] := result } } } From e908cc9a515a3b8154879d219a963373b7db0c27 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Wed, 14 Dec 2022 16:58:48 -0500 Subject: [PATCH 080/138] Rename LauncherSpecificId to PlatformRef --- Lib/Launchpad/DetectedGame/DetectedGame.ahk | 12 ++++++------ Lib/Launchpad/Entity/ManagedEntityBase.ahk | 6 +++--- Lib/Launchpad/Entity/ManagedGameEntity.ahk | 2 +- Lib/Launchpad/Entity/ManagedProcessEntity.ahk | 6 +++--- Lib/Launchpad/GamePlatform/GamePlatformBase.ahk | 6 +++--- Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk | 6 +++--- .../Blizzard/GamePlatform/BlizzardPlatform.ahk | 6 +++--- .../Modules/Epic/GamePlatform/EpicPlatform.ahk | 4 ++-- .../Modules/Riot/GamePlatform/RiotPlatform.ahk | 2 +- .../Modules/Steam/GamePlatform/SteamPlatform.ahk | 4 ++-- Lib/LaunchpadLauncher/Game/BlizzardGame.ahk | 2 +- Lib/LaunchpadLauncher/Game/RiotGame.ahk | 2 +- 12 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 5707278f..68aa54b2 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -8,7 +8,7 @@ class DetectedGame { installDir := "" launcherInstallDir := "" exeName := "" - launcherSpecificId := "" + platformRef := "" possibleExeNames := [] keyMap := Map() ; @todo Move this to properties or config or allow it to be extended @@ -17,7 +17,7 @@ class DetectedGame { prioritySuffixes := ["-Win64-Shipping", "-Win32-Shipping"] filterExes := [] - __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", launcherSpecificId := "", possibleExeNames := "", displayName := "") { + __New(key, platform, launcherType, gameType := "Default", installDir := "", exeName := "", platformRef := "", possibleExeNames := "", displayName := "") { this.key := key this.displayName := displayName ? displayName : key this.platform := platform @@ -26,7 +26,7 @@ class DetectedGame { this.gameType := gameType this.installDir := installDir this.exeName := exeName - this.launcherSpecificId := launcherSpecificId + this.platformRef := platformRef if (possibleExeNames) { if (Type(possibleExeNames) == "String") { @@ -49,7 +49,7 @@ class DetectedGame { || this.installDir != launcher["ManagedGame"]["InstallDir"] || this.launcherInstallDir != launcher["ManagedLauncher"]["InstallDir"] || this.exeName != launcher["ManagedGame"]["Exe"] - || this.launcherSpecificId != launcher["ManagedGame"]["LauncherSpecificId"] + || this.platformRef != launcher["ManagedGame"]["PlatformRef"] ) { hasChanges := true } @@ -122,8 +122,8 @@ class DetectedGame { config["GameExe"] := this.exeName } - if (this.launcherSpecificId) { - config["GameLauncherSpecificId"] := this.launcherSpecificId + if (this.platformRef) { + config["GamePlatformRef"] := this.platformRef } entityObj := launcherManager.GetFactory().CreateEntity(this.key, config) diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/Lib/Launchpad/Entity/ManagedEntityBase.ahk index b7be2261..b84be410 100644 --- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk +++ b/Lib/Launchpad/Entity/ManagedEntityBase.ahk @@ -122,7 +122,7 @@ class ManagedEntityBase extends FieldableEntity { ), "widget", "select", "selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"), - "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for LauncherSpecificId within the Blizzard product.db file if present" + "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for PlatformRef within the Blizzard product.db file if present" ) definitions["WindowTitle"] := Map( @@ -188,8 +188,8 @@ class ManagedEntityBase extends FieldableEntity { ) ) - definitions["LauncherSpecificId"] := Map( - "storageKey", this.configPrefix . "LauncherSpecificId", + definitions["PlatformRef"] := Map( + "storageKey", this.configPrefix . "PlatformRef", "description", "If the item is known to the launcher by a specific ID, it should be stored here.", "group", "general" ) diff --git a/Lib/Launchpad/Entity/ManagedGameEntity.ahk b/Lib/Launchpad/Entity/ManagedGameEntity.ahk index 235010cc..916ebe67 100644 --- a/Lib/Launchpad/Entity/ManagedGameEntity.ahk +++ b/Lib/Launchpad/Entity/ManagedGameEntity.ahk @@ -40,7 +40,7 @@ class ManagedGameEntity extends ManagedEntityBase { } GetBlizzardProductKey() { - productKey := this["LauncherSpecificId"] + productKey := this["PlatformRef"] if (this.HasConfigValue("BlizzardProductId", true, false)) { productKey := this.GetConfigValue("BlizzardProductId") diff --git a/Lib/Launchpad/Entity/ManagedProcessEntity.ahk b/Lib/Launchpad/Entity/ManagedProcessEntity.ahk index 4ff6778c..02cb087b 100644 --- a/Lib/Launchpad/Entity/ManagedProcessEntity.ahk +++ b/Lib/Launchpad/Entity/ManagedProcessEntity.ahk @@ -122,7 +122,7 @@ class ManagedProcessEntity extends FieldableEntity { ), "widget", "select", "selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"), - "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for LauncherSpecificId within the Blizzard product.db file if present" + "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for PlatformRef within the Blizzard product.db file if present" ) definitions["WindowTitle"] := Map( @@ -188,8 +188,8 @@ class ManagedProcessEntity extends FieldableEntity { ) ) - definitions["LauncherSpecificId"] := Map( - "storageKey", this.configPrefix . "LauncherSpecificId", + definitions["PlatformRef"] := Map( + "storageKey", this.configPrefix . "PlatformRef", "description", "If the item is known to the launcher by a specific ID, it should be stored here.", "group", "general" ) diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk index 0476cd6d..a8dfc88b 100644 --- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk +++ b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk @@ -138,7 +138,7 @@ class GamePlatformBase { return [] } - GetLauncherSpecificId(key) { + GetPlatformRef(key) { return key } @@ -164,8 +164,8 @@ class GamePlatformBase { locator := GameExeLocator(installDir) possibleExes := locator.Locate("") exeName := this.DetermineMainExe(key, possibleExes) - launcherSpecificId := this.GetLauncherSpecificId(key) - detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, launcherSpecificId, possibleExes) + platformRef := this.GetPlatformRef(key) + detectedGameObj := DetectedGame(key, this, this.launcherType, this.gameType, installDir, exeName, platformRef, possibleExes) if (this.installDir) { detectedGameObj.launcherInstallDir := this["InstallDir"] diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 518c04c5..6b8de59e 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -82,7 +82,7 @@ this.Add("SelectControl", "vGameType", "Game Type", this.detectedGameObj.gameType, this.gameTypes, "OnGameTypeChange", "This tells " . this.app.appName . " how to launch your game. Most games can use 'default', but launchers can support different game types.`n`nYou can customize the details of the game type after it is added.") this.Add("LocationBlock", "", "Install Dir", this.detectedGameObj.installDir, "InstallDir", "", true, "This is the directory that the game is installed in, if it could be detected.") this.Add("ComboBoxControl", "vExe", "Exe", this.detectedGameObj.exeName, this.detectedGameObj.possibleExeNames, "OnExeChange", "The main Exe, if detected, should be pre-selected. You may change it to be the name (or path) of another exe, or select another one of the detected .exe files from the list (if more than one was found).") - this.AddTextBlock("Launcher-Specific ID", "LauncherSpecificId", this.detectedGameObj.launcherSpecificId, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.") + this.AddTextBlock("Launcher-Specific ID", "PlatformRef", this.detectedGameObj.platformRef, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.") } AddTextBlock(heading, field, existingVal := "", helpText := "") { @@ -159,9 +159,9 @@ this.newValues["exeName"] := ctl.Text } - OnLauncherSpecificIdChange(ctl, info) { + OnPlatformRefChange(ctl, info) { this.guiObj.Submit(false) - this.newValues["launcherSpecificId"] := ctl.Text + this.newValues["platformRef"] := ctl.Text } ProcessResult(result, submittedData := "") { diff --git a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk index 51c997e3..097d4a3e 100644 --- a/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk +++ b/Lib/Launchpad/Modules/Blizzard/GamePlatform/BlizzardPlatform.ahk @@ -28,16 +28,16 @@ class BlizzardPlatform extends RegistryLookupGamePlatformBase { games := [] for index, productData in productInstalls { - launcherSpecificId := productData["productCode"] + platformRef := productData["productCode"] - if (launcherSpecificId != "agent" && launcherSpecificId != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) { + if (platformRef != "agent" && platformRef != "bna" && productData.Has("settings") && productData["settings"].Has("installPath")) { installPath := productData["settings"]["installPath"] installPath := StrReplace(installPath, "/", "\") SplitPath(installPath, &key) locator := GameExeLocator(installPath) possibleExes := locator.Locate("") mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installPath, mainExe, platformRef, possibleExes)) } } diff --git a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk index b42c67bc..1cdb2845 100644 --- a/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk +++ b/Lib/Launchpad/Modules/Epic/GamePlatform/EpicPlatform.ahk @@ -57,13 +57,13 @@ class EpicPlatform extends RegistryLookupGamePlatformBase { displayName := obj.Has("DisplayName") ? obj["DisplayName"] : "" installDir := obj["InstallLocation"] - launcherSpecificId := obj["AppName"] + platformRef := obj["AppName"] ;exeName := obj["LaunchExecutable"] ;possibleExes := [obj["LaunchExecutable"]] locator := GameExeLocator(installDir) possibleExes := locator.Locate("") mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes, displayName)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes, displayName)) } } } diff --git a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk b/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk index ef37ab9e..091b1bf5 100644 --- a/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk +++ b/Lib/Launchpad/Modules/Riot/GamePlatform/RiotPlatform.ahk @@ -66,7 +66,7 @@ class RiotPlatform extends RegistryLookupGamePlatformBase { return dirs } - GetLauncherSpecificId(key) { + GetPlatformRef(key) { if (key == "VALORANT") { key := "valorant" } else if (key == "Legends of Runeterra") { diff --git a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk b/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk index 3d88dfad..644f96ff 100644 --- a/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk +++ b/Lib/Launchpad/Modules/Steam/GamePlatform/SteamPlatform.ahk @@ -64,7 +64,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase { if (IsObject(obj) && obj.Has("AppState")) { gameState := obj["AppState"] - launcherSpecificId := gameState["appid"] + platformRef := gameState["appid"] key := gameState["name"] installDir := dir . "\common\" . gameState["installdir"] installDir := StrReplace(installDir, "/", "\") @@ -76,7 +76,7 @@ class SteamPlatform extends RegistryLookupGamePlatformBase { } mainExe := this.DetermineMainExe(key, possibleExes) - games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, launcherSpecificId, possibleExes)) + games.Push(DetectedGame(key, this, this.launcherType, this.gameType, installDir, mainExe, platformRef, possibleExes)) } } } diff --git a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk index 5eafec37..c88ed59b 100644 --- a/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk +++ b/Lib/LaunchpadLauncher/Game/BlizzardGame.ahk @@ -7,7 +7,7 @@ class BlizzardGame extends SimpleGame { launcherPath := this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] if (launcherPath != "") { - gameKey := this.config["GameLauncherSpecificId"] + gameKey := this.config["GamePlatformRef"] launcherPath .= " --game=" . gameKey . " --gamepath=`"" . this.config["GameInstallDir"] . "`" --productcode=" . gameKey } diff --git a/Lib/LaunchpadLauncher/Game/RiotGame.ahk b/Lib/LaunchpadLauncher/Game/RiotGame.ahk index e4edda9b..1d63a6d2 100644 --- a/Lib/LaunchpadLauncher/Game/RiotGame.ahk +++ b/Lib/LaunchpadLauncher/Game/RiotGame.ahk @@ -3,7 +3,7 @@ launcherPath := "`"" . this.app["Launcher"].config["LauncherInstallDir"] . "\" . this.app["Launcher"].config["LauncherExe"] . "`"" if (launcherPath != "") { - gameKey := this.config["GameLauncherSpecificId"] + gameKey := this.config["GamePlatformRef"] launcherPath .= " --launch-product=" . gameKey . " --launch-patchline=live" } From cbd8a79e3b5309e508bf6ed7a436b5a92431bf35 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:50:02 -0500 Subject: [PATCH 081/138] Fix 0 array index in VersionChecker --- Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk b/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk index fdf689d8..fe00f734 100644 --- a/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk +++ b/Lib/Shared/Volantis.Utility/VersionChecker/VersionChecker.ahk @@ -77,7 +77,7 @@ class VersionChecker { } incrementIndex := versionArr.Length - 1 - } else if (versionArr[0] == "0") { + } else if (versionArr[1] == "0") { incrementIndex := versionArr.Length } From 85ffdca84d68ee31d351272a065490317813b08a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:50:54 -0500 Subject: [PATCH 082/138] Throw an exception if unable to delete include file --- .../Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk b/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk index cb9e37da..aab7342d 100644 --- a/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk +++ b/Lib/Shared/Volantis.Utility/IncludeWriter/IncludeWriterBase.ahk @@ -25,7 +25,12 @@ class IncludeWriterBase { updated := this.FilesAreDifferent(this.tmpPath, this.outputPath) if (updated) { - FileDelete(this.outputPath) + try { + FileDelete(this.outputPath) + } catch Any { + throw AppException("Unable to delete file path " . this.outputPath) + } + } } From bd08d3dd2adb7c70b9a15d0eed9626531f796c91 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:51:49 -0500 Subject: [PATCH 083/138] Add NullEntityStorage class for use if you don't want any backend --- Lib/Shared/Includes.ahk | 1 + Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index d33ef65f..e65b65cb 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -220,6 +220,7 @@ #Include Volantis.Entity\EntityManager\EntityManagerBase.ahk #Include Volantis.Entity\EntityStorage\ConfigEntityStorage.ahk #Include Volantis.Entity\EntityStorage\EntityStorageBase.ahk +#Include Volantis.Entity\EntityStorage\NullEntityStorage.ahk #Include Volantis.Entity\EntityType\BasicEntityType.ahk #Include Volantis.Entity\EntityType\EntityTypeBase.ahk #Include Volantis.Entity\Event\EntityDataProcessorsEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk b/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk new file mode 100644 index 00000000..38cb4c2b --- /dev/null +++ b/Lib/Shared/Volantis.Entity/EntityStorage/NullEntityStorage.ahk @@ -0,0 +1,3 @@ +class NullEntityStorage extends EntityStorageBase { + +} From 303f4054f584d0fe9c5e547296ee01588e0b6ef4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:53:49 -0500 Subject: [PATCH 084/138] Update WebService module events --- Lib/Shared/Includes.ahk | 5 +++-- .../WebServicesEntityDataParamsEvent.ahk | 20 +++++++++++++++++++ ...tEvent.ahk => WebServicesRequestEvent.ahk} | 2 +- ...Event.ahk => WebServicesResponseEvent.ahk} | 0 .../WebServices/Events/WebServicesEvents.ahk | 13 ++++++------ 5 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk rename Lib/Shared/Modules/WebServices/Event/{WebServiceRequestEvent.ahk => WebServicesRequestEvent.ahk} (83%) rename Lib/Shared/Modules/WebServices/Event/{WebServiceResponseEvent.ahk => WebServicesResponseEvent.ahk} (100%) diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index e65b65cb..041ac818 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -2,8 +2,9 @@ #Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk #Include Modules\WebServices\Entity\WebServiceEntity.ahk #Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk -#Include Modules\WebServices\Event\WebServiceRequestEvent.ahk -#Include Modules\WebServices\Event\WebServiceResponseEvent.ahk +#Include Modules\WebServices\Event\WebServicesEntityDataParamsEvent.ahk +#Include Modules\WebServices\Event\WebServicesRequestEvent.ahk +#Include Modules\WebServices\Event\WebServicesResponseEvent.ahk #Include Modules\WebServices\Events\WebServicesEvents.ahk #Include Modules\WebServices\EventSubscriber\WebServicesEventSubscriber.ahk #Include Modules\WebServices\Factory\WebServiceAdapterFactory.ahk diff --git a/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk new file mode 100644 index 00000000..ad6ba482 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/Event/WebServicesEntityDataParamsEvent.ahk @@ -0,0 +1,20 @@ +class WebServicesEntityDataParamsEvent extends EntityEvent { + _webService := "" + _params := "" + + __New(eventName, entityTypeId, entityObj, webService, params) { + this._webService := webService + this._params := params + + super.__New(eventName, entityTypeId, entityObj) + } + + WebService { + get => this._webService + } + + Params { + get => this._params + set => this._params := value + } +} diff --git a/Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk similarity index 83% rename from Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk rename to Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk index 1b8ba941..d52a8c42 100644 --- a/Lib/Shared/Modules/WebServices/Event/WebServiceRequestEvent.ahk +++ b/Lib/Shared/Modules/WebServices/Event/WebServicesRequestEvent.ahk @@ -1,4 +1,4 @@ -class WebServiceRequestEvent extends EventBase { +class WebServicesRequestEvent extends EventBase { _requestObj := "" __New(eventName, requestObj) { diff --git a/Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk similarity index 100% rename from Lib/Shared/Modules/WebServices/Event/WebServiceResponseEvent.ahk rename to Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk diff --git a/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk index 787d5a1a..bde6b479 100644 --- a/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk +++ b/Lib/Shared/Modules/WebServices/Events/WebServicesEvents.ahk @@ -1,7 +1,8 @@ class WebServicesEvents { - static WEB_SERVICES_HTTP_REQ_ALTER := 0x4200 - static WEB_SERVICES_REQUEST_PRESEND := 0x4210 - static WEB_SERVICES_CACHED_RESPONSE_CREATED := 0x4215 - static WEB_SERVICES_HTTP_RESPONSE_CREATED := 0x4217 - static WEB_SERVICES_RESPONSE_ALTER := 0x4220 -} \ No newline at end of file + static HTTP_REQ_ALTER := 0x4200 + static REQUEST_PRESEND := 0x4210 + static CACHED_RESPONSE_CREATED := 0x4215 + static HTTP_RESPONSE_CREATED := 0x4217 + static RESPONSE_ALTER := 0x4220 + static ENTITY_DATA_PARAMS := 0x4225 +} From e0c2b7ed6c622a247dc09b94ce17955c1c3606e2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:54:27 -0500 Subject: [PATCH 085/138] Add an availability_check web service data type --- Lib/Shared/Modules/WebServices/WebServices.module.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 00854c63..c00967b0 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -74,6 +74,10 @@ "name": "Entity Data", "description": "Data to be imported into an entity within the application." }, + "web_services.data_types.availability_check": { + "name": "Availability check", + "description": "Simply checks for any response from the web service." + }, "web_services.adapter_types.json": { "class": "JsonWebServiceAdapter" }, From b7e6f7cdb2bfe13c4e9013aff0b94897f670984b Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:55:18 -0500 Subject: [PATCH 086/138] Return this from EntityStorageLayerSource DeleteData method --- .../Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk b/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk index b58e5bb9..ec182c25 100644 --- a/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk +++ b/Lib/Shared/Volantis.Entity/LayerSource/EntityStorageLayerSource.ahk @@ -24,5 +24,7 @@ class EntityStorageLayerSource extends LayerSourceBase { if (this.HasData()) { this.storageObj.DeleteData(this.storageId) } + + return this } } From a91c24c57672462d90fced85ab743fdb8babcc0c Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:57:44 -0500 Subject: [PATCH 087/138] Simple language update --- Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk b/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk index 21754d91..900a7adb 100644 --- a/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk +++ b/Lib/LaunchpadLauncher/Launcher/LauncherBase.ahk @@ -189,7 +189,7 @@ class LauncherBase { } LaunchGameAction() { - this.Log("Calling managed game's RunGame action") + this.Log("Calling game process's RunGame action") return this.game.RunGame(this.progress) } From 94d327e08a6b9ff1208a5f457869bb5c760c55b4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 02:59:13 -0500 Subject: [PATCH 088/138] Define a WebServiceAdapter service --- Lib/Shared/Includes.ahk | 1 + .../WebServiceAdapterManager.ahk | 287 ++++++++++++++++++ .../WebServices/WebServices.module.json | 4 + 3 files changed, 292 insertions(+) create mode 100644 Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index 041ac818..c5d4ec96 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -1,5 +1,6 @@ ; Automatically-generated file. Manual edits will be overwritten. #Include Modules\LaunchpadApi\EventSubscriber\LaunchpadApiSubscriber.ahk +#Include Modules\WebServices\ComponentManager\WebServiceAdapterManager.ahk #Include Modules\WebServices\Entity\WebServiceEntity.ahk #Include Modules\WebServices\Entity\WebServiceProviderEntity.ahk #Include Modules\WebServices\Event\WebServicesEntityDataParamsEvent.ahk diff --git a/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk b/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk new file mode 100644 index 00000000..b4e81fd3 --- /dev/null +++ b/Lib/Shared/Modules/WebServices/ComponentManager/WebServiceAdapterManager.ahk @@ -0,0 +1,287 @@ +class WebServiceAdapterManager { + container := "" + parameterPrefix := "" + adapterFactory := "" + entityTypeMgr := "" + eventMgr := "" + adapters := Map() + + __New(container, parameterPrefix, adapterFactory, entityTypeMgr, eventMgr) { + this.container := container + this.parameterPrefix := parameterPrefix + this.adapterFactory := adapterFactory + this.entityTypeMgr := entityTypeMgr + this.eventMgr := eventMgr + } + + AdapterRequest(params, filters, operation := "read", multiple := false, webService := "") { + if (!params) { + params := Map() + } + + if (!filters) { + filters := Map() + } + + if (Type(filters) == "String") { + filters := Map("dataType", filters) + } + + results := Map() + + for adapterKey, adapter in this.GetAdapters(filters, operation, 0, webService) { + result := adapter.SendRequest(operation, params) + + if (result) { + if (!multiple) { + results := result + + break + } + + results[adapterKey] := result + } + + if (IsNumber(multiple) && results.Count >= multiple) { + break + } + } + + return results + } + + HasAdapters(filters := "", operation := "", webService := "") { + return !!(this.GetAdapterIds(filters, operation, 1, webService).Length) + } + + GetAdapters(filters := "", operation := "", limit := 0, webService := "") { + adapterIds := this.GetAdapterIds(filters, operation, limit, webService) + + adapters := Map() + + for , adapterId in adapterIds { + adapters[adapterId] := this.GetAdapter(adapterId) + } + + return adapters + } + + GetAdapter(id) { + adapter := "" + + if (this.adapters.Has(id)) { + adapter := this.adapters[id] + } + + if (!adapter && InStr(id, ".")) { + idParts := StrSplit(id, ".") + webServiceId := idParts[1] + adapterKey := idParts[2] + + webService := this.entityTypeMgr.GetManager("web_service")[webServiceId] + + if (webService["Enabled"]) { + param := this.parameterPrefix . webService["Provider"]["id"] . "." . adapterKey + + if (this.container.HasParameter(param)) { + adapter := this.adapterFactory.CreateWebServiceAdapter(webService, this.container.GetParameter(param)) + this.adapters[id] := adapter + } + } + } + + return adapter + } + + HasAdapter(id) { + exists := this.adapters.Has(id) + + if (!exists) { + idParts := StrSplit(id, ".") + webServiceId := idParts[1] + adapterKey := idParts[2] + webService := this.entityTypeMgr.GetManager("web_service")[webServiceId] + param := this.parameterPrefix . webService["Provider"]["id"] . "." . id + exists := this.container.HasParameter(param) + } + + return exists + } + + GetAdapterIds(filters := "", operation := "", limit := 0, webService := "") { + if (!filters) { + filters := Map() + } + + if (Type(filters) == "String") { + filters := Map("dataType", filters) + } + + adapterIds := [] + weights := this._getFilterWeights(filters) + + for webServiceId, webService in this._getWebServicesForOperation(webService) { + providerId := webService["Provider"]["id"] + paramKey := "web_services.adapters." . providerId + + if (this.container.HasParameter(paramKey)) { + adapterData := this.container.GetParameter(this.parameterPrefix . providerId) + + for weightIndex, weight in weights { + filters["weight"] := weight + + for key, definition in adapterData { + adapterId := webServiceId . "." . key + adapter := this.GetAdapter(adapterId) + definition := adapter.definition + include := (!operation || adapter.SupportsOperation(operation)) + + if (include) { + for filterKey, filterVal in filters { + if (!definition.Has(filterKey)) { + include := false + + break + } + + include := this._filterValue(definition[filterKey], filterVal) + + if (!include) { + break + } + } + } + + if (include) { + adapterIds.Push(adapterId) + + if (limit && adapterIds.Length >= limit) { + break 2 + } + } + } + } + } + } + + return adapterIds + } + + _getWebServicesForOperation(webService) { + webServices := "" + + if (webService) { + webServices := Type(webService == "String") ? Map(webService["id"], webService) : webService + } else { + webServices := this.entityTypeMgr.GetManager("web_service") + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + } + + return webServices + } + + _getFilterWeights(filters) { + weights := filters.Has("weight") + ? filters["weight"] + : "" + + if (!weights) { + weights := [] + + startingWeight := -10 + maxWeight := 10 + + Loop (maxWeight - startingWeight) { + weights.Push(startingWeight + A_Index - 1) + } + } + + if (Type(weights) == "String") { + weights := [weights] + } + + return weights + } + + _filterArrayValues(definitionArray, filterArray) { + include := !filterArray || !!definitionArray + + if (include) { + if (Type(filterArray) == "String") { + filterArray := [filterArray] + } + + if (Type(definitionArray) == "String") { + definitionArray := [definitionArray] + } + + for , val in filterArray { + definitionHasVal := false + + for , definitionVal in definitionArray { + definitionHasVal := this._filterValue(definitionVal, val) + + if (definitionVal == val) { + definitionHasVal := true + + break + } + } + + if (!definitionHasVal) { + include := false + + break + } + } + } + + return include + } + + _filterMapValues(definitionMap, filterMap) { + include := !filterMap || !!definitionMap + + if (include) { + if (Type(filterMap) == "String") { + filterMap := [filterMap] + } + + if (Type(definitionMap) == "String") { + definitionMap := [definitionMap] + } + + for key, val in filterMap { + exists := definitionMap.Has(key) + + if (exists) { + exists := this._filterValue(definitionMap[key], val) + } + + if (!exists) { + include := false + + break + } + } + } + + return include + } + + _filterValue(definitionVal, filterVal) { + include := false + + if (Type(filterVal) == "Array") { + include := this._filterArrayValues(definitionVal, filterVal) + } else if (Type(filterVal) == "Map") { + include := this._filterMapValues(definitionVal, filterVal) + } else { + include := (definitionVal == filterVal) + } + + return include + } +} diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index c00967b0..99634be6 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -114,6 +114,10 @@ "web_services.adapter_factory": { "class": "WebServiceAdapterFactory", "arguments": ["@{}"] + }, + "web_services.adapter_manager": { + "class": "WebServiceAdapterManager", + "arguments": ["@{}", "web_services.adapters.", "@web_services.adapter_factory", "@manager.entity_type", "@manager.event"] } } } From 284c38f20fef15f7fb860e68cb165d27d4742807 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:07:25 -0500 Subject: [PATCH 089/138] Add an option to store child entity data inside the parent's entity storage data object --- Lib/Shared/Includes.ahk | 1 + .../LayeredData/LayeredDataBase.ahk | 50 +++++++++++++++++ .../Volantis.Entity/Entity/EntityBase.ahk | 42 +++++++++++--- .../Factory/EntityTypeFactory.ahk | 1 + .../LayerSource/ParentEntityLayerSource.ahk | 55 +++++++++++++++++++ 5 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index c5d4ec96..a96a19ed 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -245,6 +245,7 @@ #Include Volantis.Entity\Factory\EntityTypeFactory.ahk #Include Volantis.Entity\LayeredData\EntityData.ahk #Include Volantis.Entity\LayerSource\EntityStorageLayerSource.ahk +#Include Volantis.Entity\LayerSource\ParentEntityLayerSource.ahk #Include Volantis.Entity\Query\EntityQuery.ahk #Include Volantis.Entity\Validator\BasicValidator.ahk #Include Volantis.Entity\Validator\ValidatorBase.ahk diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index 423b9889..b1a18acd 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -25,6 +25,8 @@ class LayeredDataBase { cloner := "" userLayers := ["data"] loadingLayers := Map() + extraDataLayer := "data" + extraDataKey := "extra" static NO_VALUE := ":NO_VAL:" @@ -255,6 +257,54 @@ class LayeredDataBase { } } + GetExtraData(key := "") { + extraData := this.GetValue(this.extraDataKey, false, this.extraDataLayer, Map()) + + if (key) { + extraData := extraData.Has(key) ? extraData[key] : Map() + } + + return extraData + } + + SetExtraData(value, key := "") { + if (key) { + extraData := this.GetExtraData() + extraData[key] := value + value := extraData + } + + this.SetValue(this.extraDataKey, value, this.extraDataLayer) + + return this + } + + HasExtraData(key := "") { + hasData := this.HasValue(this.extraDataKey, this.extraDataLayer, false) + + if (hasData && key) { + extraData := this.GetExtraData() + hasData := extraData.Has(key) + } + + return hasData + } + + DeleteExtraData(key := "") { + if (key) { + extraData := this.GetExtraData() + + if (extraData.Has(key)) { + extraData.Delete(key) + this.SetExtraData(extraData) + } + } else { + this.DeleteValue(this.extraDataKey, this.extraDataLayer) + } + + return this + } + /** key: The key to retrieve diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index cc2562ea..12c624e0 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -461,15 +461,41 @@ class EntityBase { return text } - UpdateDefaults(recurse := true) { - if (this.HasOwnDataStorage()) { - this.GetData().UnloadAllLayers(false) - } + GetAllChildEntityData() { + return this.GetData().GetExtraData() + } - if (recurse) { - for key, child in this.GetReferencedEntities(true) { - child.UpdateDefaults(recurse) - } + GetChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + childData := this.GetData().GetExtraData(dataKey) + + return childData ? childData : Map() + } + + SetChildEntityData(entityTypeId, entityId, data) { + dataKey := entityTypeId . "." . entityId + + if (!data) { + data := Map() } + + this.GetData().SetExtraData(dataKey, data) + + return this + } + + HasChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + return this.GetData().HasExtraData(dataKey) + } + + DeleteChildEntityData(entityTypeId, entityId) { + dataKey := entityTypeId . "." . entityId + + this.GetData().DeleteExtraData(dataKey) + + return this } } diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk index 8383c8fa..fe637a5b 100644 --- a/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk +++ b/Lib/Shared/Volantis.Entity/Factory/EntityTypeFactory.ahk @@ -30,6 +30,7 @@ class EntityTypeFactory { "event_manager", "manager.event", "notifier", "notifier", "parent_entity_type", "", + "parent_entity_storage", false, "default_icon", "cube-outline", "icon_field", "IconSrc", "allow_view", false, diff --git a/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk b/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk new file mode 100644 index 00000000..dfa685aa --- /dev/null +++ b/Lib/Shared/Volantis.Entity/LayerSource/ParentEntityLayerSource.ahk @@ -0,0 +1,55 @@ +class ParentEntityLayerSource extends LayerSourceBase { + entityObj := "" + + __New(entityObj) { + this.entityObj := entityObj + } + + SaveData(data := "") { + this._validateParentEntity() + + this.entityObj.ParentEntity + .SetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id, data) + + return this + } + + LoadData() { + this._validateParentEntity() + + return this.entityObj.ParentEntity + .GetChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + } + + HasData() { + this._validateParentEntity() + + return this.entityObj.ParentEntity + .HasChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + } + + DeleteData() { + this._validateParentEntity() + + this.entityObj.ParentEntity + .DeleteChildEntityData(this.entityObj.EntityTypeId, this.entityObj.Id) + + return this + } + + _validateParentEntity() { + if (!this.entityObj.ParentEntity) { + throw AppException("Parent entity not set.") + } + + if (!HasBase(this.entityObj.ParentEntity, EntityBase.Prototype)) { + throw AppException("Parent entity is not an entity.") + } + + parentData := this.entityObj.ParentEntity.GetData() + + if (!parentData) { + throw AppException("Parent entity data is not set.") + } + } +} From 17ce18ad694a602987360d20c0386849264d5717 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:08:33 -0500 Subject: [PATCH 090/138] Add an event that can be fired to determine an entity's parent entity --- Lib/Shared/Includes.ahk | 1 + .../Event/EntityParentEvent.ahk | 28 +++++++++++++++++++ .../Volantis.Entity/Events/EntityEvents.ahk | 1 + 3 files changed, 30 insertions(+) create mode 100644 Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk diff --git a/Lib/Shared/Includes.ahk b/Lib/Shared/Includes.ahk index a96a19ed..faff2db3 100644 --- a/Lib/Shared/Includes.ahk +++ b/Lib/Shared/Includes.ahk @@ -233,6 +233,7 @@ #Include Volantis.Entity\Event\EntityLayersEvent.ahk #Include Volantis.Entity\Event\EntityLayerSourcesEvent.ahk #Include Volantis.Entity\Event\EntityListEvent.ahk +#Include Volantis.Entity\Event\EntityParentEvent.ahk #Include Volantis.Entity\Event\EntityReferenceEvent.ahk #Include Volantis.Entity\Event\EntityRefreshEvent.ahk #Include Volantis.Entity\Event\EntityValidateEvent.ahk diff --git a/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk b/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk new file mode 100644 index 00000000..c95ed335 --- /dev/null +++ b/Lib/Shared/Volantis.Entity/Event/EntityParentEvent.ahk @@ -0,0 +1,28 @@ +class EntityParentEvent extends EntityEvent { + _parentEntity := "" + _parentEntityTypeId := "" + _parentEntityId := "" + + __New(eventName, entityTypeId, entityObj, parentEntity := "", parentEntityTypeId := "", parentEntityId := "") { + this._parentEntity := parentEntity + this._parentEntityTypeId := parentEntityTypeId + this._parentEntityId := parentEntityId + + super.__New(eventName, entityTypeId, entityObj) + } + + ParentEntity { + get => this._parentEntity + set => this._parentEntity := value + } + + ParentEntityTypeId { + get => this._parentEntityTypeId + set => this._parentEntityTypeId := value + } + + ParentEntityId { + get => this._parentEntityId + set => this._parentEntityId := value + } +} diff --git a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk index 3e0a883b..2d2ed47a 100644 --- a/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk +++ b/Lib/Shared/Volantis.Entity/Events/EntityEvents.ahk @@ -24,4 +24,5 @@ class EntityEvents { static ENTITY_FIELD_GROUPS_ALTER := 0x4092 static ENTITY_REFERENCE_ENTITY_SAVED := 0x4095 static ENTITY_LIST_ENTITIES := 0x4098 + static ENTITY_DISCOVER_PARENT := 0x4100 } From 78b14b415d444b24b7a243a1d65fd5caf7667a21 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:09:22 -0500 Subject: [PATCH 091/138] Refactor EntityData so that passing in layer names and layer sources is optional, and the events are handled in existing overridden functions instead of new ones --- .../LayeredData/EntityData.ahk | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk index 3086142b..26addd79 100644 --- a/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk +++ b/Lib/Shared/Volantis.Entity/LayeredData/EntityData.ahk @@ -3,20 +3,47 @@ class EntityData extends LayeredDataBase { entity := "" eventMgr := "" - __New(entity, layerNames, layerSources) { + __New(entity, layerNames := "", layerSources := "") { this.entityTypeId := entity.EntityTypeId this.entity := entity this.eventMgr := entity.eventMgr super.__New( entity.cloner, - this._createProcessors(), - this._collectLayerNames(layerNames), - this._collectSources(layerSources) + this._createProcessors(), + layerNames, + layerSources ) } - _collectSources(layerSources) { + InitializeLayers(layerNames) { + if (!layerNames) { + layerNames := [] + } + + this._appendLayerNames(["defaults"], layerNames) + + event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames) + this.eventMgr.DispatchEvent(event) + + layerNames := event.Layers + this._appendLayerNames(["auto", "data"], layerNames) + + event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames) + this.eventMgr.DispatchEvent(event) + + layerNames := event.Layers + layers := Map() + + for index, layerName in layerNames { + this.layerPriority.Push(layerName) + layers[layerName] := Map() + } + + this.SetLayers(layers) + } + + SetLayerSources(layerSources) { if (!layerSources.Has("defaults")) { layerSources["defaults"] := ObjBindMethod(this.entity, "InitializeDefaults") } @@ -27,7 +54,9 @@ class EntityData extends LayeredDataBase { event := EntityLayerSourcesEvent(EntityEvents.ENTITY_LAYER_SOURCES_ALTER, this.entityTypeId, this.entity, event.LayerSources) this.eventMgr.DispatchEvent(event) - return event.LayerSources + for key, source in event.LayerSources { + this.SetLayerSource(key, source) + } } _createProcessors() { @@ -45,25 +74,6 @@ class EntityData extends LayeredDataBase { return event.Processors } - _collectLayerNames(layerNames) { - if (!layerNames) { - layerNames := [] - } - - this._appendLayerNames(["defaults"], layerNames) - - event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS, this.entityTypeId, this.entity, layerNames) - this.eventMgr.DispatchEvent(event) - - layerNames := event.Layers - this._appendLayerNames(["auto", "data"], layerNames) - - event := EntityLayersEvent(EntityEvents.ENTITY_DATA_LAYERS_ALTER, this.entityTypeId, this.entity, layerNames) - this.eventMgr.DispatchEvent(event) - - return event.Layers - } - _appendLayerNames(namesToAppend, existingNames) { for index, name in namesToAppend { exists := false From b12a217b8c57b9814da28526da371d07ca817884 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:14:38 -0500 Subject: [PATCH 092/138] Fix entity constructors (Except Managed entities because they will be committed next --- .../WebServices/Entity/WebServiceEntity.ahk | 100 ++---------------- .../Volantis.Entity/Entity/EntityBase.ahk | 10 +- .../Entity/FieldableEntity.ahk | 10 +- .../Volantis.Entity/Factory/EntityFactory.ahk | 2 +- 4 files changed, 20 insertions(+), 102 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index dd10622c..411e4f2a 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -19,23 +19,21 @@ class WebServiceEntity extends FieldableEntity { set => this.SetAuthData(key, value) } - __New(id, entityTypeId, container, adapterFactory, cacheObj, stateObj, persistentStateObj, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, parentEntity := "") { + __New(id, entityTypeId, container, cacheObj, stateObj, persistentStateObj, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { this.cacheObj := cacheObj this.stateObj := stateObj this.persistentStateObj := persistentStateObj - this.adapterFactory := adapterFactory - super.__New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, parentEntity) + super.__New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage) } - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { className := this.Prototype.__Class return %className%( id, entityTypeId, container, - container.Get("web_services.adapter_factory"), container.Get("cache.web_services"), container.Get("state.web_services_tmp"), container.Get("state.web_services"), @@ -44,98 +42,12 @@ class WebServiceEntity extends FieldableEntity { eventMgr, storageObj, idSanitizer, - parentEntity + autoLoad, + parentEntity, + parentEntityStorage ) } - AdapterRequest(params, adapterFilters, operation := "read", multiple := false) { - if (!adapterFilters) { - adapterFilters := Map() - } - - if (!adapterFilters) { - adapterFilters := Map() - } - - if (Type(adapterFilters) == "String") { - adapterFilters := Map("adapterType", adapterFilters) - } - - results := Map() - - for adapterKey, adapter in this.GetAdapters(adapterFilters, operation) { - result := adapter.SendRequest(operation, params) - - if (result) { - if (!multiple) { - results := result - break - } - - results[adapterKey] := result - } - - if (IsNumber(multiple) && results.Count >= multiple) { - break - } - } - - return results - } - - GetAdapters(filters := "", operation := "") { - if (!filters) { - filters := Map() - } - - adapterData := this.container.GetParameter("web_services.adapters." . this["Provider"]["id"]) - - adapters := Map() - - for key, definition in adapterData { - adapter := this.GetAdapter(key) - definition := adapter.definition - include := true - - for filterKey, filterVal in filters { - if (!definition.Has(filterKey) || definition[filterKey] != filterVal) { - include := false - - break - } - } - - if (include && operation) { - include := adapter.SupportsOperation(operation) - } - - if (include) { - adapters[key] := adapter - } - } - - return adapters - } - - GetAdapter(key) { - adapter := "" - - if (this.adapters.Has(key)) { - adapter := this.adapters[key] - } - - if (!adapter) { - param := "web_services.adapters." . this["Provider"]["id"] - - if (this.container.HasParameter(param)) { - adapter := this.adapterFactory.CreateWebServiceAdapter(this, this.container.GetParameter(param)) - this.adapters[key] := adapter - } - } - - return adapter - } - BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 12c624e0..da91df35 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -2,6 +2,7 @@ class EntityBase { idVal := "" entityTypeIdVal := "" parentEntityObj := "" + parentEntityStorage := false container := "" app := "" eventMgr := "" @@ -57,7 +58,7 @@ class EntityBase { return this.GetAllValues().__Enum(numberOfVars) } - __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "") { + __New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) { this.app := container.GetApp() this.idSanitizer := idSanitizer @@ -72,6 +73,7 @@ class EntityBase { this.storageObj := storageObj this.merger := container.Get("merger.list") this.cloner := container.Get("cloner.list") + this.parentEntityStorage := parentEntityStorage if (!parentEntity) { parentEntity := this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) @@ -89,7 +91,7 @@ class EntityBase { } } - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { className := this.Prototype.__Class return %className%( @@ -99,7 +101,9 @@ class EntityBase { eventMgr, storageObj, idSanitizer, - parentEntity + autoLoad, + parentEntity, + parentEntityStorage ) } diff --git a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk index e4e43d60..d39183b0 100644 --- a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk @@ -11,14 +11,14 @@ class FieldableEntity extends EntityBase { get => this.GetFieldDefinitions() } - __New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer := "", autoLoad := true) { + __New(id, entityTypeId, container, fieldFactory, widgetFactory, eventMgr, storageObj, idSanitizer := "", autoLoad := true, parentEntity := "", parentEntityStorage := false) { this.entityFieldFactory := fieldFactory this.entityWidgetFactory := widgetFactory - super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad) + super.__New(id, entityTypeId, container, eventMgr, storageObj, idSanitizer, autoLoad, parentEntity, parentEntityStorage) } - static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, parentEntity := "") { + static Create(container, eventMgr, id, entityTypeId, storageObj, idSanitizer, autoLoad := true, parentEntity := "", parentEntityStorage := false) { className := this.Prototype.__Class return %className%( @@ -30,7 +30,9 @@ class FieldableEntity extends EntityBase { eventMgr, storageObj, idSanitizer, - parentEntity + autoLoad, + parentEntity, + parentEntityStorage ) } diff --git a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk b/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk index 19af31bc..7d45933b 100644 --- a/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk +++ b/Lib/Shared/Volantis.Entity/Factory/EntityFactory.ahk @@ -110,6 +110,6 @@ class EntityFactory { throw EntityException("Unable to create entity '" . id . "' of type '" . entityTypeObj . "' in EntityFactory") } - return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, parentEntity) + return %entityTypeObj%.Create(this.container, this.eventMgr, id, this.entityTypeId, this.storageObj, this.idSanitizer, true, parentEntity, this.definition["parent_entity_storage"]) } } From 760826dd01791d14973568a4c47c3b52da0e18c7 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:21:41 -0500 Subject: [PATCH 093/138] Change ManagedEntityBase into LaunchProcessEntity, ManagedLauncherEntity into LauncherProcessEntity, and ManagedGameEntity into GameProcessEntity --- Launchpad.services.json | 8 +- .../Builder/BuildFile/GameAhkFile.ahk | 4 +- .../Builder/BuildFile/ShortcutFile.ahk | 4 +- Lib/Launchpad/Builder/BuilderBase.ahk | 2 +- Lib/Launchpad/DetectedGame/DetectedGame.ahk | 32 +- ...edGameEntity.ahk => GameProcessEntity.ahk} | 38 +- ...EntityBase.ahk => LaunchProcessEntity.ahk} | 134 ++--- Lib/Launchpad/Entity/LauncherEntity.ahk | 16 +- ...erEntity.ahk => LauncherProcessEntity.ahk} | 32 +- Lib/Launchpad/Entity/ManagedProcessEntity.ahk | 533 ------------------ Lib/Launchpad/Includes.ahk | 7 +- .../App/LaunchpadLauncher.ahk | 4 +- 12 files changed, 111 insertions(+), 703 deletions(-) rename Lib/Launchpad/Entity/{ManagedGameEntity.ahk => GameProcessEntity.ahk} (72%) rename Lib/Launchpad/Entity/{ManagedEntityBase.ahk => LaunchProcessEntity.ahk} (81%) rename Lib/Launchpad/Entity/{ManagedLauncherEntity.ahk => LauncherProcessEntity.ahk} (80%) delete mode 100644 Lib/Launchpad/Entity/ManagedProcessEntity.ahk diff --git a/Launchpad.services.json b/Launchpad.services.json index bf32f18e..59a88e39 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -42,18 +42,18 @@ "allow_delete": true, "manager_gui": "MainWindow" }, - "entity_type.managed_game": { + "entity_type.game_process": { "name_singular": "Managed Game", "name_plural": "Managed Games", - "entity_class": "ManagedGameEntity", + "entity_class": "GameProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", "parent_entity_type": "launcher" }, - "entity_type.managed_launcher": { + "entity_type.launcher_process": { "name_singular": "Managed Launcher", "name_plural": "Managed Launchers", - "entity_class": "ManagedLauncherEntity", + "entity_class": "LauncherProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", "parent_entity_type": "launcher" diff --git a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk index 230f51e6..d1cbe8cd 100644 --- a/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk +++ b/Lib/Launchpad/Builder/BuildFile/GameAhkFile.ahk @@ -14,9 +14,9 @@ class GameAhkFile extends ComposableBuildFile { "launcherName", this.launcherEntityObj.Id . " - Launchpad", "appVersion", appVersion, "appDir", this.appDir, - "gameConfig", this.launcherEntityObj["ManagedGame"].FieldData, + "gameConfig", this.launcherEntityObj["GameProcess"].FieldData, "launchpadLauncherConfig", this.launcherEntityObj.FieldData, - "launcherConfig", this.launcherEntityObj["ManagedLauncher"].FieldData, + "launcherConfig", this.launcherEntityObj["LauncherProcess"].FieldData, "launcherId", this.launcherEntityObj.Id, "themesDir", this.launcherEntityObj["ThemesDir"], "resourcesDir", this.launcherEntityObj["ResourcesDir"], diff --git a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk b/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk index 6f61ab18..d434a996 100644 --- a/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk +++ b/Lib/Launchpad/Builder/BuildFile/ShortcutFile.ahk @@ -6,14 +6,14 @@ class ShortcutFile extends CopyableBuildFile { if (destPath == "") { ext := ".lnk" - if (launcherEntityObj["ManagedGame"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["ManagedGame"]["ShortcutSrc"], -4) == ".url") { + if (launcherEntityObj["GameProcess"]["ShortcutSrc"] != "" && SubStr(launcherEntityObj["GameProcess"]["ShortcutSrc"], -4) == ".url") { ext := ".url" } destPath := launcherEntityObj["AssetsDir"] . "\" . launcherEntityObj.Id . ext } - super.__New(launcherEntityObj, launcherEntityObj["ManagedGame"]["ShortcutSrc"], destPath) + super.__New(launcherEntityObj, launcherEntityObj["GameProcess"]["ShortcutSrc"], destPath) } Locate() { diff --git a/Lib/Launchpad/Builder/BuilderBase.ahk b/Lib/Launchpad/Builder/BuilderBase.ahk index b9609894..d4f77787 100644 --- a/Lib/Launchpad/Builder/BuilderBase.ahk +++ b/Lib/Launchpad/Builder/BuilderBase.ahk @@ -32,7 +32,7 @@ class BuilderBase { } NeedsShortcutFile(launcherEntityObj) { - return (launcherEntityObj["ManagedGame"]["UsesShortcut"]) + return (launcherEntityObj["GameProcess"]["UsesShortcut"]) } BuildAction(launcherEntityObj, launcherDir, assetsDir) { diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 68aa54b2..0e6a966e 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -44,12 +44,10 @@ class DetectedGame { if ( this.displayName != launcher["name"] - || this.launcherType != launcher["ManagedLauncher"].EntityTypeId - || this.gameType != launcher["ManagedGame"].EntityTypeId - || this.installDir != launcher["ManagedGame"]["InstallDir"] - || this.launcherInstallDir != launcher["ManagedLauncher"]["InstallDir"] - || this.exeName != launcher["ManagedGame"]["Exe"] - || this.platformRef != launcher["ManagedGame"]["PlatformRef"] + || this.installDir != launcher["GameProcess"]["InstallDir"] + || this.launcherInstallDir != launcher["LauncherProcess"]["InstallDir"] + || this.exeName != launcher["GameProcess"]["Exe"] + || this.platformRef != launcher["GameProcess"]["PlatformRef"] ) { hasChanges := true } @@ -70,27 +68,17 @@ class DetectedGame { modified := true } - if (this.launcherType && launcher["ManagedLauncher"].EntityTypeId != this.launcherType) { - launcher["ManagedLauncher"].EntityType := this.launcherType - modified := true - } - - if (this.gameType && launcher["ManagedGame"].EntityTypeId != this.gameType) { - launcher["ManagedGame"].EntityType := this.gameType - modified := true - } - - if (this.launcherInstallDir && launcher["ManagedLauncher"]["InstallDir"] != this.launcherInstallDir) { - launcher["ManagedLauncher"]["InstallDir"] := this.launcherInstallDir + if (this.launcherInstallDir && launcher["LauncherProcess"]["InstallDir"] != this.launcherInstallDir) { + launcher["LauncherProcess"]["InstallDir"] := this.launcherInstallDir } - if (this.installDir && launcher["ManagedGame"]["InstallDir"] != this.installDir) { - launcher["ManagedGame"]["InstallDir"] := this.installDir + if (this.installDir && launcher["GameProcess"]["InstallDir"] != this.installDir) { + launcher["GameProcess"]["InstallDir"] := this.installDir modified := true } - if (this.exeName && launcher["ManagedGame"]["Exe"] != this.exeName) { - launcher["ManagedGame"]["Exe"] := this.exeName + if (this.exeName && launcher["GameProcess"]["Exe"] != this.exeName) { + launcher["GameProcess"]["Exe"] := this.exeName modified := true } diff --git a/Lib/Launchpad/Entity/ManagedGameEntity.ahk b/Lib/Launchpad/Entity/GameProcessEntity.ahk similarity index 72% rename from Lib/Launchpad/Entity/ManagedGameEntity.ahk rename to Lib/Launchpad/Entity/GameProcessEntity.ahk index 916ebe67..da93710b 100644 --- a/Lib/Launchpad/Entity/ManagedGameEntity.ahk +++ b/Lib/Launchpad/Entity/GameProcessEntity.ahk @@ -1,6 +1,4 @@ -class ManagedGameEntity extends ManagedEntityBase { - configPrefix := "Game" - defaultType := "Default" +class GameProcessEntity extends LaunchProcessEntity { defaultClass := "SimpleGame" BaseFieldDefinitions() { @@ -9,7 +7,7 @@ class ManagedGameEntity extends ManagedEntityBase { definitions["HasLoadingWindow"] := Map( "type", "boolean", "description", "Whether or not the game has a loading window to watch for.", - "storageKey", this.configPrefix . "HasLoadingWindow", + "storageKey", "HasLoadingWindow", "default", false ) @@ -19,7 +17,7 @@ class ManagedGameEntity extends ManagedEntityBase { definitions["LoadingWindowProcessType"] := Map( "description", "Which method to use to wait for the game's loading window to open.", "help", "This lets Launchpad know when the game is loading. Only used if a LoadingWindowProcessId is set", - "storageKey", this.configPrefix . "LoadingWindowProcessType", + "storageKey", "LoadingWindowProcessType", "default", "Exe", "widget", "select", "selectOptionsCallback", ObjBindMethod(this, "ListProcessTypes") @@ -29,7 +27,7 @@ class ManagedGameEntity extends ManagedEntityBase { ; - Title - This value will default to the game's Key unless overridden ; - Class - This value should be set to the game's window class definitions["LoadingWindowProcessId"] := Map( - "storageKey", this.configPrefix . "LoadingWindowProcessId", + "storageKey", "LoadingWindowProcessId", "help", "This value's type is dependent on the GameProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", "modes", Map( "simple", Map("formField", false) @@ -39,16 +37,6 @@ class ManagedGameEntity extends ManagedEntityBase { return definitions } - GetBlizzardProductKey() { - productKey := this["PlatformRef"] - - if (this.HasConfigValue("BlizzardProductId", true, false)) { - productKey := this.GetConfigValue("BlizzardProductId") - } - - return productKey - } - ShouldDetectShortcutSrc(extraConfig) { detectShortcut := false @@ -77,24 +65,24 @@ class ManagedGameEntity extends ManagedEntityBase { return detectShortcut } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) - exeKey := this.configPrefix . "Exe" + AutoDetectValues() { + detectedValues := super.AutoDetectValues() + exeKey := "Exe" if (!detectedValues.Has(exeKey)) { detectedValues[exeKey] := this["Exe"] ? this["Exe"] : this.Id . ".exe" } - if (!detectedValues.Has(this.configPrefix . "ProcessId") || !detectedValues[this.configPrefix . "ProcessId"]) { - detectedValues[this.configPrefix . "ProcessId"] := detectedValues[exeKey] + if (!detectedValues.Has("ProcessId") || !detectedValues["ProcessId"]) { + detectedValues["ProcessId"] := detectedValues[exeKey] } - if (detectedValues.Has(this.configPrefix . "ProcessType")) { - detectedValues[this.configPrefix . "LoadingWindowProcessType"] := detectedValues[this.configPrefix . "ProcessType"] + if (detectedValues.Has("ProcessType")) { + detectedValues["LoadingWindowProcessType"] := detectedValues["ProcessType"] } if (!this["LoadingWindowProcessId"]) { - detectedValues[this.configPrefix . "LoadingWindowProcessId"] := detectedValues[exeKey] + detectedValues["LoadingWindowProcessId"] := detectedValues[exeKey] } if (this.ShouldDetectShortcutSrc(detectedValues)) { @@ -110,7 +98,7 @@ class ManagedGameEntity extends ManagedEntityBase { } if (shortcutSrc != "") { - detectedValues[this.configPrefix . "ShortcutSrc"] := shortcutSrc + detectedValues["ShortcutSrc"] := shortcutSrc } } diff --git a/Lib/Launchpad/Entity/ManagedEntityBase.ahk b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk similarity index 81% rename from Lib/Launchpad/Entity/ManagedEntityBase.ahk rename to Lib/Launchpad/Entity/LaunchProcessEntity.ahk index b84be410..d28b726c 100644 --- a/Lib/Launchpad/Entity/ManagedEntityBase.ahk +++ b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk @@ -1,9 +1,12 @@ -class ManagedEntityBase extends FieldableEntity { - defaultType := "Default" +class LaunchProcessEntity extends FieldableEntity { defaultClass := "Default" - DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { - return container.Get("entity_manager.launcher")[id] + DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") { + ; TODO fix circular reference occurring + + return parentEntity + ? parentEntity + : container.Get("entity_manager.launcher")[id] } GetDefaultFieldGroups() { @@ -48,21 +51,10 @@ class ManagedEntityBase extends FieldableEntity { ) ) - definitions["EntityType"] := Map( - "default", this.defaultType, - "description", "The key of the managed type to load settings and defaults from.", - "required", true, - "storageKey", this.configPrefix . "Type", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListEntities", false, true), - "group", "general" - ) - - definitions["EntityClass"] := Map( + definitions["ProcessClass"] := Map( "default", this.defaultClass, - "description", "The name of the AHK class that will be used to control the managed entity.", + "description", "The name of the AHK class that will be used to control the process.", "formField", false, - "storageKey", this.configPrefix . "Class", "required", true, "group", "advanced", "modes", Map( @@ -73,11 +65,10 @@ class ManagedEntityBase extends FieldableEntity { definitions["SearchDirs"] := Map( "type", "directory", "mustExist", false, - "storageKey", this.configPrefix . "SearchDirs", "default", [A_ProgramFiles], "description", "Possible parent directories where the game's launcher might exist, to be used for auto-detection.", "help", "These should be as specific as possible to reduce detection time.", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "locations", "modes", Map( "simple", Map("formField", false) @@ -87,7 +78,6 @@ class ManagedEntityBase extends FieldableEntity { definitions["InstallDir"] := Map( "type", "directory", "mustExist", false, - "storageKey", this.configPrefix . "InstallDir", "group", "locations", "modes", Map( "simple", Map("group", "general") @@ -99,7 +89,6 @@ class ManagedEntityBase extends FieldableEntity { "type", "file", "fileMask", "*.exe", "mustExist", false, - "storageKey", this.configPrefix . "Exe", "description", "This can be the full path on the system to the launcher's .exe file, or simply the name of the .exe file itself.", "help", "If the .exe doesn't include the absolute path, auto-detection will be used by searching the DestinationDirs.", "group", "locations", @@ -113,7 +102,6 @@ class ManagedEntityBase extends FieldableEntity { ; - BlizzardProductDb (will search Battle.net's product.db file if it can be located for the installation directory, and the file will be found from there ; - Registry (will get a directory from the registry key specified by LocateRegKey and search for the file within it) definitions["LocateMethod"] := Map( - "storageKey", this.configPrefix . "LocateMethod", "default", "SearchDirs", "description", "How to search for the .exe if it isn't a full path already", "group", "general", @@ -126,12 +114,10 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["WindowTitle"] := Map( - "storageKey", this.configPrefix . "WindowTitle", "group", "process" ) definitions["LocateRegView"] := Map( - "storageKey", this.configPrefix . "LocateRegView", "default", 64, "group", "registry", "widget", "select", @@ -143,7 +129,7 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["LocateRegKey"] := Map( - "storageKey", this.configPrefix . "LocateRegKey", + "title", "Registry Locator - Key", "group", "registry", "description", "The registry key to look up the install dir within.", "help", "Path parts should be separated with backslashes and must start with one of: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, or the abbreviation of one of those. To read from a remote registry, prefix the root path with two backslashes and the computer name.`n`nSimple example: HKLM\Path\To\Key`nRemote example: \\OTHERPC\HKLM\Path\To\Key", @@ -153,7 +139,7 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["LocateRegValue"] := Map( - "storageKey", this.configPrefix . "LocateRegValue", + "title", "Registry Locator - Value", "group", "registry", "description", "The name of the registry value to look up within the specified key.", "help", "Example: InstallPath", @@ -163,7 +149,7 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["LocateRegRemovePrefix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemovePrefix", + "title", "Registry Locator - Remove Prefix", "group", "registry", "modes", Map( "simple", Map("formField", false) @@ -171,7 +157,7 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["LocateRegRemoveSuffix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemoveSuffix", + "title", "Registry Locator - Remove Suffix", "group", "registry", "modes", Map( "simple", Map("formField", false) @@ -179,7 +165,7 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["LocateRegStripQuotes"] := Map( - "storageKey", this.configPrefix . "LocateRegStripQuotes", + "title", "Registry Locator - Strip Quotes", "default", false, "group", "registry", "description", "Strip quotes from registry value", @@ -189,16 +175,16 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["PlatformRef"] := Map( - "storageKey", this.configPrefix . "PlatformRef", + "title", "Platform Reference", "description", "If the item is known to the launcher by a specific ID, it should be stored here.", "group", "general" ) definitions["WorkingDir"] := Map( + "title", "Working Directory", "type", "directory", "description", "The directory that the launcher should be run from.", "help", "If not set, it will be run without setting an explicit working directory, which is usually sufficient.", - "storageKey", this.configPrefix . "WorkingDir", "group", "locations", "modes", Map( "simple", Map("formField", false) @@ -210,7 +196,6 @@ class ManagedEntityBase extends FieldableEntity { definitions["RunType"] := Map( "description", "Which method to use for launching this item.", "help", "This is only needed for launchers that have to manage their own process.", - "storageKey", this.configPrefix . "RunType", "default", "Command", "group", "process", "widget", "select", @@ -220,15 +205,13 @@ class ManagedEntityBase extends FieldableEntity { definitions["UsesShortcut"] := Map( "type", "boolean", "description", "Whether a shortcut file will be used when starting the internally-managed game launcher", - "formField", false, - "storageKey", this.configPrefix . "UsesShortcut" + "formField", false ) definitions["ReplaceProcess"] := Map( "type", "boolean", "description", "Kill and re-launch the game process immediately after it is detected.", "help", "This can be used to force Launchpad to own the game process, but won't for for every game.", - "storageKey", this.configPrefix . "ReplaceProcess", "default", false, "group", "process" ) @@ -238,9 +221,9 @@ class ManagedEntityBase extends FieldableEntity { ; - The path of an .exe file on the system to which a shortcut will be created in AssetsDir if it doesn't already exist. Using this option ; is usually not necessary, since you can run the .exe directly instead. definitions["ShortcutSrc"] := Map( + "title", "Shortcut Source", "description", "The shortcut file used to launch the game launcher itself.", "help", "This is typically only needed if the Shortcut LauncherRunType is selected.", - "storageKey", this.configPrefix . "ShortcutSrc", "group", "locations", "modes", Map( "simple", Map("group", "general") @@ -252,7 +235,6 @@ class ManagedEntityBase extends FieldableEntity { ; - Scheduled (Creates an immediate scheduled task that runs the game, then waits until the window opens (if needed) and then closes) definitions["RunMethod"] := Map( "description", "Which method to use to run the RunCmd", - "storageKey", this.configPrefix . "RunMethod", "default", "Run", "group", "process", "widget", "select", @@ -265,7 +247,6 @@ class ManagedEntityBase extends FieldableEntity { definitions["ProcessType"] := Map( "description", "Which method to use to wait for the game to close.", "help", "This is not needed if the GameRunType is RunWait", - "storageKey", this.configPrefix . "ProcessType", "default", "Exe", "group", "process", "widget", "select", @@ -277,7 +258,6 @@ class ManagedEntityBase extends FieldableEntity { ; - Class - This value should be set to the game's window class definitions["ProcessId"] := Map( "help", "This value's type is dependent on the ProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", - "storageKey", this.configPrefix . "ProcessId", "group", "process", "modes", Map( "simple", Map("formField", false) @@ -286,7 +266,6 @@ class ManagedEntityBase extends FieldableEntity { definitions["ProcessTimeout"] := Map( "description", "The number of seconds to wait before giving up when waiting for a process.", - "storageKey", this.configPrefix . "ProcessTimeout", "default", 30, "group", "process", "modes", Map( @@ -295,41 +274,26 @@ class ManagedEntityBase extends FieldableEntity { ) definitions["RunCmd"] := Map( + "title", "Run Command", "description", "The command that will be used to run the game's launcher.", "help", "Typically only used if LauncherRunType is Command.", - "storageKey", this.configPrefix . "RunCmd", "group", "process" ) return definitions } - GetData() { - if (!this.ParentEntity) { - throw EntityException("A parent entity is required on type " . Type(this)) - } - - return this.ParentEntity.GetData() - } - - _createEntityData() { - return "" - } - - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() processId := "" - usesShortcut := false - if (this.GetData().HasValue(this.configPrefix . "UsesShortcut")) { - usesShortcut := this.GetData().GetValue(this.configPrefix . "UsesShortcut") - } else { - usesShortcut := (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") - } + usesShortcut := (this.GetData().HasValue("UsesShortcut")) + ? this.GetData().GetValue("UsesShortcut") + : (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") - detectedValues[this.configPrefix . "UsesShortcut"] := usesShortcut - detectedValues[this.configPrefix . "RunType"] := usesShortcut ? "Shortcut" : "Command" - detectedValues[this.configPrefix . "InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir + detectedValues["UsesShortcut"] := usesShortcut + detectedValues["RunType"] := usesShortcut ? "Shortcut" : "Command" + detectedValues["InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir if (this["ProcessType"] == "Exe") { SplitPath(this["Exe"], &processId) @@ -337,8 +301,8 @@ class ManagedEntityBase extends FieldableEntity { processId := this["WindowTitle"] ? this["WindowTitle"] : this.Id } - detectedValues[this.configPrefix . "ProcessId"] := processId - detectedValues[this.configPrefix . "WorkingDir"] := this["InstallDir"] + detectedValues["ProcessId"] := processId + detectedValues["WorkingDir"] := this["InstallDir"] return detectedValues } @@ -352,19 +316,25 @@ class ManagedEntityBase extends FieldableEntity { ListProcessTypes() { return [ - "Exe", "Title", "Class" + "Exe", + "Title", + "Class" ] } ListRunMethods() { return [ - "Run", "Scheduled", "RunWait" + "Run", + "Scheduled", + "RunWait" ] } ListLocateMethods() { return [ - "Search", "Registry", "BlizzardProductDb" + "Search", + "Registry", + "BlizzardProductDb" ; TODO Move this to the Blizzard module ] } @@ -393,7 +363,7 @@ class ManagedEntityBase extends FieldableEntity { validateResult["invalidFields"].push("RunCmd") } - ; TODO: Perform more launcher and game type validation here + ; TODO: Perform more validation here return validateResult } @@ -416,8 +386,7 @@ class ManagedEntityBase extends FieldableEntity { LocateInstallDir() { installDir := "" - ; TODO: Add additional methods to detect the install dir - + ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module if (this["LocateMethod"] == "BlizzardProductDb") { blizzardDir := this.GetBlizzardProductDir() @@ -426,6 +395,8 @@ class ManagedEntityBase extends FieldableEntity { } } + ; TODO: Add additional methods to detect the install dir + return installDir } @@ -477,6 +448,7 @@ class ManagedEntityBase extends FieldableEntity { } } } else if (this["LocateMethod"] == "BlizzardProductDb") { + ; TODO Move BlizzardProductDb method to an event handled by the Blizzard module blizzardDir := this.GetBlizzardProductDir() if (blizzardDir != "") { @@ -516,18 +488,14 @@ class ManagedEntityBase extends FieldableEntity { return path } - GetBlizzardProductKey() { - return "bna" ; Default to the Battle.net client itself - } - + ; TODO Move this method to the Blizzard module and call it from an eevent GetBlizzardProductDir() { - path := "" - productCode := this.GetBlizzardProductKey() + productCode (HasBase(this, GameProcessEntity.Prototype)) + ? productCode := this["PlatformRef"] + : "bna" ; Default to the Battle.net client itself - if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) { - path := this.app["BlizzardProductDb"].GetProductInstallPath(productCode) - } - - return path + return (productCode != "" && this.app.Services.Has("BlizzardProductDb")) + ? this.app["BlizzardProductDb"].GetProductInstallPath(productCode) + : "" } } diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index ab663653..947504ae 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -66,10 +66,10 @@ class LauncherEntity extends FieldableEntity { "showDefaultCheckbox", false ) - definitions["ManagedLauncher"] := Map( + definitions["LauncherProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_launcher", + "entityType", "launcher_process", "child", true, "weight", -25, "widget", "entity_select", @@ -85,13 +85,13 @@ class LauncherEntity extends FieldableEntity { ), "default", this.idVal, "showDefaultCheckbox", false, - "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT + "storeEntityData", true ) - definitions["ManagedGame"] := Map( + definitions["GameProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_game", + "entityType", "game_process", "child", true, "weight", -20, "widget", "entity_select", @@ -107,7 +107,7 @@ class LauncherEntity extends FieldableEntity { ), "default", this.idVal, "showDefaultCheckbox", false, - "valueType", EntityFieldBase.VALUE_TYPE_DEFAULT + "storeEntityData", true ) definitions["DestinationDir"] := Map( @@ -408,8 +408,8 @@ class LauncherEntity extends FieldableEntity { if (FileExist(checkPath)) { detectedValues["IconSrc"] := checkPath - } else if (this.Has("ManagedGame", false) && this["ManagedGame"].Has("Exe", false)) { - detectedValues["IconSrc"] := this["ManagedGame"].LocateExe() + } else if (this.Has("GameProcess", false) && this["GameProcess"].Has("Exe", false)) { + detectedValues["IconSrc"] := this["GameProcess"].LocateExe() } else { theme := this.container.Get("manager.theme").GetComponent() detectedValues["IconSrc"] := theme.GetIconPath("game") diff --git a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherProcessEntity.ahk similarity index 80% rename from Lib/Launchpad/Entity/ManagedLauncherEntity.ahk rename to Lib/Launchpad/Entity/LauncherProcessEntity.ahk index 12d5eb03..ee21fcc1 100644 --- a/Lib/Launchpad/Entity/ManagedLauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherProcessEntity.ahk @@ -1,15 +1,13 @@ -class ManagedLauncherEntity extends ManagedEntityBase { - configPrefix := "Launcher" - defaultType := "Default" +class LauncherProcessEntity extends LaunchProcessEntity { defaultClass := "SimpleLauncher" BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() - definitions["ManagedGame"] := Map( + definitions["GameProcess"] := Map( "type", "entity_reference", "required", true, - "entityType", "managed_game", + "entityType", "game_process", "child", false, "formField", false, "editable", false @@ -17,14 +15,14 @@ class ManagedLauncherEntity extends ManagedEntityBase { definitions["CloseBeforeRun"] := Map( "type", "boolean", - "storageKey", this.configPrefix . "CloseBeforeRun", + "storageKey", "CloseBeforeRun", "default", false, "description", "whether or not the launcher should be closed (if it is running) before starting the game" ) definitions["CloseAfterRun"] := Map( "type", "boolean", - "storageKey", this.configPrefix . "CloseAfterRun", + "storageKey", "CloseAfterRun", "default", false, "description", "Indicates whether the launcher should be closed after the game stops" ) @@ -33,7 +31,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "ClosePreDelay", + "storageKey", "ClosePreDelay", "default", 0, "required", true, "group", "advanced", @@ -47,7 +45,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "ClosePostDelay", + "storageKey", "ClosePostDelay", "default", 0, "required", true, "group", "advanced", @@ -64,7 +62,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { ; - "AutoPolite" - Automatically attempt to politely close the launcher, or fail if it can't be closed politely ; - "AutoKill" - Automatically attempts to end the launcher process, forcefully if needed definitions["CloseMethod"] := Map( - "storageKey", this.configPrefix . "CloseMethod", + "storageKey", "CloseMethod", "default", "Prompt", "description", "How to attempt to close the launcher if running", "widget", "select", @@ -76,7 +74,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "RecheckDelay", + "storageKey", "RecheckDelay", "default", 10, "required", true, "group", "advanced", @@ -90,7 +88,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "WaitTimeout", + "storageKey", "WaitTimeout", "default", 30, "required", true, "group", "advanced", @@ -104,11 +102,11 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "ms", "min", 0, - "storageKey", this.configPrefix . "KillPreDelay", + "storageKey", "KillPreDelay", "default", 10, "required", true, "group", "advanced", - "description", "If killing a managed launcher forcefully, ending the process will be delayed by this number of seconds.", + "description", "If killing a launch process forcefully, ending the process will be delayed by this number of seconds.", "modes", Map( "simple", Map("formField", false) ) @@ -118,11 +116,11 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "ms", "min", 0, - "storageKey", this.configPrefix . "KillPostDelay", + "storageKey", "KillPostDelay", "default", 5, "required", true, "group", "advanced", - "description", "If killing a managed launcher forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.", + "description", "If killing a launch process forcefully, the launcher will wait this number of seconds after trying to end the process before reporting success.", "modes", Map( "simple", Map("formField", false) ) @@ -132,7 +130,7 @@ class ManagedLauncherEntity extends ManagedEntityBase { "type", "time_offset", "timeUnits", "s", "min", 0, - "storageKey", this.configPrefix . "PoliteCloseWait", + "storageKey", "PoliteCloseWait", "required", true, "default", 10, "group", "advanced", diff --git a/Lib/Launchpad/Entity/ManagedProcessEntity.ahk b/Lib/Launchpad/Entity/ManagedProcessEntity.ahk deleted file mode 100644 index 02cb087b..00000000 --- a/Lib/Launchpad/Entity/ManagedProcessEntity.ahk +++ /dev/null @@ -1,533 +0,0 @@ -class ManagedProcessEntity extends FieldableEntity { - defaultType := "Default" - defaultClass := "Default" - - DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { - return container.Get("entity_manager.launcher")[id] - } - - GetDefaultFieldGroups() { - groups := super.GetDefaultFieldGroups() - - groups["locations"] := Map( - "name", "Locations", - "weight", 100 - ) - - groups["registry"] := Map( - "name", "Registry", - "weight", 125 - ) - - groups["process"] := Map( - "name", "Process", - "weight", 150 - ) - - return groups - } - - BaseFieldDefinitions() { - definitions := super.BaseFieldDefinitions() - - definitions["name"]["formField"] := false - - definitions["Launcher"] := Map( - "storageKey", "", - "type", "entity_reference", - "entityType", "launcher", - "required", true, - "formField", false, - "callbacks", Map( - "GetValue", ObjBindMethod(this, "GetId"), - "SetValue", ObjBindMethod(this, "SetId"), - "HasValue", ObjBindMethod(this, "HasId"), - "HasOverride", ObjBindMethod(this, "HasId"), - "IsEmpty", ObjBindMethod(this, "HasId", true), - "DeleteValue", "" - ) - ) - - definitions["EntityType"] := Map( - "default", this.defaultType, - "description", "The key of the managed type to load settings and defaults from.", - "required", true, - "storageKey", this.configPrefix . "Type", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListEntities", false, true), - "group", "general" - ) - - definitions["EntityClass"] := Map( - "default", this.defaultClass, - "description", "The name of the AHK class that will be used to control the managed entity.", - "formField", false, - "storageKey", this.configPrefix . "Class", - "required", true, - "group", "advanced", - "modes", Map( - "simple", Map("formField", false) - ), - ) - - definitions["SearchDirs"] := Map( - "type", "directory", - "mustExist", false, - "storageKey", this.configPrefix . "SearchDirs", - "default", [A_ProgramFiles], - "description", "Possible parent directories where the game's launcher might exist, to be used for auto-detection.", - "help", "These should be as specific as possible to reduce detection time.", - "multiple", true, - "group", "locations", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["InstallDir"] := Map( - "type", "directory", - "mustExist", false, - "storageKey", this.configPrefix . "InstallDir", - "group", "locations", - "modes", Map( - "simple", Map("group", "general") - ), - "description", "Select the installation folder, or use default for auto-detection." - ) - - definitions["Exe"] := Map( - "type", "file", - "fileMask", "*.exe", - "mustExist", false, - "storageKey", this.configPrefix . "Exe", - "description", "This can be the full path on the system to the launcher's .exe file, or simply the name of the .exe file itself.", - "help", "If the .exe doesn't include the absolute path, auto-detection will be used by searching the DestinationDirs.", - "group", "locations", - "modes", Map( - "simple", Map("group", "general") - ) - ) - - ; Options include: - ; - Search (will search through each directory in SearchDirs until a match is found) - ; - BlizzardProductDb (will search Battle.net's product.db file if it can be located for the installation directory, and the file will be found from there - ; - Registry (will get a directory from the registry key specified by LocateRegKey and search for the file within it) - definitions["LocateMethod"] := Map( - "storageKey", this.configPrefix . "LocateMethod", - "default", "SearchDirs", - "description", "How to search for the .exe if it isn't a full path already", - "group", "general", - "modes", Map( - "simple", Map("formField", false) - ), - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListLocateMethods"), - "help", "Search: Searches a list of possible directories (Defaulting to some common possibilities) for the .exe file and uses that directory`nRegistry: Looks for the provided registry key and uses its value as the install path if present`nBlizzardProductDb: Searches for PlatformRef within the Blizzard product.db file if present" - ) - - definitions["WindowTitle"] := Map( - "storageKey", this.configPrefix . "WindowTitle", - "group", "process" - ) - - definitions["LocateRegView"] := Map( - "storageKey", this.configPrefix . "LocateRegView", - "default", 64, - "group", "registry", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListRegViews"), - "description", "The registry view to use when locating the install dir.", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["LocateRegKey"] := Map( - "storageKey", this.configPrefix . "LocateRegKey", - "group", "registry", - "description", "The registry key to look up the install dir within.", - "help", "Path parts should be separated with backslashes and must start with one of: HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_USER, HKEY_CLASSES_ROOT, HKEY_CURRENT_CONFIG, or the abbreviation of one of those. To read from a remote registry, prefix the root path with two backslashes and the computer name.`n`nSimple example: HKLM\Path\To\Key`nRemote example: \\OTHERPC\HKLM\Path\To\Key", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["LocateRegValue"] := Map( - "storageKey", this.configPrefix . "LocateRegValue", - "group", "registry", - "description", "The name of the registry value to look up within the specified key.", - "help", "Example: InstallPath", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["LocateRegRemovePrefix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemovePrefix", - "group", "registry", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["LocateRegRemoveSuffix"] := Map( - "storageKey", this.configPrefix . "LocateRegRemoveSuffix", - "group", "registry", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["LocateRegStripQuotes"] := Map( - "storageKey", this.configPrefix . "LocateRegStripQuotes", - "default", false, - "group", "registry", - "description", "Strip quotes from registry value", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["PlatformRef"] := Map( - "storageKey", this.configPrefix . "PlatformRef", - "description", "If the item is known to the launcher by a specific ID, it should be stored here.", - "group", "general" - ) - - definitions["WorkingDir"] := Map( - "type", "directory", - "description", "The directory that the launcher should be run from.", - "help", "If not set, it will be run without setting an explicit working directory, which is usually sufficient.", - "storageKey", this.configPrefix . "WorkingDir", - "group", "locations", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - ; - Shortcut (Run a shortcut file) - ; - Command (Run a command directly, the default if required) - definitions["RunType"] := Map( - "description", "Which method to use for launching this item.", - "help", "This is only needed for launchers that have to manage their own process.", - "storageKey", this.configPrefix . "RunType", - "default", "Command", - "group", "process", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListRunTypes") - ) - - definitions["UsesShortcut"] := Map( - "type", "boolean", - "description", "Whether a shortcut file will be used when starting the internally-managed game launcher", - "formField", false, - "storageKey", this.configPrefix . "UsesShortcut" - ) - - definitions["ReplaceProcess"] := Map( - "type", "boolean", - "description", "Kill and re-launch the game process immediately after it is detected.", - "help", "This can be used to force Launchpad to own the game process, but won't for for every game.", - "storageKey", this.configPrefix . "ReplaceProcess", - "default", false, - "group", "process" - ) - - ; - The filename of an existing shortcut (.url or .lnk file, or even another .exe) that will be used to run the game. - ; - The path of another shortcut file (.url or .lnk) on the system, which will be copied to the AssetsDir if it doesn't already exist - ; - The path of an .exe file on the system to which a shortcut will be created in AssetsDir if it doesn't already exist. Using this option - ; is usually not necessary, since you can run the .exe directly instead. - definitions["ShortcutSrc"] := Map( - "description", "The shortcut file used to launch the game launcher itself.", - "help", "This is typically only needed if the Shortcut LauncherRunType is selected.", - "storageKey", this.configPrefix . "ShortcutSrc", - "group", "locations", - "modes", Map( - "simple", Map("group", "general") - ) - ) - - ; - RunWait (the default, uses RunWait to both run a process and wait until it completes in one step. This is most efficient if it works.) - ; - Run (Uses Run, then watches for the game window and waits until the window opens (if needed) and then closes) - ; - Scheduled (Creates an immediate scheduled task that runs the game, then waits until the window opens (if needed) and then closes) - definitions["RunMethod"] := Map( - "description", "Which method to use to run the RunCmd", - "storageKey", this.configPrefix . "RunMethod", - "default", "Run", - "group", "process", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListRunMethods") - ) - - ; - "Exe" (Waits for the game's .exe process to start if it hasn't already, and then waits for it to stop again. This is the default if the game type is not RunWait) - ; - "Title" (Waits for the game's window title to open if it isn't already, and then waits for it to close again) - ; - "Class" (Wait's for the game's window class to open if it isn't already, and then waits for it to close again) - definitions["ProcessType"] := Map( - "description", "Which method to use to wait for the game to close.", - "help", "This is not needed if the GameRunType is RunWait", - "storageKey", this.configPrefix . "ProcessType", - "default", "Exe", - "group", "process", - "widget", "select", - "selectOptionsCallback", ObjBindMethod(this, "ListProcessTypes") - ) - - ; - Exe - This value will default to the GameExe unless overridden - ; - Title - This value will default to the game's Key unless overridden - ; - Class - This value should be set to the game's window class - definitions["ProcessId"] := Map( - "help", "This value's type is dependent on the ProcessType above. It can often be detected from other values, and is not needed if the GameRunType is RunWait.", - "storageKey", this.configPrefix . "ProcessId", - "group", "process", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["ProcessTimeout"] := Map( - "description", "The number of seconds to wait before giving up when waiting for a process.", - "storageKey", this.configPrefix . "ProcessTimeout", - "default", 30, - "group", "process", - "modes", Map( - "simple", Map("formField", false) - ) - ) - - definitions["RunCmd"] := Map( - "description", "The command that will be used to run the game's launcher.", - "help", "Typically only used if LauncherRunType is Command.", - "storageKey", this.configPrefix . "RunCmd", - "group", "process" - ) - - return definitions - } - - GetData() { - if (!this.ParentEntity) { - throw EntityException("A parent entity is required on type " . Type(this)) - } - - return this.ParentEntity.GetData() - } - - _createEntityData() { - return "" - } - - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) - processId := "" - usesShortcut := false - - if (this.GetData().HasValue(this.configPrefix . "UsesShortcut")) { - usesShortcut := this.GetData().GetValue(this.configPrefix . "UsesShortcut") - } else { - usesShortcut := (this["RunType"] == "Shortcut" || this["ShortcutSrc"] != "" || this["RunCmd"] == "") - } - - detectedValues[this.configPrefix . "UsesShortcut"] := usesShortcut - detectedValues[this.configPrefix . "RunType"] := usesShortcut ? "Shortcut" : "Command" - detectedValues[this.configPrefix . "InstallDir"] := this.LocateInstallDir() ; This needs to run to expand exes without a dir - - if (this["ProcessType"] == "Exe") { - SplitPath(this["Exe"], &processId) - } else if (this["ProcessType"] == "Title") { - processId := this["WindowTitle"] ? this["WindowTitle"] : this.Id - } - - detectedValues[this.configPrefix . "ProcessId"] := processId - detectedValues[this.configPrefix . "WorkingDir"] := this["InstallDir"] - - return detectedValues - } - - ListRunTypes() { - return [ - "Command", - "Shortcut" - ] - } - - ListProcessTypes() { - return [ - "Exe", "Title", "Class" - ] - } - - ListRunMethods() { - return [ - "Run", "Scheduled", "RunWait" - ] - } - - ListLocateMethods() { - return [ - "Search", "Registry", "BlizzardProductDb" - ] - } - - ListRegViews() { - regViews := [ - "32" - ] - - if (A_Is64bitOS) { - regViews.Push("64") - } - - return regViews - } - - Validate() { - validateResult := super.Validate() - - if (((this["UsesShortcut"] && this["RunCmd"] == "") && this["ShortcutSrc"] == "") && !this.ShortcutFileExists()) { - validateResult["success"] := false - validateResult["invalidFields"].push("ShortcutSrc") - } - - if (this["ShortcutSrc"] == "" && this["RunCmd"] == "") { - validateResult["success"] := false - validateResult["invalidFields"].push("RunCmd") - } - - ; TODO: Perform more launcher and game type validation here - - return validateResult - } - - ShortcutFileExists() { - shortcutSrc := (this["ShortcutSrc"] != "") - ? this["ShortcutSrc"] - : this["AssetsDir"] . "\" . this.Id . ".lnk" - - exists := FileExist(shortcutSrc) - - if (!exists) { - shortcutSrc := this["AssetsDir"] . "\" . this.Id . ".url" - exists := FileExist(shortcutSrc) - } - - return exists - } - - LocateInstallDir() { - installDir := "" - - ; TODO: Add additional methods to detect the install dir - - if (this["LocateMethod"] == "BlizzardProductDb") { - blizzardDir := this.GetBlizzardProductDir() - - if (blizzardDir != "") { - installDir := blizzardDir - } - } - - return installDir - } - - LocateExe() { - return this.LocateFile(this["Exe"]) - } - - LocateFile(filePattern) { - filePath := "" - - if (filePattern != "") { - SplitPath(filePattern,,,,, &fileDrive) - - if (fileDrive != "") { - filePath := filePattern - } else { - searchDirs := [] - - if (this["InstallDir"] != "") { - searchDirs.Push(this["InstallDir"]) - } else if (this["LocateMethod"] == "SearchDirs") { - if (HasBase(this["SearchDirs"], Array.Prototype) && this["SearchDirs"].Length > 0) { - for index, dir in this["SearchDirs"] { - searchDirs.Push(dir) - } - } - } else if (this["LocateMethod"] == "Registry") { - regKey := this["LocateRegKey"] - - if (regKey != "") { - SetRegView(this["LocateRegView"]) - regDir := RegRead(this["LocateRegKey"], this["LocateRegValue"]) - SetRegView("Default") - - if (regDir != "") { - if (this["LocateRegStripQuotes"]) { - regDir := StrReplace(regDir, "`"", "") - } - - if (this["LocateRegRemovePrefix"] && SubStr(regDir, 1, StrLen(this["LocateRegRemovePrefix"])) == this["LocateRegRemovePrefix"]) { - regDir := SubStr(regDir, StrLen(this["LocateRegRemovePrefix"]) + 1) - } - - if (this["LocateRegRemoveSuffix"] && SubStr(regDir, 1, StrLen(this["LocateRegRemoveSuffix"])) == this["LocateRegRemoveSuffix"]) { - regDir := StrReplace(regDir, StrLen(this["LocateRegRemoveSuffix"]) + 1) - } - - searchDirs.Push(regDir) - } - } - } else if (this["LocateMethod"] == "BlizzardProductDb") { - blizzardDir := this.GetBlizzardProductDir() - - if (blizzardDir != "") { - searchDirs.Push(blizzardDir) - } - } - - filePath := this.LocateFileInSearchDirs(filePattern, searchDirs) - } - } - - return filePath - } - - LocateFileInSearchDirs(filePattern, searchDirs := "") { - path := "" - - if (searchDirs == "") { - searchDirs := this["SearchDirs"].Clone() - } - - if (!HasBase(searchDirs, Array.Prototype)) { - searchDirs := [searchDirs] - } - - for index, searchDir in searchDirs { - Loop Files, searchDir . "\" . filePattern, "R" { - path := A_LoopFileFullPath - break - } - - if (path != "") { - break - } - } - - return path - } - - GetBlizzardProductKey() { - return "bna" ; Default to the Battle.net client itself - } - - GetBlizzardProductDir() { - path := "" - productCode := this.GetBlizzardProductKey() - - if (productCode != "" && this.app.Services.Has("BlizzardProductDb")) { - path := this.app["BlizzardProductDb"].GetProductInstallPath(productCode) - } - - return path - } -} diff --git a/Lib/Launchpad/Includes.ahk b/Lib/Launchpad/Includes.ahk index cec1c809..cd69a6ec 100644 --- a/Lib/Launchpad/Includes.ahk +++ b/Lib/Launchpad/Includes.ahk @@ -22,11 +22,10 @@ #Include Config\PlatformsConfig.ahk #Include ConfigMigrator\LaunchpadIniMigrator.ahk #Include DetectedGame\DetectedGame.ahk +#Include Entity\GameProcessEntity.ahk #Include Entity\LauncherEntity.ahk -#Include Entity\ManagedEntityBase.ahk -#Include Entity\ManagedGameEntity.ahk -#Include Entity\ManagedLauncherEntity.ahk -#Include Entity\ManagedProcessEntity.ahk +#Include Entity\LauncherProcessEntity.ahk +#Include Entity\LaunchProcessEntity.ahk #Include Entity\PlatformEntity.ahk #Include GamePlatform\BasicPlatform.ahk #Include GamePlatform\GamePlatformBase.ahk diff --git a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk index 747feefa..7bc3a131 100644 --- a/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk +++ b/Lib/LaunchpadLauncher/App/LaunchpadLauncher.ahk @@ -39,7 +39,7 @@ class LaunchpadLauncher extends AppBase { ) services["Game"] := Map( - "class", config["gameConfig"]["GameClass"], + "class", config["gameConfig"]["ProcessClass"], "arguments", [ AppRef(), ParameterRef("launcher_key"), @@ -48,7 +48,7 @@ class LaunchpadLauncher extends AppBase { ) services["Launcher"] := Map( - "class", config["launcherConfig"]["LauncherClass"], + "class", config["launcherConfig"]["ProcessClass"], "arguments", [ ParameterRef("launcher_key"), ServiceRef("manager.gui"), From c92e332856a20bf4fda33455f0717e6f1027bce2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:23:06 -0500 Subject: [PATCH 094/138] Update field types to support cardinality options better cardinality of 1 means a single value will always be returned, while any other option will return an array --- .../EntityField/BooleanEntityField.ahk | 55 ++++++++-- .../EntityField/EntityFieldBase.ahk | 91 +++++++++++++--- .../EntityField/EntityReferenceField.ahk | 102 ++++++++++++++---- .../EntityField/ServiceReferenceField.ahk | 67 ++++++++---- 4 files changed, 254 insertions(+), 61 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk b/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk index b3dad413..91d37e01 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk +++ b/Lib/Shared/Volantis.Entity/EntityField/BooleanEntityField.ahk @@ -2,20 +2,61 @@ class BooleanEntityField extends EntityFieldBase { DefinitionDefaults(fieldDefinition) { defaults := super.DefinitionDefaults(fieldDefinition) defaults["widget"] := "checkbox" + defaults["default"] := false return defaults } - GetValue() { - isTrue := StrLower(super.GetValue()) + GetValue(index := "") { + value := super.GetValue(index) - if (isTrue == "true" || isTrue == "false") { - isTrue := (isTrue == "true") + if (!HasBase(value, Array.Prototype)) { + value := [value] } - return !!(isTrue) + newValues := [] + + for singleIndex, singleValue in value { + isTrue := StrLower(singleValue) + + if (isTrue == "true" || isTrue == "false") { + isTrue := (isTrue == "true") + } + + newValues.Push(!!isTrue) + } + + value := newValues + + if (!value.Length) { + value.Push("") + } + + if (index && !value.Has(index)) { + throw AppException("Index out of range") + } + + if (index) { + return value[index] + } else if (this.multiple) { + return value + } else { + return value[1] + } } - SetValue(value) { - super.SetValue(!!(value)) + SetValue(value, index := "") { + if (index || !this.multiple || !HasBase(value, Array.Prototype)) { + value := !!value + } else { + newValues := [] + + for singleIndex, singleValue in value { + newValues.Push(!!singleValue) + } + + value := newValues + } + + super.SetValue(value, index) } } diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk b/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk index a13646a4..c4856c10 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk +++ b/Lib/Shared/Volantis.Entity/EntityField/EntityFieldBase.ahk @@ -10,6 +10,7 @@ class EntityFieldBase { userLayer := "data" cloner := "" merger := "" + multiple := false needsEntityRefresh := false static VALUE_TYPE_DATA := "data" @@ -19,6 +20,10 @@ class EntityFieldBase { get => this.fieldDefinition set => this.fieldDefinition := value } + + IsMultiple { + get => this.multiple + } __New(fieldTypeId, entityObj, container, eventMgr, dataObj, fieldKey, fieldDefinition) { this.fieldTypeId := fieldTypeId @@ -31,6 +36,11 @@ class EntityFieldBase { this.merger := container.Get("merger.list") this.Definition := ParameterBag(this.DefinitionDefaults(fieldDefinition)) this.Definition.Add(fieldDefinition) + this.multiple := (this.Definition["cardinality"] == 0 || this.Definition["cardinality"] > 1) + + if (this.multiple && this.Definition["default"] && !HasBase(this.Definition["default"], Array.Prototype)) { + this.Definition["default"] := [this.Definition["default"]] + } } static Create(container, entityTypeId, entityObj, dataObj, fieldId, definition) { @@ -98,9 +108,8 @@ class EntityFieldBase { "formField", true, "group", "general", "help", "", - "limit", false, + "cardinality", 1, "modes", Map(), - "multiple", false, "processValue", false, "refreshEntityOnChange", false, "required", false, @@ -139,15 +148,49 @@ class EntityFieldBase { return result } - GetValue() { - return this.GetRawValue() + GetValue(index := "") { + return this.GetRawValue(index) } - GetRawValue() { - return this._callback("GetValue") + GetRawValue(index := "") { + value := this._callback("GetValue") + + if (this.multiple && !HasBase(value, Array.Prototype)) { + value := [value] + } + + if (this.multiple && index && !value.Has(index)) { + throw AppException("Index " . index . " does not exist in field " . this.fieldKey . ".") + } + + if (this.multiple && index) { + value := value[index] + } + + return value } - SetValue(value) { + SetValue(value, index := "") { + if (index && this.multiple) { + existingValues := this.GetRawValue() + + if (existingValues.Length < (index + 1)) { + throw AppException("Index to set is too high, there are only " . existingValues.Length . " values in field " . this.fieldKey . ".") + } + + if (existingValues.Length < index) { + existingValues.Push(value) + } else { + existingValues[index] := value + } + + value := existingValues + } + + if (this.multiple && !HasBase(value, Array.Prototype)) { + value := [value] + } + this._callback("SetValue", value) this.RefreshEntity() return this @@ -175,9 +218,19 @@ class EntityFieldBase { } Validate(value) { - return this - .CreateValidator(this.GetValidators(value)) - .Validate(value) + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + results := [] + + validator := this.CreateValidator(this.GetValidators(value)) + + for index, singleValue in value { + results.Push(validator.Validate(singleValue)) + } + + return this.multiple ? results : results[1] } /** @@ -187,11 +240,9 @@ class EntityFieldBase { _parseLayer(layer := "", allowAll := true) { if (!layer) { layer := this.Definition["dataLayer"] + } else if (layer == "*" && !allowAll) { + throw EntityException("Cannot pass wildcard for this layer value.") } else if (layer == "*") { - if (!allowAll) { - throw EntityException("Cannot pass wildcard for this layer value.") - } - layer := "" } @@ -234,6 +285,14 @@ class EntityFieldBase { allowEmpty ) + if (this.multiple && HasBase(val, Array.Prototype)) { + if (val.Length) { + val := (val[1] != "") + } else { + val := false + } + } + if (negate) { val := !val } @@ -244,6 +303,10 @@ class EntityFieldBase { _hasDefaultValue(allowEmpty := true, negate := false) { hasValue := allowEmpty ? true : !!(this.Definition["default"]) + if (hasValue && !allowEmpty && this.multiple && HasBase(this.Definition["default"], Array.Prototype)) { + hasValue := !!(this.Definition["default"][1]) + } + if (negate) { hasValue := !hasValue } diff --git a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk b/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk index 6591eedc..af424717 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk +++ b/Lib/Shared/Volantis.Entity/EntityField/EntityReferenceField.ahk @@ -1,4 +1,4 @@ -class EntityReferenceField extends ServiceReferenceField { +class EntityReferenceField extends EntityFieldBase { managerObj := "" DefinitionDefaults(fieldDefinition) { @@ -8,12 +8,14 @@ class EntityReferenceField extends ServiceReferenceField { throw EntityException("Entity reference fields require an entityType mapping.") } - managerObj := this._entityManager(entityTypeId) defaults := super.DefinitionDefaults(fieldDefinition) - defaults["servicePrefix"] := managerObj.GetServicePrefix() - defaults["entityType"] := managerObj.entityTypeId + defaults["entityType"] := entityTypeId + defaults["widget"] := "select" defaults["child"] := false + defaults["storeEntityData"] := false + defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions") + defaults["selectConditions"] := [] return defaults } @@ -32,34 +34,98 @@ class EntityReferenceField extends ServiceReferenceField { return validators } + GetValue(index := "") { + value := super.GetValue(index) + + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + entities := [] + entityManager := this._entityManager() + + for entityIndex, entityId in value { + if (!entityId) { + entities.Push("") + } else if (entityManager.Has(entityId)) { + entities.Push(entityManager[entityId]) + } else { + throw AppException("Entity with ID '" . entityId . "' does not exist.") + } + } + + if (!this.multiple || index) { + value := entities.Length ? entities[1] : "" + } else { + value := entities + } + + return value + } + + SetValue(value, index := "") { + if (!HasBase(value, Array.Prototype)) { + value := [value] + } + + newValues := [] + + for singleIndex, singleValue in value { + if (HasBase(singleValue, EntityBase.Prototype)) { + newValues.Push(singleValue.Id) + } else if (Type(singleValue) == "String") { + newValues.Push(singleValue) + } else { + throw AppException("Invalid entity reference data.") + } + } + + value := newValues + + if (!this.multiple || index) { + value := value.Length ? value[1] : "" + } + + super.SetValue(value, index) + return this + } + + GetEntitySelectOptions() { + options := this._getSelectQuery().Execute() + + if (!this.Definition["required"]) { + options.InsertAt(1, "") + } + + return options + } + _entityManager(entityTypeId := "") { if (!this.managerObj) { if (!entityTypeId) { entityTypeId := this.Definition["entityType"] } - this.managerObj := this.container.Get("entity_manager." . entityTypeId) + this.managerObj := this.container["entity_manager." . entityTypeId] } return this.managerObj } - _getService(entityId) { - if (!this.Definition["entityType"]) { - throw AppException("Entity type of reference field is not specified") - } + _getSelectQuery() { + query := this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS) + conditions := this.Definition["selectConditions"] - entityObj := "" + if (conditions) { + if (Type(conditions) != "Array") { + conditions := [conditions] + } - if (entityId) { - entityObj := this._entityManager()[entityId] - entityObj.LoadEntity() + for index, condition in conditions { + query.Condition(condition) + } } - return entityObj - } - - _getSelectQuery() { - return this._entityManager().EntityQuery(EntityQuery.RESULT_TYPE_IDS) + return query } } diff --git a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk b/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk index 10ed2747..b0c21423 100644 --- a/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk +++ b/Lib/Shared/Volantis.Entity/EntityField/ServiceReferenceField.ahk @@ -1,6 +1,13 @@ class ServiceReferenceField extends EntityFieldBase { - ReferencedObject { - get => this.GetValue() + DefinitionDefaults(fieldDefinition) { + defaults := super.DefinitionDefaults(fieldDefinition) + + defaults["servicePrefix"] := "" + defaults["widget"] := "select" + defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetServiceSelectOptions") + defaults["selectConditions"] := [] + + return defaults } GetValidators(value) { @@ -13,30 +20,34 @@ class ServiceReferenceField extends EntityFieldBase { return validators } - DefinitionDefaults(fieldDefinition) { - defaults := super.DefinitionDefaults(fieldDefinition) + GetValue(index := "") { + value := super.GetValue(index) - defaults["servicePrefix"] := "" - defaults["widget"] := "select" - defaults["selectOptionsCallback"] := ObjBindMethod(this, "GetEntitySelectOptions") - defaults["selectConditions"] := [] - - return defaults - } + if (!HasBase(value, Array.Prototype)) { + value := [value] + } - GetValue() { - serviceObj := "" - serviceId := super.GetValue() + newValues := [] - if (serviceId ) { - if (Type(serviceId) != "String") { - serviceObj := serviceId + for singleIndex, singleValue in value { + if (Type(singleValue) != "String") { + serviceObj := singleValue } else { - serviceObj := this._getService(serviceId) + serviceObj := this._getService(singleValue) + } + + if (serviceObj) { + newValues.Push(serviceObj) } } - return serviceObj + value := newValues + + if (!this.multiple || index) { + value := value.Length ? value[1] : "" + } + + return value } _getService(serviceId) { @@ -53,8 +64,20 @@ class ServiceReferenceField extends EntityFieldBase { return serviceObj } - SetValue(value) { - super.SetValue(this._getServiceId(value)) + SetValue(value, index := "") { + if (index || !this.multiple || !HasBase(value, Array.Prototype)) { + value := this._getServiceId(singleValue) + } else { + newValues := [] + + for singleIndex, singleValue in value { + newValues[singleIndex] = this._getServiceId(singleValue) + } + + value := newValues + } + + super.SetValue(value, index) } _getServiceId(value) { @@ -69,7 +92,7 @@ class ServiceReferenceField extends EntityFieldBase { return this.container.Query(this.Definition["servicePrefix"], ContainerQuery.RESULT_TYPE_NAMES, false, true) } - GetEntitySelectOptions() { + GetServiceSelectOptions() { query := this._getSelectQuery() conditions := this.Definition["selectConditions"] From fd110bee4d5a69f4c4f47d2376d75ed232576d7a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:23:46 -0500 Subject: [PATCH 095/138] Return this from set functions --- Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk index bd366064..ebf04287 100644 --- a/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk +++ b/Lib/Shared/Volantis.Entity/EntityStorage/EntityStorageBase.ahk @@ -24,6 +24,7 @@ class EntityStorageBase { id := this._dereferenceId(idOrObj) data := this._dereferenceData(idOrObj, data) this._saveEntityData(id, data) + return this } _saveEntityData(id, data) { @@ -48,6 +49,7 @@ class EntityStorageBase { DeleteData(idOrObj) { this._deleteEntityData(this._dereferenceId(idOrObj)) + return this } _dereferenceId(idOrObj) { From 25804aaeecba67978845a98dd68fe7d5bf494b98 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:24:59 -0500 Subject: [PATCH 096/138] Fire the new EntityParentEvent from EntityBase to determine the parent --- .../Volantis.Entity/Entity/EntityBase.ahk | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index da91df35..81a7dfd2 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -134,18 +134,29 @@ class EntityBase { .ListEntities(includeManaged, includeExtended) } - DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) { - return "" + DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") { + event := EntityParentEvent(EntityEvents.ENTITY_DISCOVER_PARENT, this.entityTypeId, this, parentEntity) + this.eventMgr.DispatchEvent(event) + + if (event.ParentEntity) { + this.parentEntityObj := event.ParentEntity + } else if (event.ParentEntityId) { + this.parentEntityId := event.ParentEntityId + this.parentEntityMgr := event.ParentEntityManager + ? event.ParentEntityManager + : container.Get("entity_manager." . event.ParentEntityTypeId) + + } + + this.parentEntityObj := event.ParentEntity + + return event.ParentEntity } GetParentEntity() { return this.parentEntityObj } - SetParentEntity(parentEntity) { - this.parentEntityObj := parentEntity - } - SetupEntity() { event := EntityEvent(EntityEvents.ENTITY_PREPARE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) From ad8e56d80045dd4202387d75de8c4292ebdfd6f5 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:27:15 -0500 Subject: [PATCH 097/138] Only create EntityDat automatically if dataLoaded = false, and set it to true afterward --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 81a7dfd2..3351b1ea 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -11,8 +11,8 @@ class EntityBase { idSanitizer := "" sanitizeId := true loaded := false - merger := "" dataLayer := "data" + dataLoaded := false cloner := "" Id { @@ -108,7 +108,11 @@ class EntityBase { } _createEntityData() { - this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources()) + if (!this.dataLoaded) { + this.dataObj := EntityData(this, this._getLayerNames(), this._getLayerSources()) + } + + this.dataLoaded := true } _getLayerNames() { From 7215fa3cc425fc345ab08fdb4eee69c4c9150cdf Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:29:22 -0500 Subject: [PATCH 098/138] Remove ConfigPrefix and update additionalManagedLauncherDefaults to additionalLauncherProcessDefaults --- Lib/Launchpad/Entity/LauncherEntity.ahk | 3 +-- Lib/Launchpad/Entity/PlatformEntity.ahk | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index 947504ae..e75c0d05 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -1,6 +1,5 @@ class LauncherEntity extends FieldableEntity { - configPrefix := "Launcher" - additionalManagedLauncherDefaults := Map() + additionalLauncherProcessDefaults := Map() IsBuilt { get => this.LauncherExists(false) diff --git a/Lib/Launchpad/Entity/PlatformEntity.ahk b/Lib/Launchpad/Entity/PlatformEntity.ahk index 5eacd08a..7e2aef0e 100644 --- a/Lib/Launchpad/Entity/PlatformEntity.ahk +++ b/Lib/Launchpad/Entity/PlatformEntity.ahk @@ -1,6 +1,5 @@ class PlatformEntity extends FieldableEntity { platformObj := "" - configPrefix := "" Platform { get => this.GetPlatform() From bf7e213be757715568fcc2443a65f9a7ff305843 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:30:22 -0500 Subject: [PATCH 099/138] Don't pass LauncherType or GameType in during detected game creation since it's handled by the platform now --- Lib/Launchpad/DetectedGame/DetectedGame.ahk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/Launchpad/DetectedGame/DetectedGame.ahk b/Lib/Launchpad/DetectedGame/DetectedGame.ahk index 0e6a966e..e92e87a7 100644 --- a/Lib/Launchpad/DetectedGame/DetectedGame.ahk +++ b/Lib/Launchpad/DetectedGame/DetectedGame.ahk @@ -89,9 +89,7 @@ class DetectedGame { CreateLauncher(launcherManager) { config := Map( - "Platform", this.platform.key, - "LauncherType", this.launcherType, - "GameType", this.gameType + "Platform", this.platform.key ) if (this.displayName && this.displayName != this.key) { From d4aa4817663b6ab04cb8dfa0e1045d2a471ee959 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:32:14 -0500 Subject: [PATCH 100/138] Remove recursive options from AutoDetectValues and InitializeDefaults for better data separation --- Lib/Launchpad/Entity/LauncherEntity.ahk | 4 ++-- Lib/Launchpad/Entity/PlatformEntity.ahk | 4 ++-- Lib/Shared/Volantis.App/Entity/BackupEntity.ahk | 4 ++-- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 16 ++-------------- .../Volantis.Entity/Entity/FieldableEntity.ahk | 4 ++-- 5 files changed, 10 insertions(+), 22 deletions(-) diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index e75c0d05..f94596de 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -399,8 +399,8 @@ class LauncherEntity extends FieldableEntity { return key } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() if (!detectedValues.Has("IconSrc")) { checkPath := this["AssetsDir"] . "\" . this.Id . ".ico" diff --git a/Lib/Launchpad/Entity/PlatformEntity.ahk b/Lib/Launchpad/Entity/PlatformEntity.ahk index 7e2aef0e..77060a1a 100644 --- a/Lib/Launchpad/Entity/PlatformEntity.ahk +++ b/Lib/Launchpad/Entity/PlatformEntity.ahk @@ -134,8 +134,8 @@ class PlatformEntity extends FieldableEntity { } } - AutoDetectValues(recurse := true) { - detectedValues := super.AutoDetectValues(recurse) + AutoDetectValues() { + detectedValues := super.AutoDetectValues() detectedValues["IsInstalled"] := this.Platform.IsInstalled() detectedValues["InstalledVersion"] := this.Platform.GetInstalledVersion() detectedValues["InstallDir"] := this.Platform.GetInstallDir() diff --git a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk index 9d2c32fa..9dfc0cf0 100644 --- a/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk +++ b/Lib/Shared/Volantis.App/Entity/BackupEntity.ahk @@ -93,12 +93,12 @@ class BackupEntity extends FieldableEntity { this.CreateBackupObject() } - AutoDetectValues(recurse := true) { + AutoDetectValues() { if (!this.backup) { this.CreateBackupObject() } - detectedValues := super.AutoDetectValues(recurse) + detectedValues := super.AutoDetectValues() return detectedValues } diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 3351b1ea..c7876fc5 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -175,17 +175,11 @@ class EntityBase { return this.container.Get("manager.entity_type")[this.EntityTypeId] } - InitializeDefaults(recurse := true) { + InitializeDefaults() { defaults := Map( "name", this.Id ) - if (recurse) { - for key, referencedEntity in this.GetReferencedEntities(true) { - this.merger.Merge(defaults, referencedEntity.InitializeDefaults()) - } - } - return defaults } @@ -302,15 +296,9 @@ class EntityBase { this.eventMgr.DispatchEvent(event) } - AutoDetectValues(recurse := true) { + AutoDetectValues() { values := Map() - if (recurse) { - for key, referencedEntity in this.GetReferencedEntities(true) { - this.merger.Merge(values, referencedEntity.AutoDetectValues(recurse)) - } - } - event := EntityDetectValuesEvent(EntityEvents.ENTITY_DETECT_VALUES, this.EntityTypeId, this, values) this.eventMgr.DispatchEvent(event) diff --git a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk index d39183b0..38709289 100644 --- a/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/FieldableEntity.ahk @@ -254,8 +254,8 @@ class FieldableEntity extends EntityBase { } } - InitializeDefaults(recurse := true) { - defaults := super.InitializeDefaults(recurse) + InitializeDefaults() { + defaults := super.InitializeDefaults() for key, fieldObj in this.GetFields() { defaults[fieldObj.Definition["storageKey"]] := fieldObj.Definition["default"] From 1eae1875ef4328d003db3a5ff48e255aeff523fa Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:33:53 -0500 Subject: [PATCH 101/138] Rename recursive option to recurse in DiffChanges method --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index c7876fc5..6f13e21b 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -376,14 +376,14 @@ class EntityBase { return !!(changes.GetAdded().Count || changes.GetModified().Count || changes.GetDeleted().Count) } - DiffChanges(recursive := true) { + DiffChanges(recurse := true) { diff := this.GetData().DiffChanges("original", this.dataLayer) - if (recursive) { + if (recurse) { diffs := [diff] for index, referencedEntity in this.GetReferencedEntities(true) { - diffs.Push(referencedEntity.DiffChanges(recursive)) + diffs.Push(referencedEntity.DiffChanges(recurse)) } diff := DiffResult.Combine(diffs) From a7f0d9d53d4f2d2d2fafa1aa6665b767e5ed5b55 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:34:20 -0500 Subject: [PATCH 102/138] Make layerNames and layerSources optional in LayeredDataBase --- Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index b1a18acd..8d88908b 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -30,7 +30,7 @@ class LayeredDataBase { static NO_VALUE := ":NO_VAL:" - __New(cloner, processors, layerNames, layerSources) { + __New(cloner, processors, layerNames := "", layerSources := "") { this.cloner := cloner if (processors) { From 0399fb1d10d6ec24406d35c61e747e198491cb27 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:35:00 -0500 Subject: [PATCH 103/138] Define parent entity storage on game_process and launcher_process entity types --- Launchpad.services.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index 59a88e39..feedbddb 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -48,7 +48,8 @@ "entity_class": "GameProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", - "parent_entity_type": "launcher" + "parent_entity_type": "launcher", + "parent_entity_storage": true }, "entity_type.launcher_process": { "name_singular": "Managed Launcher", From f2f7b05f30af641d21bec451dedd305d2d0da758 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:35:13 -0500 Subject: [PATCH 104/138] Missing change from last commit --- Launchpad.services.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Launchpad.services.json b/Launchpad.services.json index feedbddb..7df3dbb0 100644 --- a/Launchpad.services.json +++ b/Launchpad.services.json @@ -57,7 +57,8 @@ "entity_class": "LauncherProcessEntity", "storage_config_storage_parent_key": "Games", "storage_config_path_parameter": "config.launcher_file", - "parent_entity_type": "launcher" + "parent_entity_type": "launcher", + "parent_entity_storage": true }, "entity_type.platform": { "name_singular": "Platform", From 90b452b64956c0d2926f12c3ba9fe9348b98ed05 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:35:42 -0500 Subject: [PATCH 105/138] Temporarily add single cardinality options to some Launcher fields until widgets support multiple values --- Lib/Launchpad/Entity/LauncherEntity.ahk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/Launchpad/Entity/LauncherEntity.ahk b/Lib/Launchpad/Entity/LauncherEntity.ahk index f94596de..b5d4c554 100644 --- a/Lib/Launchpad/Entity/LauncherEntity.ahk +++ b/Lib/Launchpad/Entity/LauncherEntity.ahk @@ -186,7 +186,7 @@ class LauncherEntity extends FieldableEntity { definitions["RunBefore"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -198,7 +198,7 @@ class LauncherEntity extends FieldableEntity { definitions["CloseBefore"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -210,7 +210,7 @@ class LauncherEntity extends FieldableEntity { definitions["RunAfter"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) @@ -222,7 +222,7 @@ class LauncherEntity extends FieldableEntity { definitions["CloseAfter"] := Map( "type", "entity_reference", "entityType", "task", - "multiple", true, + "cardinality", 1, ; Change to another number once widgets for multiple values are worked out "group", "tasks", "modes", Map( "simple", Map("formField", false) From 4ed87e1de52da94f25f824ed1299863c3b033ea4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:36:11 -0500 Subject: [PATCH 106/138] Always set cache object in CachedWebServiceResponseBase --- .../WebServiceResponse/CachedWebServiceResponse.ahk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk index a237cfb8..551b7efa 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceResponse/CachedWebServiceResponse.ahk @@ -2,9 +2,7 @@ class CachedWebServiceResponse extends WebServiceResponseBase { cacheObj := "" __New(webServiceEnt, webServiceReq) { - if (webServiceEnt.Has("Cache", false)) { - this.cacheObj := webServiceEnt.cacheObj - } + this.cacheObj := webServiceEnt.cacheObj super.__New(webServiceEnt, webServiceReq) } From 3444470665f1b961bc54a3a6634766e58bf75bd2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:36:24 -0500 Subject: [PATCH 107/138] Simplify event dispatch call --- Lib/Shared/Volantis.App/App/AppBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.App/App/AppBase.ahk b/Lib/Shared/Volantis.App/App/AppBase.ahk index 07f03fa2..307ceb47 100644 --- a/Lib/Shared/Volantis.App/App/AppBase.ahk +++ b/Lib/Shared/Volantis.App/App/AppBase.ahk @@ -956,7 +956,7 @@ class AppBase { updateAvailable := false event := ReleaseInfoEvent(Events.APP_GET_RELEASE_INFO, this) - this["manager.event"].DispatchEvent(event) + this.Dispatch(event) releaseInfo := event.ReleaseInfo if ( From ee45219dd1aca103a2015574b946f83cd8b662a7 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:37:00 -0500 Subject: [PATCH 108/138] Change how CloneLayers works in LayeredDataBase so it only clones user layers by default --- .../LayeredData/LayeredDataBase.ahk | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index 8d88908b..6575e28e 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -536,8 +536,22 @@ class LayeredDataBase { return data } + GetUserLayers() { + layerNames := this.userLayers + + layers := Map() + + for index, layerName in layerNames { + layers[layerName] := this.GetLayer(layerName) + } + + return layers + } + CloneLayers(layers := "") { if (layers == "") { + layers := this.GetUserLayers() + } else if (layers == "*") { this.LoadAllLayers() layers := this.layers } else if (Type(layers) == "String") { @@ -550,8 +564,10 @@ class LayeredDataBase { cloned := Map() - for key, layer in layers { - cloned[key] := this.CloneData(layer) + if (layers) { + for key, layer in layers { + cloned[key] := this.CloneData(layer) + } } return cloned From 079fc79968496253d441e834c05a9f6bf9cba74f Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:37:47 -0500 Subject: [PATCH 109/138] Update GamePlatformBase to make a web service call to look up a game's main Exe --- .../GamePlatform/GamePlatformBase.ahk | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk index a8dfc88b..7c25316b 100644 --- a/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk +++ b/Lib/Launchpad/GamePlatform/GamePlatformBase.ahk @@ -184,39 +184,34 @@ class GamePlatformBase { if (possibleExes.Length == 1) { mainExe := possibleExes[1] } else if (possibleExes.Length > 1) { - ; @todo move the API functionality into a module that depends on WebServices - if (this.app.Services.Has("entity_manager.web_service")) { - mgr := this.app["entity_manager.web_service"] - - if (mgr.Has("launchpad_api")) { - webService := mgr["launchpad_api"] - - resultData := webService.AdapterRequest( - Map("id", key), - Map( - "adapterType", "entity_data", - "entityType", "launcher" - ), - "read", - true - ) - - for key, data in resultData { - if ( - data - && HasBase(data, Map.Prototype) - && data.Has("defaults") - && data["defaults"] - && data["defaults"].Has("GameExe") - && data["defaults"]["GameExe"] - ) { - for index, possibleExe in possibleExes { - SplitPath(possibleExe, &fileName) - - if (data["defaults"]["GameExe"] == fileName) { - mainExe := possibleExe - break 2 - } + ; @todo move the API functionality into an event in an event in the WebServicesEventSubscriber + if (this.app.Services.Has("web_services.adapter_manager")) { + resultData := this.app["web_services.adapter_manager"].AdapterRequest( + Map("id", key), + Map( + "dataType", "entity_data", + "entityType", "launcher", + "tags", "defaults" + ), + "read", + true + ) + + for key, data in resultData { + if ( + data + && HasBase(data, Map.Prototype) + && data.Has("defaults") + && data["defaults"] + && data["defaults"].Has("GameExe") + && data["defaults"]["GameExe"] + ) { + for index, possibleExe in possibleExes { + SplitPath(possibleExe, &fileName) + + if (data["defaults"]["GameExe"] == fileName) { + mainExe := possibleExe + break 2 } } } From f0b027489c51149745dad1bcf6f293380bc0c7a2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:41:56 -0500 Subject: [PATCH 110/138] Update the error dialog and feedback window to send data via WebServices if it's installed --- .../WebServices/Gui/Form/FeedbackWindow.ahk | 62 ++++++++++--------- .../Volantis.App/Gui/Dialog/ErrorDialog.ahk | 26 +++++--- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk index fda1e07a..b790b0dc 100644 --- a/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk +++ b/Lib/Shared/Modules/WebServices/Gui/Form/FeedbackWindow.ahk @@ -36,44 +36,50 @@ class FeedbackWindow extends DialogBox { SendFeedback() { global appVersion - webServiceId := "launchpad_api" - entityMgr := this.container["entity_manager.web_service"] - - results := Map() - success := false - - if (entityMgr.Has(webServiceId) && entityMgr[webServiceId]["Enabled"]) { - webService := entityMgr[webServiceId] + filters := "feedback_submission" + operation := "create" + if ( + this.container.Has("web_services.adapter_manager") + && this.container["web_services.adapter_manager"].HasAdapters(filters, operation) + ) { body := Map() body["email"] := this.guiObj["Email"].Text body["version"] := appVersion body["feedback"] := this.guiObj["Feedback"].Text - results := webService.AdapterRequest(Map("data", body), "feedback_submission", "create", true) - } + results := this.container["web_services.adapter_manager"].AdapterRequest( + Map("data", body), + filters, + operation, + true + ) + + success := false + + for adapterId, adapterResult in results { + if (adapterResult) { + success := true - for key, result in results { - if (result) { - success := true - break + break + } } - } - message := "" + message := "" - if (success) { - message := "Successfully sent feedback" - } else if (results.Count) { - message := "Failed to send feedback" - } else { - message := "No feedback adapters are enabled" - } + if (success) { + message := "Successfully sent feedback" + } else if (results.Count) { + message := "Failed to send feedback" + } else { + message := "No feedback adapters are enabled" + } - this.notifierObj.Notify( - message, - "Feedback Submission", - success ? "info" : "error" - ) + this.notifierObj.Notify( + message, + "Feedback Submission", + success ? "info" : "error" + ) + } } } diff --git a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk index 672baa1a..a97a6e87 100644 --- a/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk +++ b/Lib/Shared/Volantis.App/Gui/Dialog/ErrorDialog.ahk @@ -109,14 +109,13 @@ class ErrorDialog extends DialogBox { global appVersion ; @todo Move the API connection stuff into the LaunchpadApi module + filters := "error_submission" + operation := "create" if ( - this.container.Has("entity_manager.web_service") - && this.container["entity_manager.web_service"].Has("launchpad_api") - && this.container["entity_manager.web_service"]["launchpad_api"]["Enabled"] + this.container.Has("web_services.adapter_manager") + && this.container["web_services.adapter_manager"].HasAdapters(filters, operation) ) { - webService := this.container["entity_manager.web_service"]["launchpad_api"] - body := Map() body["message"] := this.errorObj.Message body["what"] := this.errorObj.What @@ -128,12 +127,23 @@ class ErrorDialog extends DialogBox { body["version"] := appVersion ? appVersion : "" body["details"] := this.guiObj["ErrorDetails"].Text - success := webService.AdapterRequest( + results := this.container["web_services.adapter_manager"].AdapterRequest( Map("data", body), - Map("adapterType", "error_submission"), - "create" + filters, + operation, + true ) + success := false + + for adapterId, adapterResult in results { + if (adapterResult) { + success := true + + break + } + } + notification := success ? "Successfully sent error details for further investigation" : "Failed to send error details" this.notifierObj.Notify(notification, "Error Submission", success ? "info" : "error") } From 35aefe2492491033f6378889185c1a8290f5d556 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:49:11 -0500 Subject: [PATCH 111/138] Remove LauncherType and GameType handling from DetectedGameEditor --- Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 6b8de59e..2e53e2a3 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -1,10 +1,8 @@ -class DetectedGameEditor extends FormGuiBase { +class DetectedGameEditor extends FormGuiBase { detectedGameObj := "" newValues := Map() missingFields := Map() knownGames := "" - launcherTypes := "" - gameTypes := "" __New(container, themeObj, config, detectedGameObj) { this.detectedGameObj := detectedGameObj @@ -77,9 +75,7 @@ Controls() { super.Controls() - this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.Id, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.") - this.Add("SelectControl", "vLauncherType", "Launcher Type", this.detectedGameObj.launcherType, this.launcherTypes, "OnLauncherTypeChange", "This tells " . this.app.appName . " how to interact with any launcher your game might require. If your game's launcher isn't listed, or your game doesn't have a launcher, start with `"Default`".`n`nYou can customize the details of the launcher type after it is added.") - this.Add("SelectControl", "vGameType", "Game Type", this.detectedGameObj.gameType, this.gameTypes, "OnGameTypeChange", "This tells " . this.app.appName . " how to launch your game. Most games can use 'default', but launchers can support different game types.`n`nYou can customize the details of the game type after it is added.") + this.Add("ComboBoxControl", "vId", "Id", this.detectedGameObj.key, this.knownGames, "OnIdChange", "You can change the detected game key here, which will become the name of your launcher. Your existing launchers, and all launchers known about via the API, can be selected to match this game up with one of those items.") this.Add("LocationBlock", "", "Install Dir", this.detectedGameObj.installDir, "InstallDir", "", true, "This is the directory that the game is installed in, if it could be detected.") this.Add("ComboBoxControl", "vExe", "Exe", this.detectedGameObj.exeName, this.detectedGameObj.possibleExeNames, "OnExeChange", "The main Exe, if detected, should be pre-selected. You may change it to be the name (or path) of another exe, or select another one of the detected .exe files from the list (if more than one was found).") this.AddTextBlock("Launcher-Specific ID", "PlatformRef", this.detectedGameObj.platformRef, "This is typically the ID which the game platform or launcher uses when referring to the game internally. Changing this value could cause issues with game launching.") @@ -103,16 +99,6 @@ this.newValues["key"] := ctl.Text } - OnLauncherTypeChange(ctl, info) { - this.guiObj.Submit(false) - this.newValues["launcherType"] := ctl.Text - } - - OnGameTypeChange(ctl, info) { - this.guiObj.Submit(false) - this.newValues["gameType"] := ctl.Text - } - GetValue(key) { val := this.detectedGameObj.%key% From 75c3d7d5be7e53e333774ac51e11fe95d63e90bc Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:56:11 -0500 Subject: [PATCH 112/138] Subscribe to events to automatically make API requests on behalf of entities based on registered adapters --- .../WebServicesEventSubscriber.ahk | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk index 0924015c..97023350 100644 --- a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk +++ b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk @@ -1,4 +1,12 @@ class WebServicesEventSubscriber extends EventSubscriberBase { + adapterMgr := "" + + __New(container, adapterMgr) { + this.adapterMgr := adapterMgr + + super.__New(container) + } + GetEventSubscribers() { return Map( Events.APP_POST_STARTUP, [ @@ -9,6 +17,27 @@ class WebServicesEventSubscriber extends EventSubscriberBase { ], Events.APP_MENU_PROCESS_RESULT, [ ObjBindMethod(this, "OnMenuProcessResult") + ], + EntityEvents.ENTITY_DATA_LAYERS, [ + ObjBindMethod(this, "EntityDataLayers") + ], + EntityEvents.ENTITY_LAYER_SOURCES, [ + ObjBindMethod(this, "EntityLayerSources") + ], + Events.APP_GET_RELEASE_INFO, [ + ObjBindMethod(this, "GetReleaseInfo") + ], + EntityEvents.ENTITY_FIELD_GROUPS, [ + ObjBindMethod(this, "EntityFieldGroups") + ], + EntityEvents.ENTITY_FIELD_DEFINITIONS, [ + ObjBindMethod(this, "EntityFieldDefinitions") + ], + EntityEvents.ENTITY_DETECT_VALUES, [ + ObjBindMethod(this, "EntityDetectValues") + ], + EntityEvents.ENTITY_LIST_ENTITIES, [ + ObjBindMethod(this, "ListEntities") ] ) } @@ -40,4 +69,254 @@ class WebServicesEventSubscriber extends EventSubscriberBase { } } } + + _getEntityLayerKey(webService, adapterId) { + return webService["id"] . "." . adapterId + } + + EntityDataLayers(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for webServiceId, webService in webServices { + adapterIds := this.adapterMgr.GetAdapterIds(Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId, + "tags", "defaults" + ), "", 0, webService) + + for , adapterId in adapterIds { + adapter := this.adapterMgr.GetAdapter(adapterId) + layerExists := false + layerKey := this._getEntityLayerKey(webService, adapterId) + + for , existingLayerKey in event.Layers { + if (existingLayerKey == layerKey) { + layerExists := true + + break + } + } + + if (!layerExists) { + event.Layers.Push(layerKey) + } + } + } + } + + EntityLayerSources(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for webServiceId, webService in webServices { + adapters := this.adapterMgr.GetAdapters(Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ), "", 0, webService) + + paramsEvent := WebServicesEntityDataParamsEvent( + WebServicesEvents.ENTITY_DATA_PARAMS, + event.EntityTypeId, + event.Entity, + webService, + Map("id", event.Entity.Id) + ) + this.container["manager.event"].DispatchEvent(paramsEvent) + + for key, adapter in adapters { + layerKey := this._getEntityLayerKey(webService, key) + + if (!event.LayerSources.Has(layerKey)) { + event.LayerSources[layerKey] := WebServiceAdapterLayerSource(adapter, paramsEvent.Params) + } + } + } + } + + GetReleaseInfo(event, extra, eventName, hwnd) { + if (!event.ReleaseInfo.Count && this.container.GetApp().Version != "{{VERSION}}") { + releaseInfo := this.adapterMgr.AdapterRequest("", "release_info") + + if (releaseInfo && releaseInfo.Count) { + event.ReleaseInfo := releaseInfo + } + } + } + + EntityFieldGroups(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + if (!event.FieldGroups.Has("web_services")) { + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + addGroup := false + + for key, webService in webServices { + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + operation := "read" + + if (this.adapterMgr.HasAdapters(filters, operation, webService)) { + addGroup := true + + break + } + } + + if (addGroup) { + event.FieldGroups["web_services"] := Map( + "name", "Web Services", + "weight", 100 + ) + } + } + } + + EntityFieldDefinitions(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for key, webService in webServices { + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + operation := "read" + + if (this.adapterMgr.HasAdapters(filters, operation, webService)) { + event.FieldDefinitions["web_service_" . webService["id"] . "_ref"] := Map( + "title", webService["name"] . " Reference", + "description", "The key that is used to look up the entity's data from the " . webService["name"] . " web service.", + "help", "It defaults to the entity ID, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same entity can exist by giving them different IDs but using the same " . webService["name"] . " reference.", + "group", "web_services", + "processValue", false, + "modes", Map("simple", Map("formField", false)) + ) + + break + } + } + } + + EntityDetectValues(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { + return + } + + webServices := this.container["entity_manager.web_service"] + .EntityQuery(EntityQuery.RESULT_TYPE_ENTITIES) + .Condition(IsTrueCondition(), "Enabled") + .Execute() + + for key, webService in webServices { + fieldId := "web_service_" . webService["id"] . "_ref" + filters := Map( + "dataType", "entity_data", + "entityType", event.EntityTypeId + ) + + if ( + this.adapterMgr.HasAdapters(filters, "read", webService) + && (!event.Values.Has(fieldId) || !event.Values[fieldId]) + && event.Entity.HasField(fieldId) + && (!event.Entity.RawData.Has(fieldId) || !event.Entity.RawData[fieldId]) + ) { + paramsEvent := WebServicesEntityDataParamsEvent( + WebServicesEvents.ENTITY_DATA_PARAMS, + event.EntityTypeId, + event.Entity, + webService, + Map("id", event.Entity.Id) + ) + this.container["manager.event"].DispatchEvent(paramsEvent) + + result := this.adapterMgr.AdapterRequest( + paramsEvent.Params, + Map( + "dataType", "entity_lookup", + "entityType", event.EntityTypeId + ), + "read", + false, + webService + ) + + if (!result) { + result := event.Entity["id"] + } + + event.Values[fieldId] := result + } + } + } + + ListEntities(event, extra, eventName, hwnd) { + if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + return + } + + if (event.includeExtended) { + entityMgr := this.container["entity_manager." . event.EntityTypeId] + results := this.adapterMgr.AdapterRequest("", Map( + "dataType", "entity_list", + "entityType", event.EntityTypeId + ), "read", true) + + if (results && HasBase(results, Array.Prototype)) { + managedIds := event.includeManaged + ? [] + : entityMgr.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() + + for index, id in results { + exists := false + + for , existingId in event.EntityList { + if (existingId == id) { + exists := true + break + } + } + + if (!exists && !event.includeManaged) { + for , managedId in managedIds { + if (managedId == id) { + exists := true + break + } + } + } + + if (!exists) { + event.EntityList.Push(id) + } + } + } + } + } } From 23c386583256bc8d3af54bc087e6f275d7b41a6a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:56:42 -0500 Subject: [PATCH 113/138] Add a recurse option, defaulting to false, when calling DeleteEntity --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 6f13e21b..af504744 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -349,11 +349,17 @@ class EntityBase { } } - DeleteEntity() { + DeleteEntity(recurse := false) { if (this.storageObj.HasData(this.GetStorageId())) { event := EntityEvent(EntityEvents.ENTITY_PREDELETE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) + if (recurse) { + for index, entityObj in this.ChildEntities { + entityObj.DeleteEntity(recurse) + } + } + this.storageObj.DeleteData(this.GetStorageId()) event := EntityEvent(EntityEvents.ENTITY_DELETED, this.entityTypeId, this) From d63f348a900f826e685ae73d287d2f85ce7b8344 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:57:36 -0500 Subject: [PATCH 114/138] Move SaveData and CreateSnapshot to below the recursive saving loop in SaveEntity --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index af504744..f4a242c4 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -317,15 +317,15 @@ class EntityBase { event := EntityEvent(EntityEvents.ENTITY_PRESAVE, this.entityTypeId, this) this.eventMgr.DispatchEvent(event) - - this.GetData().SaveData() - this.CreateSnapshot("original") if (recurse) { for index, entityObj in this.GetReferencedEntities(true) { entityObj.SaveEntity(recurse) } } + + this.GetData().SaveData() + this.CreateSnapshot("original") if (alreadyExists) { event := EntityEvent(EntityEvents.ENTITY_UPDATED, this.entityTypeId, this) From 23999223360f3f18e98331f89b51e8a825777c50 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 03:59:46 -0500 Subject: [PATCH 115/138] Remove duplicate event subscriber definitions from LaunchpadApiSubscriber, and update field in MainWindow to match WebService's field name --- Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk | 4 +- .../LaunchpadApiSubscriber.ahk | 249 +----------------- 2 files changed, 14 insertions(+), 239 deletions(-) diff --git a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk index d8833ccd..3f897337 100644 --- a/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk +++ b/Lib/Launchpad/Gui/ManageWindow/MainWindow.ahk @@ -133,7 +133,7 @@ status := launcher.GetStatus() ; @todo Move the API data to an event in the LaunchpadApi module - apiStatus := (launcher.HasField["LaunchpadApiRef"] && launcher["LaunchpadApiRef"]) ? "Linked" : "Not linked" + apiStatus := (launcher.HasField["web_service_launchpad_api_ref"] && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo("Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo("Build")["Timestamp"]) @@ -311,7 +311,7 @@ status := launcher.GetStatus() ; @todo Move the API code to the LaunchpadApi module - apiStatus := (launcher.HasField("LaunchpadApiRef") && launcher["LaunchpadApiRef"]) ? "Linked" : "Not linked" + apiStatus := (launcher.HasField("web_service_launchpad_api_ref") && launcher["web_service_launchpad_api_ref"]) ? "Linked" : "Not linked" created := this.FormatDate(this.app.State.GetLauncherCreated(key)) updated := this.FormatDate(this.app.State.GetLauncherInfo(key, "Config")["Timestamp"]) built := this.FormatDate(this.app.State.GetLauncherInfo(key, "Build")["Timestamp"]) diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk index 699cbb5d..8b864ab7 100644 --- a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -1,250 +1,25 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { GetEventSubscribers() { return Map( - Events.APP_GET_RELEASE_INFO, [ - ObjBindMethod(this, "GetReleaseInfo") - ], - EntityEvents.ENTITY_DATA_LAYERS, [ - ObjBindMethod(this, "EntityDataLayers") - ], - EntityEvents.ENTITY_LAYER_SOURCES, [ - ObjBindMethod(this, "EntityLayerSources") - ], - EntityEvents.ENTITY_FIELD_GROUPS, [ - ObjBindMethod(this, "EntityFieldGroups") - ], - EntityEvents.ENTITY_FIELD_DEFINITIONS, [ - ObjBindMethod(this, "EntityFieldDefinitions") - ], - EntityEvents.ENTITY_DETECT_VALUES, [ - ObjBindMethod(this, "EntityDetectValues") - ], - EntityEvents.ENTITY_LIST_ENTITIES, [ - ObjBindMethod(this, "ListEntities") - ], + WebServicesEvents.ENTITY_DATA_PARAMS, [ + ObjBindMethod(this, "EntityDataParams") + ] ) } - GetReleaseInfo(event, extra, eventName, hwnd) { - releaseInfo := event.ReleaseInfo - - if (!event.ReleaseInfo.Count && this.container.GetApp().Version != "{{VERSION}}") { - webService := this.container["entity_manager.web_service"]["launchpad_api"] - - if (webService["Enabled"]) { - releaseInfo := webService.AdapterRequest("", "release_info") - - if (releaseInfo) { - for key, val in releaseInfo { - event.ReleaseInfo[key] = val - } - } - } - } - } - - EntityDataLayers(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { - return - } - - webService := this.container["entity_manager.web_service"]["launchpad_api"] - - layers := event.Layers - - if (WebService["Enabled"]) { - entity := event.Entity - - adapters := webService.GetAdapters([ - "adapterType", "entity_data", - "entityType", event.EntityTypeId - ]) - - for key, adapter in adapters { - layerExists := false - layerKey := webService["id"] . "." . event.EntityTypeId . "." . key - - for index, layerName in layers { - if (layerName == layerKey) { - layerExists := true - break - } - } - - if (!layerExists) { - layers.Push(layerKey) - } - } - } - } - - EntityLayerSources(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { - return - } - - webService := this.container["entity_manager.web_service"]["launchpad_api"] - - layerData := event.LayerSources - - if (WebService["Enabled"]) { - adapters := webService.GetAdapters([ - "adapterType", "entity_data", - "entityType", event.EntityTypeId - ]) - - for key, adapter in adapters { - layerKey := webService["id"] . "." . event.EntityTypeId . "." . key - - if (!layerData.Has(layerKey)) { - layerData[layerKey] := WebServiceAdapterLayerSource(adapter) - } - } - } - } - - EntityFieldGroups(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { - return - } - - webService := this.container["entity_manager.web_service"]["launchpad_api"] - - if (WebService["Enabled"]) { - fieldGroups := event.FieldGroups - - if (!fieldGroups.Has("api")) { - adapters := webService.GetAdapters([ - "adapterType", "entity_data", - "entityType", event.EntityTypeId - ]) - - if (adapters.Count) { - fieldGroups["api"] := Map( - "name", "API", - "weight", 150 - ) - } - } - } - } - - EntityFieldDefinitions(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { + EntityDataParams(event, extra, eventName, hwnd) { + if (HasProp(event.Entity, "isWebServiceEntity") && event.Entity.isWebServiceEntity) { return } - webService := this.container["entity_manager.web_service"]["launchpad_api"] - - if (WebService["Enabled"]) { - fieldDefinitions := event.FieldDefinitions - - adapters := webService.GetAdapters([ - "adapterType", "entity_data", - "entityType", event.EntityTypeId - ]) - - if (adapters.Count) { - fieldDefinitions["LaunchpadApiRef"] := Map( - "description", "The key that is used to look up the entity's data from configured external data sources.", - "help", "It defaults to the key which is usually sufficient, but it can be overridden by setting this value.`n`nAddtionally, multiple copies of the same data source entity can exist by giving them different keys but using the same LaunchpadApiRef", - "group", "api", - "processValue", false, - "modes", Map("simple", Map("formField", false)) - ) - } - } - } - - EntityDetectValues(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { - return - } - - webService := this.container["entity_manager.web_service"]["launchpad_api"] - values := event.Values - entity := event.Entity - - if ( - webService["Enabled"] - && (!values.Has("LaunchpadApiRef") || !values["LaunchpadApiRef"]) - && entity.HasField("LaunchpadApiRef") - && (!entity.RawData.Has["LaunchpadApiRef"] || !entity.RawData["LaunchpadApiRef"]) - ) { - result := "" - - if (event.EntityTypeId == "Launcher") { - platform := entity["Platform"] ? entity["Platform"]["id"] : "" - - result := webService.AdapterRequest( - Map("id", entity["id"], "platform", platform), - Map( - "adapterType", "entity_list", - "entityType", event.EntityTypeId - ) - ) - } else if (HasBase(entity, ManagedEntityBase.Prototype)) { - result := entity["EntityType"] - } else { - result := entity["id"] - } - - if (result) { - values["LaunchpadApiRef"] := result - } - } - } - - ListEntities(event, extra, eventName, hwnd) { - if (event.EntityTypeId == "web_service" || event.EntityTypeId == "web_service_provider") { - return - } - - if (event.includeExtended) { - webService := this.container["entity_manager.web_service"]["launchpad_api"] - entityMgr := this.container["entity_manager." . event.EntityTypeId] - - managedIds := event.includeManaged - ? [] - : entityMgr.EntityQuery(EntityQuery.RESULT_TYPE_IDS).Execute() - - if (webService["Enabled"]) { - results := webService.AdapterRequest( - "", - Map( - "adapterType", "entity_list", - "entityType", event.EntityTypeId - ), - "read", - true - ) - - if (results && HasBase(results, Array.Prototype)) { - for index, id in results { - exists := false - - for , existingId in event.EntityList { - if (existingId == id) { - exists := true - break - } - } - - if (!exists && !event.includeManaged) { - for , managedId in managedIds { - if (managedId == id) { - exists := true - break - } - } - } + test := "here" - if (!exists) { - event.EntityList.Push(id) - } - } - } - + ; TODO figure out how to access these values while the data layers are still being loaded + if (event.WebService["id"] == "launchpad_api") { + if (HasBase(event.Entity, LauncherEntity.Prototype)) { + event.Params["platformId"] := "Blizzard" ;event.Entity["Platform"]["id"] + } else if (HasBase(event.Entity, LaunchProcessEntity.Prototype)) { + event.Params["platformId"] := "Blizzard" ;event.Entity.ParentEntity["Platform"]["id"] } } } From af33820ab859ea6cf74465ac29147e4b6df8b6db Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:01:12 -0500 Subject: [PATCH 116/138] Add an additional check to ensure that the referenced Platform exists before accessing it in WebServiceEntity --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 411e4f2a..f2b2ffc9 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -107,7 +107,7 @@ class WebServiceEntity extends FieldableEntity { IsAuthenticated() { isAuthenticated := false - if (this["Provider"]["SupportsAuthentication"]) { + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { isAuthenticated := this["Provider"]["Authenticator"].IsAuthenticated(this) } @@ -115,13 +115,13 @@ class WebServiceEntity extends FieldableEntity { } Login() { - if (this["Provider"]["SupportsAuthentication"]) { + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { this["Provider"]["Authenticator"].Login(this) } } Logout() { - if (this["Provider"]["SupportsAuthentication"]) { + if (this["Provider"] && this["Provider"]["SupportsAuthentication"]) { this["Provider"]["Authenticator"].Logout(this) } } From fd74dd6bc2c2f2411bee958efdcbf052e3974f69 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:02:46 -0500 Subject: [PATCH 117/138] Fix adapter definitions and organize parameters in LaunchpadApi.module.json --- .../LaunchpadApi/LaunchpadApi.module.json | 180 ++++++++++-------- 1 file changed, 103 insertions(+), 77 deletions(-) diff --git a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json index 78ac16ec..3bb1307d 100644 --- a/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json +++ b/Lib/Shared/Modules/LaunchpadApi/LaunchpadApi.module.json @@ -16,21 +16,6 @@ }, "parameters": { "app.supports_update_check": true, - "web_services.providers.launchpad_api": { - "name": "Launchpad API", - "EndpointUrl": "https://api.launchpad.games/v1", - "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", - "AuthenticationRefreshPath": "token", - "IconSrc": "logo", - "SupportsAuthentication": true, - "Authenticator": "jwt", - "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", - "LoginWindow": "LaunchpadLoginWindow" - }, - "web_services.services.launchpad_api": { - "name": "Launchpad API", - "Provider": "launchpad_api" - }, "web_services.adapters.launchpad_api.account_info": { "dataType": "account_info", "requestPath": "/status", @@ -40,84 +25,125 @@ "email": "account" } }, - "web_services.adapters.launchpad_api.error_submission": { - "dataType": "error_submission", - "requestPath": "/error-submissions/{submission}", - "cacheResponse": false, - "readAllow": false, - "createAllow": true, - "createAuth": false + "web_services.adapters.launchpad_api.hello": { + "dataType": "availability_check", + "requestPath": "/hello", + "cacheResponse": false }, - "web_services.adapters.launchpad_api.feedback_submission": { - "dataType": "feedback_submission", - "requestPath": "/feedback-submissions/{submission}", - "cacheResponse": false, - "readAllow": false, - "createAllow": true, - "createAuth": false - }, - "web_services.adapters.launchpad_api.game_submission": { - "dataType": "game_submission", - "requestPath": "/game-submissions/{submission}", - "cacheResponse": false, - "readAllow": false, - "createAllow": true, - "createAuth": false - }, - "web_services.adapters.launchpad_api.release_info": { - "dataType": "release_info", - "requestPath": "/release-info/{tag}", - "cacheMaxAge": 1800 - }, - "web_services.adapters.launchpad_api.platform_list": { - "dataType": "entity_list", - "requestPath": "/game-platforms", - "entityType": "platform" - }, - "web_services.adapters.launchpad_api.platform_data": { + "web_services.adapters.launchpad_api.game_process_launcher_defaults": { "dataType": "entity_data", - "requestPath": "/game-platforms/{id}", - "dataSelector": "data.defaults", - "entityType": "platform" - }, - "web_services.adapters.launchpad_api.game_type_list": { - "dataType": "entity_list", - "requestPath": "/game-types", - "entityType": "managed_game" + "requestPath": "/games/{id}", + "dataSelector": "gameProcessDefaults", + "entityType": "game_process", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 }, - "web_services.adapters.launchpad_api.game_type_data": { + "web_services.adapters.launchpad_api.game_process_platform_defaults": { "dataType": "entity_data", - "requestPath": "/game-types/{id}", - "dataSelector": "data.defaults", - "entityType": "managed_game" + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.gameProcessDefaults", + "entityType": "game_process", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.launcher_defaults": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "dataSelector": "defaults", + "entityType": "launcher", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 }, "web_services.adapters.launchpad_api.launcher_list": { "dataType": "entity_list", - "requestPath": "/games", - "entityType": "launcher" - }, - "web_services.adapters.launchpad_api.launcher_data": { - "dataType": "entity_data", - "requestPath": "/games/{id}", - "dataSelector": "data.defaults", + "requestPath": "/game-keys", "entityType": "launcher" }, "web_services.adapters.launchpad_api.launcher_lookup": { "dataType": "entity_lookup", - "requestPath": "/lookup/{id}/{platform}", + "requestPath": "/lookup/{id}/{platformId}", "dataSelector": "id", - "entityType": "launcher" + "entityType": "launcher", + "requiredParams": ["id"] }, - "web_services.adapters.launchpad_api.launcher_type_list": { - "dataType": "entity_list", - "requestPath": "/launcher-types", - "entityType": "managed_launcher" + "web_services.adapters.launchpad_api.launcher_platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.launcherDefaults", + "entityType": "launcher", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.launcher_process_launcher_defaults": { + "dataType": "entity_data", + "requestPath": "/games/{id}", + "dataSelector": "launcherProcessDefaults", + "entityType": "launcher_process", + "requiredParams": ["id"], + "tags": ["defaults"], + "weight": 5 }, - "web_services.adapters.launchpad_api.launcher_type_data": { + "web_services.adapters.launchpad_api.launcher_process_platform_defaults": { + "dataType": "entity_data", + "requestPath": "/platforms/{platformId}", + "dataSelector": "data.launcherProcessDefaults", + "entityType": "launcher_process", + "requiredParams": ["platformId"], + "tags": ["defaults"], + "weight": 2 + }, + "web_services.adapters.launchpad_api.platform_defaults": { "dataType": "entity_data", - "requestPath": "/launcher-types/{id}", + "requestPath": "/platforms/{id}", "dataSelector": "data.defaults", - "entityType": "managed_launcher" + "entityType": "platform", + "requiredParams": ["id"], + "tags": ["defaults"] + }, + "web_services.adapters.launchpad_api.platform_list": { + "dataType": "entity_list", + "requestPath": "/platforms", + "entityType": "platform" + }, + "web_services.adapters.launchpad_api.release_info": { + "dataType": "release_info", + "requestPath": "/release-info/{version}", + "cacheMaxAge": 1800 + }, + "web_services.adapters.launchpad_api.submit_error": { + "dataType": "error_submission", + "requestPath": "/submit-error", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.adapters.launchpad_api.submit_feedback": { + "dataType": "feedback_submission", + "requestPath": "/submit-feedback", + "cacheResponse": false, + "readAllow": false, + "createAllow": true, + "createAuth": false + }, + "web_services.providers.launchpad_api": { + "name": "Launchpad API", + "EndpointUrl": "https://api.launchpad.games/v1", + "AuthenticationEndpointUrl": "https://securetoken.googleapis.com/v1", + "AuthenticationRefreshPath": "token", + "IconSrc": "logo", + "SupportsAuthentication": true, + "Authenticator": "jwt", + "AppKey": "AIzaSyCbwzOWJjTft77P96dV5VB3dAx9TjdDowQ", + "LoginWindow": "LaunchpadLoginWindow" + }, + "web_services.services.launchpad_api": { + "name": "Launchpad API", + "Provider": "launchpad_api" } }, "services": { From 8a0ff45e00e57262f30f8a23cf6909aafb1f9ffa Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:05:03 -0500 Subject: [PATCH 118/138] Add ReferencedEntities, ChildEntities, and ChildEntityData properties to EntityBase --- .../Volantis.Entity/Entity/EntityBase.ahk | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index f4a242c4..314b6c5f 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -31,7 +31,7 @@ class EntityBase { } FieldData { - Get => this.GetData().GetMergedData() + get => this.GetData().GetMergedData() } Name { @@ -46,7 +46,18 @@ class EntityBase { ParentEntity { get => this.GetParentEntity() - set => this.SetParentEntity(value) + } + + ReferencedEntities { + get => this.GetReferencedEntities(false) + } + + ChildEntities { + get => this.GetReferencedEntities(true) + } + + ChildEntityData { + get => this.GetAllChildEntityData() } __Item[key := ""] { @@ -287,7 +298,7 @@ class EntityBase { this.GetData().UnloadAllLayers(reloadUserData) if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { + for index, entityObj in this.ChildEntities { entityObj.RefreshEntityData(recurse, reloadUserData) } } @@ -319,7 +330,7 @@ class EntityBase { this.eventMgr.DispatchEvent(event) if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { + for index, entityObj in this.ChildEntities { entityObj.SaveEntity(recurse) } } @@ -388,7 +399,7 @@ class EntityBase { if (recurse) { diffs := [diff] - for index, referencedEntity in this.GetReferencedEntities(true) { + for index, referencedEntity in this.ChildEntities { diffs.Push(referencedEntity.DiffChanges(recurse)) } From 2b74d9407402ca12be4827af704362e47e4c64e1 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:05:56 -0500 Subject: [PATCH 119/138] Update handling of parent entity discover in EntityBase --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 314b6c5f..c0b94f4b 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -2,6 +2,8 @@ class EntityBase { idVal := "" entityTypeIdVal := "" parentEntityObj := "" + parentEntityTypeId := "" + parentEntityId := "" parentEntityStorage := false container := "" app := "" @@ -86,13 +88,11 @@ class EntityBase { this.cloner := container.Get("cloner.list") this.parentEntityStorage := parentEntityStorage - if (!parentEntity) { - parentEntity := this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer) + if (!parentEntity && this.parentEntityObj) { + parentEntity := this.parentEntityObj } - if (parentEntity) { - this.SetParentEntity(parentEntity) - } + this.DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity) this._createEntityData() this.SetupEntity() @@ -156,6 +156,7 @@ class EntityBase { if (event.ParentEntity) { this.parentEntityObj := event.ParentEntity } else if (event.ParentEntityId) { + this.parentEntityTypeId := event.ParentEntityTypeId this.parentEntityId := event.ParentEntityId this.parentEntityMgr := event.ParentEntityManager ? event.ParentEntityManager From 744038e77708b46791f70a4cb960380638f0aff7 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:06:17 -0500 Subject: [PATCH 120/138] Add missing merger attribute --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index c0b94f4b..98e4a1db 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -15,6 +15,7 @@ class EntityBase { loaded := false dataLayer := "data" dataLoaded := false + merger := "" cloner := "" Id { From 4dd1034f51fb48081441c1f13cb8a894bb9a988e Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:07:01 -0500 Subject: [PATCH 121/138] Refactor entity loading slightly, and use a "loading" parameter to try and prevent recursive loops --- .../Volantis.Entity/Entity/EntityBase.ahk | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 98e4a1db..2ada223d 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -12,6 +12,7 @@ class EntityBase { storageObj := "" idSanitizer := "" sanitizeId := true + loading := false loaded := false dataLayer := "data" dataLoaded := false @@ -99,7 +100,7 @@ class EntityBase { this.SetupEntity() if (autoLoad) { - this.LoadEntity(false, true) + this.LoadEntity() } } @@ -275,24 +276,28 @@ class EntityBase { } LoadEntity(reload := false, recurse := false) { - loaded := false + if (this.loading) { + throw AppException("Attempting to load entity with a circular reference.") + } - if (!this.loaded || reload) { - this.RefreshEntityData(true) + if (!this.loading && this.dataLoaded && (!this.loaded || reload)) { + this.loading := true + this.RefreshEntityData(recurse) this.CreateSnapshot("original") this.loaded := true loaded := true - } + this.loading := false - if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { - entityObj.LoadEntity(reload, recurse) + if (recurse) { + for index, entityObj in this.ChildEntities { + entityObj.LoadEntity(reload, recurse) + } } - } - if (loaded) { - event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this) - this.eventMgr.DispatchEvent(event) + if (loaded) { + event := EntityEvent(EntityEvents.ENTITY_LOADED, this.entityTypeId, this) + this.eventMgr.DispatchEvent(event) + } } } From 04e7f67c50704d1dffae463bf5dc246b1511dd19 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:07:39 -0500 Subject: [PATCH 122/138] Update recursive handling of snapshots in EntityBase --- .../Volantis.Entity/Entity/EntityBase.ahk | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 2ada223d..fc73bbe1 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -239,32 +239,24 @@ class EntityBase { return this.GetData().DeleteValue(key, this.dataLayer) } - CreateSnapshot(name, recurse := true) { - this.GetData().CreateSnapshot(name) - + CreateSnapshot(name, recurse := false) { if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { - if (entityObj.HasOwnDataStorage()) { - entityObj.GetData().CreateSnapshot(name, recurse) - } + for index, entityObj in this.ChildEntities { + entityObj.GetData().CreateSnapshot(name, recurse) } } - return this - } + this.GetData().CreateSnapshot(name) - HasOwnDataStorage() { - return this.dataObj + return this } - RestoreSnapshot(name, recurse := true) { + RestoreSnapshot(name, recurse := false) { this.GetData().RestoreSnapshot(name) if (recurse) { - for index, entityObj in this.GetReferencedEntities(true) { - if (entityObj.HasOwnDataStorage()) { - entityObj.GetData().RestoreSnapshot(name, recurse) - } + for index, entityObj in this.ChildEntities { + entityObj.GetData().RestoreSnapshot(name, recurse) } } From b026b19981dfed70a73fedc18b0014a6e5f63dc4 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:08:53 -0500 Subject: [PATCH 123/138] Update _getLayerSources in EntityBase to use parent entity storage when configured --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index fc73bbe1..17e3456d 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -134,10 +134,14 @@ class EntityBase { } _getLayerSources() { + layerSource := this.parentEntityStorage + ? ParentEntityLayerSource(this) + : EntityStorageLayerSource(this.storageObj, this.GetStorageId()) + return Map( "defaults", ObjBindMethod(this, "InitializeDefaults"), "auto", ObjBindMethod(this, "AutoDetectValues"), - "data", EntityStorageLayerSource(this.storageObj, this.GetStorageId()) + "data", layerSource ) } From cb23292844a7d700001a6bb135853ba953d9ccb6 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:09:37 -0500 Subject: [PATCH 124/138] Add UpdateDefaults method to EntityBase --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 17e3456d..8ba83fa1 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -488,6 +488,16 @@ class EntityBase { return text } + UpdateDefaults(recurse := false) { + if (recurse) { + for key, child in this.ChildEntities { + child.UpdateDefaults(recurse) + } + } + + this.GetData().UnloadAllLayers(false) + } + GetAllChildEntityData() { return this.GetData().GetExtraData() } From 124c6cb098195012f96f2434248c13a6f866a76c Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:11:01 -0500 Subject: [PATCH 125/138] Fix event class name and event name references --- .../WebServices/Event/WebServicesResponseEvent.ahk | 2 +- .../WebServiceRequest/WebServiceRequestBase.ahk | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk b/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk index c2d5773d..80010b44 100644 --- a/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk +++ b/Lib/Shared/Modules/WebServices/Event/WebServicesResponseEvent.ahk @@ -1,4 +1,4 @@ -class WebServiceResponseEvent extends EventBase { +class WebServicesResponseEvent extends EventBase { _requestObj := "" _responseObj := "" diff --git a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk index 8e7f56d8..e89cfce5 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceRequest/WebServiceRequestBase.ahk @@ -136,10 +136,10 @@ class WebServiceRequestBase { httpReqObj.requestHeaders["Cache-Control"] := "no-cache" } - event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_HTTP_REQ_ALTER, this) + event := WebServicesRequestEvent(WebServicesEvents.HTTP_REQ_ALTER, this) this.eventMgr.DispatchEvent(event) - event := WebServiceRequestEvent(WebServicesEvents.WEB_SERVICES_REQUEST_PRESEND, this) + event := WebServicesRequestEvent(WebServicesEvents.REQUEST_PRESEND, this) this.eventMgr.DispatchEvent(event) httpReqObj.Send(this.GetMethod(), this.GetData()) @@ -147,7 +147,7 @@ class WebServiceRequestBase { this._cacheResponse() } - event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_RESPONSE_ALTER, this, this.responseObj) + event := WebServicesResponseEvent(WebServicesEvents.RESPONSE_ALTER, this, this.responseObj) this.eventMgr.DispatchEvent(event) this.responseObj := event.Response @@ -165,7 +165,7 @@ class WebServiceRequestBase { _createCachedResponse() { response := CachedWebServiceResponse(this.webServiceEnt, this) - event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_CACHED_RESPONSE_CREATED, this, response) + event := WebServicesResponseEvent(WebServicesEvents.CACHED_RESPONSE_CREATED, this, response) this.eventMgr.DispatchEvent(event) return event.Response @@ -174,7 +174,7 @@ class WebServiceRequestBase { _createHttpReqResponse() { response := HttpReqWebServiceResponse(this.webServiceEnt, this) - event := WebServiceResponseEvent(WebServicesEvents.WEB_SERVICES_HTTP_RESPONSE_CREATED, this, response) + event := WebServicesResponseEvent(WebServicesEvents.HTTP_RESPONSE_CREATED, this, response) this.eventMgr.DispatchEvent(event) return event.Response From 322f1901f0386c22e4e7458325e860f25e337b62 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:11:22 -0500 Subject: [PATCH 126/138] Add missing argument to event_subscriber.web_services --- Lib/Shared/Modules/WebServices/WebServices.module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/WebServices.module.json b/Lib/Shared/Modules/WebServices/WebServices.module.json index 99634be6..1f19e86b 100644 --- a/Lib/Shared/Modules/WebServices/WebServices.module.json +++ b/Lib/Shared/Modules/WebServices/WebServices.module.json @@ -96,7 +96,7 @@ }, "event_subscriber.web_services": { "class": "WebServicesEventSubscriber", - "arguments": ["@{}"], + "arguments": ["@{}", "@web_services.adapter_manager"], "tags": ["event_subscriber"] }, "state.web_services": { From a6bbd3eeb8a49475a072e05a4a723f7d7f8a08cf Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:12:29 -0500 Subject: [PATCH 127/138] Change dataType property to dataClass on adapters --- .../WebServiceAdapter/FileWebServiceAdapter.ahk | 2 +- .../WebServiceAdapter/JsonWebServiceAdapter.ahk | 2 +- .../WebServiceAdapter/WebServiceAdapterBase.ahk | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk index 22d44841..e922134a 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/FileWebServiceAdapter.ahk @@ -1,5 +1,5 @@ class FileWebServiceAdapter extends WebServiceAdapterBase { - dataType := "" + dataClass := "" ; @todo Implement file downloading } diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk index 7c61899b..5f63b104 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/JsonWebServiceAdapter.ahk @@ -1,3 +1,3 @@ class JsonWebServiceAdapter extends WebServiceAdapterBase { - dataType := "JsonData" + dataClass := "JsonData" } \ No newline at end of file diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk index b34ea21d..d963e6a8 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -2,7 +2,7 @@ class WebServiceAdapterBase { container := "" webService := "" definition := "" - dataType := "" + dataClass := "" merger := "" operationTypes := ["create", "read", "update", "delete"] @@ -270,9 +270,9 @@ class WebServiceAdapterBase { } _parseData(data, params) { - if (data && this.definition["dataType"]) { - dataType := this.definition["dataType"] - data := %dataType%().FromString(data) + if (data && this.dataClass) { + dataClass := this.dataClass + data := %dataClass%().FromString(&data) if (this.definition["dataSelector"]) { dataSelector := this.definition["dataSelector"] From 9e3a09e9605620fea12902f1fd2d7a82b4ab32e2 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:13:30 -0500 Subject: [PATCH 128/138] Enhance WebServiceAdapterBase code --- .../WebServiceAdapterBase.ahk | 55 +++++++++++++++++-- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk index d963e6a8..8e30611e 100644 --- a/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk +++ b/Lib/Shared/Modules/WebServices/WebServiceAdapter/WebServiceAdapterBase.ahk @@ -58,15 +58,23 @@ class WebServiceAdapterBase { "deleteMethod", "PUT", "deleteAuth", true, "dataMap", Map(), - "dataSelector", [] + "dataSelector", "", + "weight", 0, + "entityType", "", + "tags", [], + "requiredParams", [], ) } SupportsOperation(operation) { supported := false - if (this.operationTypes.Contains(operation)) { - supported := this.definition[operation + "Allow"] + for index, operationType in this.operationTypes { + if (operation == operationType) { + supported := true + + break + } } return supported @@ -240,12 +248,47 @@ class WebServiceAdapterBase { return requestPath } - _request(params, method, data, cacheResponse) { + _validateParams(params) { + if (!params) { + params := Map() + } + + valid := true + requiredParams := this.definition["requiredParams"] + + if (requiredParams) { + if (Type(requiredParams) == "String") { + requiredParams := [requiredParams] + } + + for , requiredParam in requiredParams { + if (!params.Has(requiredParam) || !params[requiredParam]) { + valid := false + + break + } + } + } + + return valid + } + + _request(params, method, data, useAuthentication, cacheResponse) { + if (!this._validateParams(params)) { + throw AppException("The data adapter request was called with invalid or missing parameters.") + } + + requestPath := this.definition["requestPath"] + + for key, value in params { + + } + return this.webService.Request( - this.definition["requestPath"], + this._requestPath(params), method, data, - this.definition["useAuthentication"], + useAuthentication, cacheResponse ) } From e0512087c15973444f6082523b05cf96285b0e70 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:13:49 -0500 Subject: [PATCH 129/138] Use web service calls in DetectedGameEditor and refactor a bit of the code --- Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk | 61 ++++++++----------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk index 2e53e2a3..3aee2ba9 100644 --- a/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk +++ b/Lib/Launchpad/Gui/Form/DetectedGameEditor.ahk @@ -1,4 +1,4 @@ -class DetectedGameEditor extends FormGuiBase { +class DetectedGameEditor extends FormGuiBase { detectedGameObj := "" newValues := Map() missingFields := Map() @@ -22,44 +22,35 @@ class DetectedGameEditor extends FormGuiBase { this.knownPlatforms := [] this.knownGames := [] - this.launcherTypes := [] - this.gameTypes := [] ; @todo replace this, or at least refactor it to live somewhere else - if (this.container.Has("entity_manager.web_service")) { - mgr := this.container["entity_manager.web_service"] - - if (mgr.Has("launchpad_api") && mgr["launchpad_api"]["Enabled"]) { - webService := mgr["launchpad_api"] - knownMap := Map( - "platform", "knownPlatforms", - "game", "knownGames", - "managed_launcher", "launcherTypes", - "managed_game", "gameTypes" - ) - - for entityTypeId, varName in knownMap { - results := webService.AdapterRequest("", Map( - "adapterType", "entity_list", - "entityType", entityTypeId - ), "read", true) - - if (results) { - for , idList in results { - if (idList) { - for , id in idList { - exists := false - - for , item in %varName% { - if (item == id) { - exists := true - break - } + if (this.container.Has("web_services.adapter_manager")) { + knownMap := Map( + "platform", "knownPlatforms", + "launcher", "knownGames", + ) + + for entityTypeId, varName in knownMap { + results := this.container["web_services.adapter_manager"].AdapterRequest("", Map( + "dataType", "entity_list", + "entityType", entityTypeId + ), "read", true) + + if (results) { + for , idList in results { + if (idList) { + for , id in idList { + exists := false + + for , item in %varName% { + if (item == id) { + exists := true + break } + } - if (!exists) { - this.%varName%.Push(id) - } + if (!exists) { + this.%varName%.Push(id) } } } From 6a27ffd3bebd3118aca985749bcbd3d2951e0eaa Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:14:42 -0500 Subject: [PATCH 130/138] Add isWebServiceEntity to web service entities, and remove old adapters and adapterFactory --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 3 +-- .../Modules/WebServices/Entity/WebServiceProviderEntity.ahk | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index f2b2ffc9..6c46d5c2 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -3,8 +3,7 @@ class WebServiceEntity extends FieldableEntity { stateObj := "" persistentStateObj := "" statusIndicators := [] - adapters := Map() - adapterFactory := "" + isWebServiceEntity := true Authenticated { get => this.IsAuthenticated() diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk index 573120f3..5c54ae3a 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceProviderEntity.ahk @@ -1,4 +1,6 @@ class WebServiceProviderEntity extends FieldableEntity { + isWebServiceEntity := true + BaseFieldDefinitions() { definitions := super.BaseFieldDefinitions() From 1cef906acdfdc2b5e10b87f42829005684998cd3 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:15:10 -0500 Subject: [PATCH 131/138] Add CacheResponses and DefaultResponseCacheExpiration fields to WebServiceEntity --- .../Modules/WebServices/Entity/WebServiceEntity.ahk | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index 6c46d5c2..cdc2a7a9 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -86,6 +86,18 @@ class WebServiceEntity extends FieldableEntity { "default", (this.idVal == "launchpad_api") ) + definitions["CacheResponses"] := Map( + "type", "boolean", + "required", false, + "default", true + ) + + definitions["DefaultResponseCacheExpiration"] := Map( + "type", "string", + "required", false, + "default", 3600 + ) + return definitions } From bd64d5b2628724e85a7ae26cf6800d2e5245716b Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:24:10 -0500 Subject: [PATCH 132/138] Rename new web service fields to ResponseCache and ResponseCacheDefaultExpireSeconds --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index cdc2a7a9..a17a8ad9 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -86,13 +86,14 @@ class WebServiceEntity extends FieldableEntity { "default", (this.idVal == "launchpad_api") ) - definitions["CacheResponses"] := Map( + definitions["ResponseCache"] := Map( "type", "boolean", "required", false, "default", true ) - definitions["DefaultResponseCacheExpiration"] := Map( + definitions["ResponseCacheDefaultExpireSeconds"] := Map( + "title", "Response Cache - Default Expiration (seconds)" "type", "string", "required", false, "default", 3600 From 9ca8909acea70868d59f6af48d28f564f2437c7c Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:24:34 -0500 Subject: [PATCH 133/138] Missing comma --- Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk index a17a8ad9..d0caf51b 100644 --- a/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk +++ b/Lib/Shared/Modules/WebServices/Entity/WebServiceEntity.ahk @@ -93,7 +93,7 @@ class WebServiceEntity extends FieldableEntity { ) definitions["ResponseCacheDefaultExpireSeconds"] := Map( - "title", "Response Cache - Default Expiration (seconds)" + "title", "Response Cache - Default Expiration (seconds)", "type", "string", "required", false, "default", 3600 From 8b90b24ec79f1a3c04703cfb780756519a053cbf Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:35:33 -0500 Subject: [PATCH 134/138] Fix parent entity assignment --- Lib/Launchpad/Entity/LaunchProcessEntity.ahk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/Launchpad/Entity/LaunchProcessEntity.ahk b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk index d28b726c..a102f336 100644 --- a/Lib/Launchpad/Entity/LaunchProcessEntity.ahk +++ b/Lib/Launchpad/Entity/LaunchProcessEntity.ahk @@ -2,9 +2,7 @@ class LaunchProcessEntity extends FieldableEntity { defaultClass := "Default" DiscoverParentEntity(container, eventMgr, id, storageObj, idSanitizer, parentEntity := "") { - ; TODO fix circular reference occurring - - return parentEntity + this.parentEntityObj := parentEntity ? parentEntity : container.Get("entity_manager.launcher")[id] } From 5f02482a272d5da024bfae189b0a10242af0ec0a Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:35:42 -0500 Subject: [PATCH 135/138] Remove test code --- .../LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk index 8b864ab7..1d349909 100644 --- a/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk +++ b/Lib/Shared/Modules/LaunchpadApi/EventSubscriber/LaunchpadApiSubscriber.ahk @@ -12,8 +12,6 @@ class LaunchpadApiSubscriber extends EventSubscriberBase { return } - test := "here" - ; TODO figure out how to access these values while the data layers are still being loaded if (event.WebService["id"] == "launchpad_api") { if (HasBase(event.Entity, LauncherEntity.Prototype)) { From 113b1677a7af98e224ef2da7d2d0a87fd595b1d6 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:50:40 -0500 Subject: [PATCH 136/138] Change extra key in data storage from "extra" to "_extra" --- Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk index 6575e28e..60a4095d 100644 --- a/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk +++ b/Lib/Shared/Volantis.Data/LayeredData/LayeredDataBase.ahk @@ -26,7 +26,7 @@ class LayeredDataBase { userLayers := ["data"] loadingLayers := Map() extraDataLayer := "data" - extraDataKey := "extra" + extraDataKey := "_extra" static NO_VALUE := ":NO_VAL:" From e8d9c08f2317db0c3ace49389891cb68d0140b09 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 04:50:49 -0500 Subject: [PATCH 137/138] Fix child entity data storage --- Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk index 8ba83fa1..079af972 100644 --- a/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk +++ b/Lib/Shared/Volantis.Entity/Entity/EntityBase.ahk @@ -517,7 +517,7 @@ class EntityBase { data := Map() } - this.GetData().SetExtraData(dataKey, data) + this.GetData().SetExtraData(data, dataKey) return this } From 0e7c3d674b49084145703597c1e6bd0d50923e63 Mon Sep 17 00:00:00 2001 From: Ben McClure Date: Sat, 17 Dec 2022 05:03:22 -0500 Subject: [PATCH 138/138] Fix web service ref discovery in the case of the wrong data type being received --- .../EventSubscriber/WebServicesEventSubscriber.ahk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk index 97023350..3e7a0598 100644 --- a/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk +++ b/Lib/Shared/Modules/WebServices/EventSubscriber/WebServicesEventSubscriber.ahk @@ -267,8 +267,8 @@ class WebServicesEventSubscriber extends EventSubscriberBase { webService ) - if (!result) { - result := event.Entity["id"] + if (!result || Type(result) != "String") { + result := "" ; TODO decide whether to default to blank or the entity ID } event.Values[fieldId] := result