UNPKG

@xmobitea/gn-server

Version:

GearN Server by XmobiTea (Pro)

669 lines (579 loc) 20.7 kB
import { GNNetwork, Action0, Action1, Action2, Action3, Action4, ConverterService, OwnerType, ErrorCode, ParameterCode, Commands, EventCode, OperationCode, ReturnCode, CodeHelper, DataMember, StringDataMember, BooleanDataMember, GNHashtableDataMember, GNArrayDataMember, NumberDataMember, GNHashtableBuilder, GNHashtable, GNArrayBuilder, GNArray, InvalidMemberType, InvalidMember, RequestType, RequestRole, CustomOperationRequest, CustomOperationRequestAbstract, CustomOperationResponse, Constructor, AbstractConstructor, FieldDataType, GNObjectFieldMetadata, GNObjectMetadata, CustomOperationResponseAbstract, IServerEventHandler, OperationEvent, OperationRequest, OperationResponse, GenericModels, CharacterPlayerModels, ContentModels, GamePlayerModels, GroupModels, InventoryModels, MasterPlayerModels, StoreInventoryModels, MultiplayerModels, CloudScriptModels, GNServerSettingsOptions, GNServerSettings, PermissionDataItem, FriendStatus, GoogleLoginType, GroupStatus, GNDebug, ItemType, StoreItemType, LogType, GNSupport, PostType, GetAuthInfoResponse, PushPlatformType, StoreReceiveType, GNUtils, } from "@xmobitea/gn-typescript-client"; import * as mongodb from "mongodb"; import * as axios from "axios"; enum CloudScriptEventType { SendToUser = 0, SendToMore = 1, SendToAll = 2, } interface CloudScriptEvent { eventType: CloudScriptEventType; operationEvent: { eventCode: string; parameters: {} | null; }; receiver: string[] | string | null; } class CloudScriptAdmin { public readonly characterPlayer = GNNetwork.characterPlayer.admin; public readonly content = GNNetwork.content.admin; public readonly gamePlayer = GNNetwork.gamePlayer.admin; public readonly group = GNNetwork.group.admin; public readonly inventory = GNNetwork.inventory.admin; public readonly masterPlayer = GNNetwork.masterPlayer.admin; public readonly storeInventory = GNNetwork.storeInventory.admin; public readonly multiplayer = GNNetwork.multiplayer.admin; public readonly cloudScript = GNNetwork.cloudScript.admin; public readonly dashboard = { getAdminAccountList: GNNetwork.dashboard.getAdminAccountList, getAdminAccountListAsync: GNNetwork.dashboard.getAdminAccountListAsync, getGameInformation: GNNetwork.dashboard.getGameInformation, getGameInformationAsync: GNNetwork.dashboard.getGameInformationAsync, getGameList: GNNetwork.dashboard.getGameList, getGameListAsync: GNNetwork.dashboard.getGameListAsync, getMasterGameSettings: GNNetwork.dashboard.getMasterGameSettings, getMasterGameSettingsAsync: GNNetwork.dashboard.getMasterGameSettingsAsync, getSecretInfoInformation: GNNetwork.dashboard.getSecretInfoInformation, getSecretInfoInformationAsync: GNNetwork.dashboard.getSecretInfoInformationAsync, getSecretInfoList: GNNetwork.dashboard.getSecretInfoList, getSecretInfoListAsync: GNNetwork.dashboard.getSecretInfoListAsync, getServerLog: GNNetwork.dashboard.getServerLog, getServerLogAsync: GNNetwork.dashboard.getServerLogAsync, setGameInformation: GNNetwork.dashboard.setGameInformation, setGameInformationAsync: GNNetwork.dashboard.setGameInformationAsync, getUsernameAdminAccount: GNNetwork.dashboard.getUsernameAdminAccount, getUsernameAdminAccountAsync: GNNetwork.dashboard.getUsernameAdminAccountAsync, getAnalytics: GNNetwork.dashboard.getAnalytics, getAnalyticsAsync: GNNetwork.dashboard.getAnalyticsAsync, resetStatisticsLeaderboard: GNNetwork.dashboard.resetStatisticsLeaderboard, resetStatisticsLeaderboardAsync: GNNetwork.dashboard.resetStatisticsLeaderboardAsync, getBackupStatisticsLeaderboardVersion: GNNetwork.dashboard.getBackupStatisticsLeaderboardVersion, getBackupStatisticsLeaderboardVersionAsync: GNNetwork.dashboard.getBackupStatisticsLeaderboardVersionAsync, getServerGameData: GNNetwork.dashboard.getServerGameData, getServerGameDataAsync: GNNetwork.dashboard.getServerGameDataAsync, deleteInDatabase: GNNetwork.dashboard.deleteInDatabase, deleteInDatabaseAsync: GNNetwork.dashboard.deleteInDatabaseAsync, }; public send( requestType: RequestType, role: RequestRole, request: OperationRequest, onResponse: Action1<OperationResponse>, overrideAuthToken: string, overrideSecretKey: string, customTags: GNHashtable ): void { GNNetwork.sendViaHttp(requestType, role, request, onResponse, overrideAuthToken, overrideSecretKey, customTags); } public sendAsync( requestType: RequestType, role: RequestRole, request: OperationRequest, overrideAuthToken: string, overrideSecretKey: string, customTags: GNHashtable ): Promise<OperationResponse> { return GNNetwork.sendViaHttpAsync(requestType, role, request, overrideAuthToken, overrideSecretKey, customTags); } } class CloudScriptDatabase { private static readonly DOT: string = "."; private static readonly RUNTIME: string = "Runtime" + CloudScriptDatabase.DOT; private static readonly META: string = "Meta" + CloudScriptDatabase.DOT; private static readonly SYSTEM: string = "System" + CloudScriptDatabase.DOT; private client: mongodb.MongoClient; private db: mongodb.Db; private logClient: mongodb.MongoClient; private logDb: mongodb.Db; init(url: string, dbName: string, logUrl: string, logDbName: string, options?: mongodb.MongoClientOptions): void { this.client = new mongodb.MongoClient(url, options); this.db = this.client.db(dbName); if (!logUrl || logUrl == "" || logUrl == url) { this.logClient = this.client; this.logDb = this.db; } else { this.logClient = new mongodb.MongoClient(logUrl, options); this.logDb = this.logClient.db(logDbName); } } public runtimeCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.RUNTIME + collectionName); } public metaCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.META + collectionName); } public systemCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.SYSTEM + collectionName); } public runtimeGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.RUNTIME + gameId + CloudScriptDatabase.DOT + collectionName); } public metaGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.META + gameId + CloudScriptDatabase.DOT + collectionName); } public systemGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.collection<TSchema>(CloudScriptDatabase.SYSTEM + gameId + CloudScriptDatabase.DOT + collectionName); } public collection<TSchema extends mongodb.Document = mongodb.Document>(fullCollectionName: string): mongodb.Collection<TSchema> { return this.db.collection<TSchema>(fullCollectionName); } public createCollection(name: string, options?: mongodb.CreateCollectionOptions): Promise<mongodb.Collection<mongodb.BSON.Document>> { return this.db.createCollection(name, options); } public logRuntimeCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.RUNTIME + collectionName); } public logMetaCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.META + collectionName); } public logSystemCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.SYSTEM + collectionName); } public logRuntimeGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.RUNTIME + gameId + CloudScriptDatabase.DOT + collectionName); } public logMetaGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.META + gameId + CloudScriptDatabase.DOT + collectionName); } public logSystemGameCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, gameId: string): mongodb.Collection<TSchema> { return this.logCollection<TSchema>(CloudScriptDatabase.SYSTEM + gameId + CloudScriptDatabase.DOT + collectionName); } public logCollection<TSchema extends mongodb.Document = mongodb.Document>(fullCollectionName: string): mongodb.Collection<TSchema> { return this.logDb.collection<TSchema>(fullCollectionName); } public logCreateCollection<TSchema extends mongodb.Document = mongodb.Document>(collectionName: string, options?: mongodb.CreateCollectionOptions) { return this.logDb.createCollection<TSchema>(collectionName, options) } } class CloudScriptHttp { public async get<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.get<T, R, D>(url, config); } public async delete<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.delete<T, R, D>(url, config); } public async head<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.head<T, R, D>(url, config); } public async options<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.options<T, R, D>(url, config); } public async post<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, data?: any, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.post<T, R, D>(url, data, config); } public async put<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, data?: any, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.put<T, R, D>(url, data, config); } public async patch<T = any, R = axios.AxiosResponse<T>, D = any>(url: string, data?: any, config?: axios.AxiosRequestConfig<D>): Promise<R> { return await axios.default.patch<T, R, D>(url, data, config); } } class CloudScriptSocket { public async sendEventTo(userId: string, operationEvent: OperationEvent): Promise<void> { return await this.sendEvent({ eventType: CloudScriptEventType.SendToUser, receiver: userId, operationEvent: { eventCode: operationEvent.getEventCode(), parameters: operationEvent.getParameters()?.toData() ?? null, } }); } public async sendEventToMoreUser(userIds: string[], operationEvent: OperationEvent): Promise<void> { return await this.sendEvent({ eventType: CloudScriptEventType.SendToMore, receiver: userIds, operationEvent: { eventCode: operationEvent.getEventCode(), parameters: operationEvent.getParameters()?.toData() ?? null, } }); } public async sendEventToAllPlayer(operationEvent: OperationEvent): Promise<void> { return await this.sendEvent({ eventType: CloudScriptEventType.SendToAll, receiver: null, operationEvent: { eventCode: operationEvent.getEventCode(), parameters: operationEvent.getParameters()?.toData() ?? null, } }); } private async sendEvent(event: CloudScriptEvent) { if (process?.send) process.send(event); } } class CloudScriptMail { public async send(email: string, subject: string, contentHtml: string) { if (process?.send) process.send({ eventType: 20, receiver: null, operationEvent: { eventCode: 0, parameters: { email: email, subject: subject, contentHtml: contentHtml, }, }, }); } public async sendToMore(emails: string[], subject: string, contentHtml: string) { if (process?.send) process.send({ eventType: 21, receiver: null, operationEvent: { eventCode: 0, parameters: { emails: emails, subject: subject, contentHtml: contentHtml, }, }, }); } } class CloudScriptPushNotification { public async send(token: string, title: string, body: string, badge?: number, sound?: string, icon?: string, data?: { [k: string]: any }) { if (process?.send) process.send({ eventType: 30, receiver: null, operationEvent: { eventCode: 0, parameters: { token: token, title: title, body: body, badge: badge, sound: sound, icon: icon, data: data, }, }, }); } public async sendToMore(tokens: string[], title: string, body: string, badge?: number, sound?: string, icon?: string, data?: { [k: string]: any }) { if (process?.send) process.send({ eventType: 31, receiver: null, operationEvent: { eventCode: 0, parameters: { tokens: tokens, title: title, body: body, badge: badge, sound: sound, icon: icon, data: data, }, }, }); } public async sendToTopic(topic: string, title: string, body: string, badge?: number, sound?: string, icon?: string, data?: { [k: string]: any }) { if (process?.send) process.send({ eventType: 32, receiver: null, operationEvent: { eventCode: 0, parameters: { topic: topic, title: title, body: body, badge: badge, sound: sound, icon: icon, data: data, }, }, }); } public async subscribeToTopic(tokens: string[], topic: string) { if (process?.send) process.send({ eventType: 33, receiver: null, operationEvent: { eventCode: 0, parameters: { tokens: tokens, topic: topic, }, }, }); } public async unsubscribeFromTopic(tokens: string[], topic: string) { if (process?.send) process.send({ eventType: 34, receiver: null, operationEvent: { eventCode: 0, parameters: { tokens: tokens, topic: topic, }, }, }); } } interface CloudScriptRequest { requestId: string; userId: string; name: string; parameters: object; customTags: { [k: string]: any }; } interface CloudScriptResponse { stats: { memoryUsedInBytes: number; executeTimeInMs: number; }; response: FunctionResponse; responseId: string; logs: string[]; } interface FunctionRequest { userId: string; name: string; parameters: any; customTags: { [k: string]: any }; context: {}; log: (log: any) => void; } enum ExecuteResponseStatus { Ok = 1, Exception = 2, FunctionNameNotFound = 3, } interface FunctionResponse { status: ExecuteResponseStatus; result: any; errorMessage: any; } const ConvertObject = (result: any) => { if (result) { if (result instanceof GNHashtable) { result = (<GNHashtable>result).toData(); } else if (result instanceof GNArray) { result = (<GNArray>result).toData(); } else { let typeofObject = typeof (result); if (typeofObject == "object") { if (Array.isArray(result)) { result = ConvertArray(result as any[]); } else { let answer = {} as any; let keys = Object.keys(result); for (let i = 0; i < keys.length; i++) { let key = keys[i]; let value = ConvertObject(result[key]); if (value) { answer[key] = value; } } result = answer; } } else if (typeofObject == "function") { result = null; } } } return result; } const ConvertArray = (results: any[]) => { let answer: any[] = []; for (let i = 0; i < results.length; i++) { let result = ConvertObject(results[i]); answer.push(result); } return answer; } process?.on("message", async (request: CloudScriptRequest) => { let startTime = performance.now(); let startMemoryUsage = process.memoryUsage(); let logs: string[] = []; let functionRequest: FunctionRequest = { userId: request.userId, name: request.name, parameters: request.parameters, context: {}, customTags: request.customTags, log(log: any) { logs.push(JSON.stringify(log)); }, }; let handleResponse = await ___handleFunction(functionRequest); if (handleResponse.result) { if (handleResponse.result instanceof GNHashtable) { handleResponse.result = (<GNHashtable>handleResponse.result).toData(); } else if (handleResponse.result instanceof GNArray) { handleResponse.result = (<GNArray>handleResponse.result).toData(); } else { let typeofResult = typeof (handleResponse.result); if (typeofResult == "boolean") { } else if (typeofResult == "number") { } else if (typeofResult == "object") { if (Array.isArray(handleResponse.result)) { handleResponse.result = ConvertArray(handleResponse.result as any[]); //answer.push(this.deserializeArray(gnArray.getGNArray(i), cls)); } else { handleResponse.result = ConvertObject(handleResponse.result); } } else if (typeofResult == "string") { } else { handleResponse.result = null; handleResponse.errorMessage = "CloudScript must return void, number, string, boolean, null, json dictionary, json array, GNHashtable or GNArray"; handleResponse.status = ExecuteResponseStatus.Exception; } } } let endTime = performance.now(); let endMemoryUsage = process.memoryUsage(); let memoryUsedInBytes = endMemoryUsage.heapUsed - startMemoryUsage.heapUsed; if (memoryUsedInBytes <= 0) memoryUsedInBytes = 8192; let executeTimeInMs = endTime - startTime; //let cost = (memoryUsedInGb * executeTime) / 1000 * 0.022; let response: CloudScriptResponse = { responseId: request.requestId, response: handleResponse, stats: { executeTimeInMs: executeTimeInMs, memoryUsedInBytes: memoryUsedInBytes, }, logs: logs, }; if (process?.send) process.send(response); }); async function ___handleFunction(request: FunctionRequest): Promise<FunctionResponse> { let handler = handlers[request.name]; if (handler == null) { return { status: ExecuteResponseStatus.FunctionNameNotFound, result: null, errorMessage: null, }; } let answer: FunctionResponse = { status: ExecuteResponseStatus.Ok, errorMessage: null, result: null, }; try { answer.result = await handler(request.parameters, { userId: request.userId, customTags: request.customTags }, request.log); } catch (err: any) { answer.status = ExecuteResponseStatus.Exception; answer.errorMessage = err.message; } return answer; } const gameId: string = process.env.gameId ?? ""; { let gnServerSettingsOptions: GNServerSettingsOptions = JSON.parse(process.env.gnServerSettingsOptions ?? ""); let gnServerSettings = new GNServerSettings(); gnServerSettings.config(gnServerSettingsOptions); GNNetwork.init(gnServerSettings); GNNetwork.setGameId(gameId); } const admin = new CloudScriptAdmin(); const database = new CloudScriptDatabase(); const http = new CloudScriptHttp(); const socket = new CloudScriptSocket(); const mail = new CloudScriptMail(); const pushNotification = new CloudScriptPushNotification(); { let dbConnection = JSON.parse(process.env.dbConnection ?? ""); database.init(dbConnection.url, dbConnection.dbName, dbConnection.logUrl, dbConnection.logDbName, dbConnection.options); } process.env.dbConnection = ""; process.env.gnServerSettingsOptions = ""; const handlers: Record<string, (args: any, context: { userId: string; customTags: { [k: string]: any } }, log: (log: any) => void) => any> = {}; //$replaceScript