diff --git a/packages/client-core/src/common/services/LocationInstanceConnectionService.ts b/packages/client-core/src/common/services/LocationInstanceConnectionService.ts index bb19b625ae..284e9ba798 100755 --- a/packages/client-core/src/common/services/LocationInstanceConnectionService.ts +++ b/packages/client-core/src/common/services/LocationInstanceConnectionService.ts @@ -36,7 +36,6 @@ import { instanceProvisionPath } from '@etherealengine/engine/src/schemas/networ import { InstanceID, instancePath, InstanceType } from '@etherealengine/engine/src/schemas/networking/instance.schema' import { SceneID } from '@etherealengine/engine/src/schemas/projects/scene.schema' import { LocationID, RoomCode } from '@etherealengine/engine/src/schemas/social/location.schema' -import { API } from '../../API' import { SocketWebRTCClientNetwork } from '../../transports/SocketWebRTCClientFunctions' import { AuthState } from '../../user/services/AuthService' @@ -80,7 +79,7 @@ export const LocationInstanceConnectionService = { logger.info({ locationId, instanceId, sceneId }, 'Provision World Server') const token = getState(AuthState).authUser.accessToken if (instanceId != null) { - const instance = (await API.instance.client.service(instancePath).find({ + const instance = (await Engine.instance.api.service(instancePath).find({ query: { id: instanceId, ended: false @@ -90,7 +89,7 @@ export const LocationInstanceConnectionService = { instanceId = null! } } - const provisionResult = await API.instance.client.service(instanceProvisionPath).find({ + const provisionResult = await Engine.instance.api.service(instanceProvisionPath).find({ query: { locationId, instanceId, @@ -120,7 +119,7 @@ export const LocationInstanceConnectionService = { provisionExistingServer: async (locationId: LocationID, instanceId: InstanceID, sceneId: SceneID) => { logger.info({ locationId, instanceId, sceneId }, 'Provision Existing World Server') const token = getState(AuthState).authUser.accessToken - const instance = (await API.instance.client.service(instancePath).find({ + const instance = (await Engine.instance.api.service(instancePath).find({ query: { id: instanceId, ended: false @@ -136,7 +135,7 @@ export const LocationInstanceConnectionService = { } return } - const provisionResult = await API.instance.client.service(instanceProvisionPath).find({ + const provisionResult = await Engine.instance.api.service(instanceProvisionPath).find({ query: { locationId, instanceId, @@ -161,7 +160,7 @@ export const LocationInstanceConnectionService = { provisionExistingServerByRoomCode: async (locationId: LocationID, roomCode: RoomCode, sceneId: SceneID) => { logger.info({ locationId, roomCode, sceneId }, 'Provision Existing World Server') const token = getState(AuthState).authUser.accessToken - const instance = (await API.instance.client.service(instancePath).find({ + const instance = (await Engine.instance.api.service(instancePath).find({ query: { roomCode, ended: false @@ -177,7 +176,7 @@ export const LocationInstanceConnectionService = { } return } - const provisionResult = await API.instance.client.service(instanceProvisionPath).find({ + const provisionResult = await Engine.instance.api.service(instanceProvisionPath).find({ query: { locationId, roomCode, diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index 5276777ca0..67b58a5f12 100755 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -307,7 +307,7 @@ export default defineConfig(async () => { ? 'dev-sw.js?dev-sw' : 'service-worker.js' : '', - paymentPointer: coilSetting.paymentPointer || '' + paymentPointer: coilSetting?.paymentPointer || '' }), viteCompression({ filter: /\.(js|mjs|json|css)$/i, diff --git a/packages/engine/src/assets/compression/ModelTransformFunctions.ts b/packages/engine/src/assets/compression/ModelTransformFunctions.ts index 82df839dbc..d72ec65e00 100644 --- a/packages/engine/src/assets/compression/ModelTransformFunctions.ts +++ b/packages/engine/src/assets/compression/ModelTransformFunctions.ts @@ -689,7 +689,7 @@ export async function transformModel(args: ModelTransformParameters) { { name: fileName, byteLength: data.byteLength, - + } }) )*/ diff --git a/packages/server-core/src/networking/instance-provision/instance-provision.class.ts b/packages/server-core/src/networking/instance-provision/instance-provision.class.ts index 6adc355149..f2af781f89 100755 --- a/packages/server-core/src/networking/instance-provision/instance-provision.class.ts +++ b/packages/server-core/src/networking/instance-provision/instance-provision.class.ts @@ -57,7 +57,6 @@ import getLocalServerIp from '../../util/get-local-server-ip' const releaseRegex = /^([a-zA-Z0-9]+)-/ const isNameRegex = /instanceserver-([a-zA-Z0-9]{5}-[a-zA-Z0-9]{5})/ -const pressureThresholdPercent = 0.8 /** * Gets an instanceserver that is not in use or reserved @@ -69,7 +68,8 @@ export async function getFreeInstanceserver({ channelId, roomCode, userId, - createPrivateRoom + createPrivateRoom, + provisionConstraints }: { app: Application iteration: number @@ -78,6 +78,7 @@ export async function getFreeInstanceserver({ roomCode?: RoomCode userId?: UserID createPrivateRoom?: boolean + provisionConstraints?: object }): Promise { await app.service(instancePath).remove(null, { query: { @@ -101,7 +102,8 @@ export async function getFreeInstanceserver({ channelId, roomCode, userId, - createPrivateRoom + createPrivateRoom, + provisionConstraints }) } logger.info('Getting free instanceserver') @@ -109,7 +111,47 @@ export async function getFreeInstanceserver({ const serverResult = await k8AgonesClient.listNamespacedCustomObject('agones.dev', 'v1', 'default', 'gameservers') const readyServers = _.filter((serverResult.body as any).items, (server: any) => { const releaseMatch = releaseRegex.exec(server.metadata.name) - return server.status.state === 'Ready' && releaseMatch != null && releaseMatch[1] === config.server.releaseName + let returned = server.status.state === 'Ready' + if (returned && !provisionConstraints) + returned = returned && releaseMatch != null && releaseMatch[1] === config.server.releaseName + if (returned && provisionConstraints) { + const keys = Object.keys(provisionConstraints) + for (let key of keys) { + const constraint = provisionConstraints[key] + const provisionFunction = Object.keys(constraint)[0] + const provisionValue = constraint[provisionFunction] + const provisionFieldSplit = key.split('.') + let serverField = server + for (const item of provisionFieldSplit) { + serverField = serverField[item] + if (!serverField) break + } + switch (provisionFunction) { + case 'lte': + returned = returned && parseFloat(serverField) <= parseFloat(provisionValue) + break + case 'lt': + returned = returned && parseFloat(serverField) < parseFloat(provisionValue) + break + case 'gte': + returned = returned && parseFloat(serverField) >= parseFloat(provisionValue) + break + case 'gt': + returned = returned && parseFloat(serverField) > parseFloat(provisionValue) + break + case 'eq': + returned = + returned && + (parseFloat(provisionValue) + ? parseFloat(serverField) === parseFloat(provisionValue) + : serverField === provisionValue) + break + default: + break + } + } + } + return returned }) const ipAddresses = readyServers.map((server) => `${server.status.address}:${server.status.ports[0].port}`) const assignedInstances: any = await app.service(instancePath).find({ @@ -147,7 +189,8 @@ export async function getFreeInstanceserver({ roomCode, userId, createPrivateRoom, - podName: pod.metadata.name + podName: pod.metadata.name, + provisionConstraints }) } @@ -160,7 +203,8 @@ export async function checkForDuplicatedAssignments({ roomCode, createPrivateRoom, userId, - podName + podName, + provisionConstraints }: { app: Application ipAddress: string @@ -171,6 +215,7 @@ export async function checkForDuplicatedAssignments({ createPrivateRoom?: boolean userId?: UserID podName?: string + provisionConstraints?: object }): Promise { /** since in local dev we can only have one instance server of each type at a time, we must force all old instances of this type to be ended */ if (!config.kubernetes.enabled) { @@ -251,7 +296,15 @@ export async function checkForDuplicatedAssignments({ await app.service(instancePath).remove(assignResult.id) //If this is the 10th or more attempt to get a free instanceserver, then there probably aren't any free ones, if (iteration < 10) { - return getFreeInstanceserver({ app, iteration: iteration + 1, locationId, channelId, roomCode, userId }) + return getFreeInstanceserver({ + app, + iteration: iteration + 1, + locationId, + channelId, + roomCode, + userId, + provisionConstraints + }) } else { logger.info('Made 10 attempts to get free instanceserver without success, returning null') return { @@ -374,7 +427,8 @@ export async function checkForDuplicatedAssignments({ channelId, roomCode, createPrivateRoom, - userId + userId, + provisionConstraints }) } @@ -413,6 +467,7 @@ export class InstanceProvisionService implements ServiceInterface { await this.app.service(instancePath).remove(null, { query: { @@ -437,12 +494,21 @@ export class InstanceProvisionService implements ServiceInterface { - return instance.currentUsers < pressureThresholdPercent * instance.location.maxUsersPerInstance - }) - const instances = nonPressuredInstances.length > 0 ? nonPressuredInstances : instanceUserSort - const instance = instances[0] + const nonFullInstances = availableLocationInstances.filter( + (instance) => instance.currentUsers < instance.location.maxUsersPerInstance + ) + if (nonFullInstances.length === 0) + return getFreeInstanceserver({ + app: this.app, + iteration: 0, + locationId, + channelId, + roomCode, + userId, + provisionConstraints + }) + const instanceUserSort = _.orderBy(nonFullInstances, ['currentUsers'], ['desc']) + const instance = instanceUserSort[0] if (!config.kubernetes.enabled) { logger.info('Resetting local instance to ' + instance.id) const localIp = await getLocalServerIp(channelId != null) @@ -455,14 +521,24 @@ export class InstanceProvisionService implements ServiceInterface 1) + if (instanceUserSort.length > 1) return this.getISInService({ availableLocationInstances: availableLocationInstances.slice(1), locationId, channelId, - roomCode + roomCode, + provisionConstraints + }) + else + return getFreeInstanceserver({ + app: this.app, + iteration: 0, + locationId, + channelId, + roomCode, + userId, + provisionConstraints }) - else return getFreeInstanceserver({ app: this.app, iteration: 0, locationId, channelId, roomCode, userId }) } logger.info('IS existed, using it %o', instance) const ipAddressSplit = instance.ipAddress!.split(':') @@ -538,6 +614,7 @@ export class InstanceProvisionService implements ServiceInterface if (channelInstance == null || channelInstance.data.length === 0) - return getFreeInstanceserver({ app: this.app, iteration: 0, channelId, roomCode, userId }) + return getFreeInstanceserver({ + app: this.app, + iteration: 0, + channelId, + roomCode, + userId, + provisionConstraints + }) else { if (config.kubernetes.enabled) { const isCleanup = await this.isCleanup(channelInstance.data[0]) - if (isCleanup) return getFreeInstanceserver({ app: this.app, iteration: 0, channelId, roomCode, userId }) + if (isCleanup) + return getFreeInstanceserver({ + app: this.app, + iteration: 0, + channelId, + roomCode, + userId, + provisionConstraints + }) } const actualInstance = channelInstance.data[0] const ipAddressSplit = actualInstance.ipAddress!.split(':') @@ -599,7 +691,15 @@ export class InstanceProvisionService implements ServiceInterface