Skip to content

Commit

Permalink
feat: remove double save loading (skyrim-multiplayer#1741)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pospelove authored Nov 17, 2023
1 parent 53d05cc commit 4aec267
Show file tree
Hide file tree
Showing 54 changed files with 1,010 additions and 687 deletions.
1 change: 1 addition & 0 deletions docs/release/dev/sp-httpclient-callback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
HTTP Client API now supports callbacks, not only promises. This is useful since in SkyrimPlatform promises aren't resolving in the main menu.
1 change: 1 addition & 0 deletions docs/release/dev/sp-loadgame.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added `time` and `loadOrder` parameters to `loadGame` function.
1 change: 1 addition & 0 deletions docs/release/dev/sp-tick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added initial support for calling script functions in tick context: `Game.getModCount`, `Game.getModName`.
6 changes: 3 additions & 3 deletions savefile/src/SFStructure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,17 @@ int64_t SaveFile_::SaveFile::FindIndexInFormIdArray(uint32_t refID)
}

void SaveFile_::SaveFile::OverwritePluginInfo(
std::vector<std::string>& newPlaginNames)
std::vector<std::string>& newPluginNames)
{
uint32_t oldSize = this->pluginInfoSize;

this->pluginInfoSize = 1;
this->pluginInfo.numPlugins = 0;
this->pluginInfo.pluginsName.clear();

this->pluginInfo.numPlugins = uint8_t(newPlaginNames.size());
this->pluginInfo.numPlugins = static_cast<uint8_t>(newPluginNames.size());

for (auto& plugin : newPlaginNames) {
for (auto& plugin : newPluginNames) {
this->pluginInfo.pluginsName.push_back(plugin);
this->pluginInfoSize += uint32_t(2 + plugin.size());
}
Expand Down
205 changes: 92 additions & 113 deletions skymp5-client/src/features/authSystem.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import * as sp from "skyrimPlatform";
import * as browser from "./browser";
import { AuthGameData, RemoteAuthGameData } from "./authModel";
import { Transform } from "../sync/movement";
import { FunctionInfo } from "../lib/functionInfo";
import { SpApiInteractor } from "../services/spApiInteractor";
import { LoadGameService } from "../services/services/loadGameService";

const normalizeUrl = (url: string) => {
if (url.endsWith('/')) {
Expand Down Expand Up @@ -53,15 +50,15 @@ export const addAuthListener = (callback: AuthCallback): void => {
authListeners.push(callback);
}

export const main = (lobbyLocation: Transform): void => {
export const main = (): void => {
const settingsGameData = sp.settings["skymp5-client"]["gameData"] as any;
const isOfflineMode = Number.isInteger(settingsGameData?.profileId);
if (isOfflineMode) {
onAuthListeners({ local: { profileId: settingsGameData.profileId } });
} else {
startListenBrowserMessage();
browser.addOnWindowLoadListener(() => {
if (isListenBrowserMessage) loadLobby(lobbyLocation);
if (isListenBrowserMessage) loadLobby();
});
}
}
Expand All @@ -78,7 +75,7 @@ export const setPlayerAuthMode = (frozen: boolean): void => {
sp.Game.forceFirstPerson();
}

function createPlaySession(token: string) {
function createPlaySession(token: string, callback: (res: string, err: string) => void) {
const client = new sp.HttpClient(authUrl);
let masterKey = sp.settings["skymp5-client"]["server-master-key"];
if (!masterKey) {
Expand All @@ -88,17 +85,21 @@ function createPlaySession(token: string) {
masterKey = sp.settings["skymp5-client"]["server-ip"] + ":" + sp.settings["skymp5-client"]["server-port"];
}
sp.printConsole({ masterKey });
return client.post(`/api/users/me/play/${masterKey}`, {

client.post(`/api/users/me/play/${masterKey}`, {
body: '{}',
contentType: 'application/json',
headers: {
'authorization': token,
},
}).then((res) => {
}, (res: sp.HttpResponse) => {
if (res.status != 200) {
throw Error('status code ' + res.status);
callback('', 'status code ' + res.status);
}
else {
// TODO: handle JSON.parse failure?
callback(JSON.parse(res.body).session, '');
}
return JSON.parse(res.body).session;
});
}

Expand Down Expand Up @@ -146,86 +147,64 @@ const checkLoginState = () => {
if (!isListenBrowserMessage) {
return;
}

new sp.HttpClient(authUrl)
.get("/api/users/login-discord/status?state=" + discordAuthState)
.then(response => {
switch (response.status) {
case 200:
const {
token,
masterApiId,
discordUsername,
discordDiscriminator,
discordAvatar,
} = JSON.parse(response.body) as AuthStatus;
browserState.failCount = 0;
createPlaySession(token).then((playSession) => {
authData = {
session: playSession,
.get("/api/users/login-discord/status?state=" + discordAuthState, undefined,
(response) => {
switch (response.status) {
case 200:
const {
token,
masterApiId,
discordUsername,
discordDiscriminator,
discordAvatar,
};
refreshWidgets();
});
break;
case 401: // Unauthorized
browserState.failCount = 0;
browserState.comment = (`Still waiting...`);
setTimeout(() => checkLoginState(), 1.5 + Math.random() * 2);
break;
case 403: // Forbidden
case 404: // Not found
browserState.failCount = 9000;
browserState.comment = (`Fail: ${response.body}`);
break;
default:
++browserState.failCount;
browserState.comment = `Server returned ${response.status.toString() || "???"} "${response.body || response.error}"`;
setTimeout(() => checkLoginState(), 1.5 + Math.random() * 2);
}
})
.catch(reason => {
++browserState.failCount;
if (typeof reason === "string") {
browserState.comment = (`Skyrim platform error (http): ${reason}`)
} else {
browserState.comment = (`Skyrim platform error (http): request rejected`);
}
})
.finally(() => {
refreshWidgets();
});
} = JSON.parse(response.body) as AuthStatus;
browserState.failCount = 0;
createPlaySession(token, (playSession, error) => {
if (error) {
browserState.failCount = 0;
browserState.comment = (error);
setTimeout(() => checkLoginState(), 1.5 + Math.random() * 2);
refreshWidgets();
return;
}
authData = {
session: playSession,
masterApiId,
discordUsername,
discordDiscriminator,
discordAvatar,
};
refreshWidgets();
});
break;
case 401: // Unauthorized
browserState.failCount = 0;
browserState.comment = (`Still waiting...`);
setTimeout(() => checkLoginState(), 1.5 + Math.random() * 2);
break;
case 403: // Forbidden
case 404: // Not found
browserState.failCount = 9000;
browserState.comment = (`Fail: ${response.body}`);
break;
default:
++browserState.failCount;
browserState.comment = `Server returned ${response.status.toString() || "???"} "${response.body || response.error}"`;
setTimeout(() => checkLoginState(), 1.5 + Math.random() * 2);
}
});
};

const loadLobby = (location: Transform): void => {
sp.once("update", () => {
defaultAutoVanityModeDelay = sp.Utility.getINIFloat("fAutoVanityModeDelay:Camera");
setPlayerAuthMode(true);
authData = browser.getAuthData();
refreshWidgets();
sp.browser.setVisible(true);
});

sp.once("loadGame", () => {
// In non-offline mode we still want to see our face in RaceMenu
const ironHelment = sp.Armor.from(sp.Game.getFormEx(0x00012e4d));
const pl = sp.Game.getPlayer();
if (pl) pl.unequipItem(ironHelment, false, true);

sp.browser.setFocused(true);
browser.keepCursorMenuOpenedWhenBrowserFocused();
checkLoginState();
});
const loadLobby = (): void => {
authData = browser.getAuthData();
refreshWidgets();
sp.browser.setVisible(true);
sp.browser.setFocused(true);

const loadGameService = SpApiInteractor.makeController().lookupListener(LoadGameService);
loadGameService.loadGame(
location.pos,
location.rot,
location.worldOrCell
);
// Launch checkLoginState loop
checkLoginState();
}

declare const window: any;
Expand All @@ -234,54 +213,54 @@ const browsersideWidgetSetter = () => {
const loginWidget = {
type: "form",
id: 1,
caption: "authorization",
caption: "Авторизация",
elements: [
{
type: "button",
tags: ["BUTTON_STYLE_GITHUB"],
hint: "get a colored nickname and mention in news",
click: () => window.skyrimPlatform.sendMessage(events.openGithub),
},
{
type: "button",
tags: ["BUTTON_STYLE_PATREON", "ELEMENT_SAME_LINE", "HINT_STYLE_RIGHT"],
hint: "get a colored nickname and other bonuses for patrons",
click: () => window.skyrimPlatform.sendMessage(events.openPatreon),
},
{
type: "icon",
text: "username",
tags: ["ICON_STYLE_SKYMP"],
},
// {
// type: "button",
// tags: ["BUTTON_STYLE_GITHUB"],
// hint: "get a colored nickname and mention in news",
// click: () => window.skyrimPlatform.sendMessage(events.openGithub),
// },
// {
// type: "button",
// tags: ["BUTTON_STYLE_PATREON", "ELEMENT_SAME_LINE", "HINT_STYLE_RIGHT"],
// hint: "get a colored nickname and other bonuses for patrons",
// click: () => window.skyrimPlatform.sendMessage(events.openPatreon),
// },
// {
// type: "icon",
// text: "username",
// tags: ["ICON_STYLE_SKYMP"],
// },
{
type: "text",
text: (
authData ? (
authData.discordUsername
? `${authData.discordUsername}`
? `Добро пожаловать, ${authData.discordUsername}`
: `id: ${authData.masterApiId}`
) : "Please log in"
) : "Не авторизирован"
),
tags: ["ELEMENT_SAME_LINE", "ELEMENT_STYLE_MARGIN_EXTENDED"],
},
{
type: "icon",
text: "discord",
tags: ["ICON_STYLE_DISCORD"],
tags: [/*"ELEMENT_SAME_LINE", */"ELEMENT_STYLE_MARGIN_EXTENDED"],
},
// {
// type: "icon",
// text: "discord",
// tags: ["ICON_STYLE_DISCORD"],
// },
{
type: "button",
text: authData ? "change account" : "login or register",
tags: ["ELEMENT_SAME_LINE"],
text: authData ? "Сменить аккаунт" : "Войти через Discord",
tags: [/*"ELEMENT_SAME_LINE"*/],
click: () => window.skyrimPlatform.sendMessage(events.openDiscordOauth),
hint: "You can log in or change account at any time",
hint: "Вы можете войти или поменять аккаунт",
},
{
type: "button",
text: "travel to skyrim",
text: "Играть",
tags: ["BUTTON_STYLE_FRAME", "ELEMENT_STYLE_MARGIN_EXTENDED"],
click: () => window.skyrimPlatform.sendMessage(events.login),
hint: "Connect to the game server",
hint: "Подключиться к игровому серверу",
},
{
type: "text",
Expand Down
1 change: 0 additions & 1 deletion skymp5-client/src/features/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
once,
Input,
printConsole,
settings,
Menu,
DxScanCode,
writePlugin,
Expand Down
8 changes: 5 additions & 3 deletions skymp5-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ import { ActivationService } from "./services/services/activationService";
import { CraftService } from "./services/services/craftService";
import { DropItemService } from "./services/services/dropItemService";
import { HitService } from "./services/services/hitService";
import { SendMessagesService } from "./services/services/sendMessagesService";
import { RagdollService } from "./services/services/ragdollService";
import { DeathService } from "./services/services/deathService";
import { ContainersService } from "./services/services/containersService";
import { NetworkingService } from "./services/services/networkingService";
import { RemoteServer } from "./services/services/remoteServer";

browser.main();

Expand Down Expand Up @@ -107,10 +108,11 @@ const main = () => {
new CraftService(sp, controller),
new DropItemService(sp, controller),
new HitService(sp, controller),
new SendMessagesService(sp, controller),
new RagdollService(sp, controller),
new DeathService(sp, controller),
new ContainersService(sp, controller)
new ContainersService(sp, controller),
new NetworkingService(sp, controller),
new RemoteServer(sp, controller)
];
SpApiInteractor.setup(listeners);
listeners.forEach(listener => SpApiInteractor.registerListenerForLookup(listener.constructor.name, listener));
Expand Down
3 changes: 2 additions & 1 deletion skymp5-client/src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export class RespawnNeededError extends Error {

export class NeverError extends Error {
constructor(message: never) {
super(`Unreachable statement: ${message}`);
super(`NeverError: ${JSON.stringify(message)}`);
Object.setPrototypeOf(this, NeverError.prototype);
}
}
Loading

0 comments on commit 4aec267

Please sign in to comment.