diff --git a/game/experiment/GameContent.txt b/game/experiment/GameContent.txt
index 6657df4b63..516223a754 100644
--- a/game/experiment/GameContent.txt
+++ b/game/experiment/GameContent.txt
@@ -1,7 +1,8 @@
"GameContent"
{
- FileSystem
+ "filesystem"
{
- "AppId" 4000
+ "garrysmod" "4000"
+ "cstrike" "240"
}
-}
\ No newline at end of file
+}
diff --git a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua
index efd9e63826..2920e7c0b7 100644
--- a/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua
+++ b/game/experiment/addons/gmod_compatibility/scripts/lua/includes/modules/gmod_compatibility/sh_init.lua
@@ -190,7 +190,24 @@ PrecacheParticleSystem = ParticleSystem.Precache
engine.ActiveGamemode = function()
return Gamemodes.GetActiveName()
end
-engine.GetGames = function() return {} end
+engine.GetGames = function()
+ local games = Engine.GetMountableGames()
+ local result = {}
+
+ for i, game in ipairs(games) do
+ result[i] = {
+ depot = game.appId,
+ title = game.name,
+ folder = game.directoryName,
+
+ owned = game.isOwned,
+ mounted = game.isMounted,
+ installed = game.isInstalled,
+ }
+ end
+
+ return result
+end
engine.GetAddons = function() return {} end -- TODO: Implement with our addon system
engine.GetGamemodes = function() return {} end -- TODO: Implement with our gamemode system
engine.GetUserContent = function() return {} end
diff --git a/game/experiment/bin/game_shader_dx9.dll b/game/experiment/bin/game_shader_dx9.dll
index 5068910431..ff028d6974 100644
--- a/game/experiment/bin/game_shader_dx9.dll
+++ b/game/experiment/bin/game_shader_dx9.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1cea7541ec85a23eb1cece63394c9954302cb5699f2fc33ae58e4d0314d4bf47
+oid sha256:8c73839b88256ee25a34c2196c3b5b050b117ffc878a33d105b54049d29893ff
size 1041920
diff --git a/game/experiment/bin/game_shader_dx9.pdb b/game/experiment/bin/game_shader_dx9.pdb
index c806feacd7..58c859b931 100644
--- a/game/experiment/bin/game_shader_dx9.pdb
+++ b/game/experiment/bin/game_shader_dx9.pdb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5cdfe0862dd4021fe06fe2b6da0e7fd7371d00132e759036df80f1838a600699
+oid sha256:55e48d42b404c6c1f4f557cd18af582f1fa43ed6e7da43339cd61bcf0f82e9f2
size 4091904
diff --git a/game/experiment/bin/luasocket.dll b/game/experiment/bin/luasocket.dll
index 1da865462f..f3bd61eea8 100644
--- a/game/experiment/bin/luasocket.dll
+++ b/game/experiment/bin/luasocket.dll
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:63b59805dbfced6738aba04a170c3890acf47dd2ea6249ff3a0a83bd9aa30507
+oid sha256:2a9aae7ff1661a9bd28ed9a40564d78c53b64dbb0fa54a99bb86d1618781e4c8
size 581120
diff --git a/game/experiment/bin/luasocket.pdb b/game/experiment/bin/luasocket.pdb
index f5aabb83d3..9c5bfaef64 100644
--- a/game/experiment/bin/luasocket.pdb
+++ b/game/experiment/bin/luasocket.pdb
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b9207706b6b93125665df19285eaf047aa2db9216fed655b5984f6a26169bf91
+oid sha256:36c2309d537cb7a6cbb13936a66ecd321e3bf855cb7011f34502a2ff9bb84591
size 2469888
diff --git a/game/experiment/menus/images/game-icons/dod.png b/game/experiment/menus/images/game-icons/dod.png
new file mode 100644
index 0000000000..481513685b
Binary files /dev/null and b/game/experiment/menus/images/game-icons/dod.png differ
diff --git a/game/experiment/menus/images/game-icons/ep2.png b/game/experiment/menus/images/game-icons/ep2.png
new file mode 100644
index 0000000000..730dc26fc8
Binary files /dev/null and b/game/experiment/menus/images/game-icons/ep2.png differ
diff --git a/game/experiment/menus/images/game-icons/episodic.png b/game/experiment/menus/images/game-icons/episodic.png
new file mode 100644
index 0000000000..730dc26fc8
Binary files /dev/null and b/game/experiment/menus/images/game-icons/episodic.png differ
diff --git a/game/experiment/menus/images/game-icons/hl1mp.png b/game/experiment/menus/images/game-icons/hl1mp.png
new file mode 100644
index 0000000000..126298d24c
Binary files /dev/null and b/game/experiment/menus/images/game-icons/hl1mp.png differ
diff --git a/game/experiment/menus/images/game-icons/lostcoast.png b/game/experiment/menus/images/game-icons/lostcoast.png
new file mode 100644
index 0000000000..730dc26fc8
Binary files /dev/null and b/game/experiment/menus/images/game-icons/lostcoast.png differ
diff --git a/game/experiment/menus/images/game-icons/portal.png b/game/experiment/menus/images/game-icons/portal.png
new file mode 100644
index 0000000000..07d9910789
Binary files /dev/null and b/game/experiment/menus/images/game-icons/portal.png differ
diff --git a/game/experiment/menus/images/game-icons/portal2.png b/game/experiment/menus/images/game-icons/portal2.png
new file mode 100644
index 0000000000..ce32d19033
Binary files /dev/null and b/game/experiment/menus/images/game-icons/portal2.png differ
diff --git a/game/experiment/menus/images/game-icons/tf.png b/game/experiment/menus/images/game-icons/tf.png
new file mode 100644
index 0000000000..1c09bef7f8
Binary files /dev/null and b/game/experiment/menus/images/game-icons/tf.png differ
diff --git a/game/experiment/menus/init.js b/game/experiment/menus/init.js
index 96ac933e14..7f4fadbf2f 100644
--- a/game/experiment/menus/init.js
+++ b/game/experiment/menus/init.js
@@ -4,34 +4,68 @@
* on the window.
*/
document.addEventListener('DOMContentLoaded', function () {
- if (!navigator.userAgent.includes('Valve Client')) {
- window.g_Interop = window.g_Interop || {};
- window.g_Interop.__callbackQueue = window.g_Interop.__callbackQueue || [];
-
- window.g_Interop.__isReady = window.g_Interop.__isReady || false;
-
- if (!window.g_Interop.__isReady) {
- window.g_Interop.DispatchReadyEvent = function () {
- if (window.g_Interop.__isReady) return;
- window.g_Interop.__isReady = true;
- window.dispatchEvent(new Event('interop:ready'));
- };
-
- window.addEventListener('error', function (e) {
- //alert('%s' + JSON.stringify(e));
- });
-
- (function () {
- let oldLog = console.log;
- console.log = function () {
- oldLog.apply(console, arguments);
- //alert('%s' + JSON.stringify({ type: 'log', arguments: Array.prototype.slice.call(arguments) }));
- };
- })();
- }
-
- window.dispatchEvent(new Event('interop:installmock'));
-
- window.g_Interop.DispatchReadyEvent();
+ if (!navigator.userAgent.includes('Valve Client')) {
+ window.g_Interop = window.g_Interop || {};
+ window.g_Interop.__callbackQueue = window.g_Interop.__callbackQueue || [];
+
+ window.g_Interop.__isReady = window.g_Interop.__isReady || false;
+
+ if (!window.g_Interop.__isReady) {
+ window.g_Interop.DispatchReadyEvent = function () {
+ if (window.g_Interop.__isReady) return;
+ window.g_Interop.__isReady = true;
+ window.dispatchEvent(new Event('interop:ready'));
+ };
+
+ window.addEventListener('error', function (e) {
+ //alert('%s' + JSON.stringify(e));
+ });
+
+ (function () {
+ let oldLog = console.log;
+ console.log = function () {
+ oldLog.apply(console, arguments);
+ //alert('%s' + JSON.stringify({ type: 'log', arguments: Array.prototype.slice.call(arguments) }));
+ };
+ })();
}
+
+ window.dispatchEvent(new Event('interop:installmock'));
+
+ window.g_Interop.DispatchReadyEvent();
+ }
});
+
+let loadingOverlay;
+
+document.addEventListener('DOMContentLoaded', function () {
+ loadingOverlay = document.createElement('div');
+ loadingOverlay.classList.add('hidden', 'inset-0', 'fixed', 'bg-black', 'bg-opacity-50', 'flex', 'items-center', 'justify-center');
+ loadingOverlay.innerHTML = `
+
+ `;
+ document.body.appendChild(loadingOverlay);
+});
+
+function showLoadingOverlay(text, callback) {
+ const loadingText = loadingOverlay.querySelector('#loadingText');
+ loadingText.textContent = text || 'Loading...';
+
+ loadingOverlay.classList.remove('hidden');
+
+ const finish = () => {
+ hideLoadingOverlay();
+ }
+
+ callback(finish);
+}
+
+function hideLoadingOverlay() {
+ loadingOverlay.classList.add('hidden');
+}
diff --git a/game/experiment/menus/main.js b/game/experiment/menus/main.js
index 12f8a7044d..93240c62e1 100644
--- a/game/experiment/menus/main.js
+++ b/game/experiment/menus/main.js
@@ -5,11 +5,11 @@ const BACKGROUND_LEVEL = 3;
const BACKGROUND_DISCONNECTED = 4;
function SetBackgroundRenderState(state) {
- if (state === BACKGROUND_LEVEL) {
- document.body.classList.add('in-level');
- } else {
- document.body.classList.remove('in-level');
- }
+ if (state === BACKGROUND_LEVEL) {
+ document.body.classList.add('in-level');
+ } else {
+ document.body.classList.remove('in-level');
+ }
}
const pageElement = document.querySelector('#page');
@@ -23,228 +23,263 @@ const registeredPages = new Map();
let currentPage = null;
pageCloseElement.addEventListener('click', () => {
- if (currentPage) {
- currentPage.hide();
- currentPage = null;
- }
+ if (currentPage) {
+ currentPage.hide();
+ currentPage = null;
+ }
});
// Custom element for links
customElements.define('game-menu-link', class extends HTMLElement {
- constructor() {
- super();
+ constructor() {
+ super();
- this.addEventListener('click', this.onClick);
+ this.addEventListener('click', this.onClick);
- this.classList.add('group', 'flex', 'gap-2', 'items-center', 'hover:scale-125', 'cursor-pointer', 'transition');
+ this.classList.add('group', 'flex', 'gap-2', 'items-center', 'hover:scale-125', 'cursor-pointer', 'transition');
- if (this.hasAttribute('label-above')) {
- this.classList.add('flex-col-reverse');
- } else {
- this.classList.add('flex-col');
- }
-
- const key = this.getAttribute('translation');
- console.log(key); // TODO: request the menu for translations
- // this.textContent = menus.localize.getTranslation(key); // TODO: Set this on the span (not the element itself) or sub-links will break
+ if (this.hasAttribute('label-above')) {
+ this.classList.add('flex-col-reverse');
+ } else {
+ this.classList.add('flex-col');
}
- connectedCallback() {
- const sizeClassImage = this.hasAttribute('small') ? 'w-12 h-12' : 'w-24 h-24';
- const sizeClassText = this.hasAttribute('small') ? 'text-sm' : 'text-md';
- this.innerHTML = `
+ const key = this.getAttribute('translation');
+ console.log(key); // TODO: request the menu for translations
+ // this.textContent = menus.localize.getTranslation(key); // TODO: Set this on the span (not the element itself) or sub-links will break
+ }
+
+ connectedCallback() {
+ const sizeClassImage = this.hasAttribute('small') ? 'w-12 h-12' : 'w-24 h-24';
+ const sizeClassText = this.hasAttribute('small') ? 'text-sm' : 'text-md';
+ this.innerHTML = `
${this.textContent}
`;
- }
+ }
- onClick(event) {
- event.preventDefault();
+ onClick(event) {
+ event.preventDefault();
- const href = this.getAttribute('href');
+ const href = this.getAttribute('href');
- if (href) {
- if (currentPage) {
- currentPage.hide();
- }
+ if (href) {
+ if (currentPage) {
+ currentPage.hide();
+ }
- window.location.href = href;
- return;
- }
+ window.location.href = href;
+ return;
+ }
- const pageRef = this.getAttribute('pageref');
+ const pageRef = this.getAttribute('pageref');
- if (pageRef) {
- const registeredPage = registeredPages.get(pageRef);
+ if (pageRef) {
+ const registeredPage = registeredPages.get(pageRef);
- if (registeredPage.isShowing()) {
- registeredPage.hide();
- return;
- }
+ if (registeredPage.isShowing()) {
+ registeredPage.hide();
+ return;
+ }
- if (currentPage) {
- currentPage.hide();
- }
+ if (currentPage) {
+ currentPage.hide();
+ }
- registeredPage.show();
- currentPage = registeredPage;
- return;
- }
+ registeredPage.show();
+ currentPage = registeredPage;
+ return;
}
+ }
});
// Custom element for pages (invisible until selected)
customElements.define('game-page', class extends HTMLElement {
- _lastShowHandler = 0;
- _isShowing = false;
+ _lastShowHandler = 0;
+ _isShowing = false;
- constructor() {
- super();
+ constructor() {
+ super();
- this.classList.add('hidden');
- }
-
- connectedCallback() {
- registeredPages.set(this.id, this);
- }
+ this.classList.add('hidden');
+ }
- show() {
- this._isShowing = true;
- this._lastShowHandler++;
+ connectedCallback() {
+ registeredPages.set(this.id, this);
+ }
- pageTitleElement.textContent = this.getAttribute('title');
- // pageContentElement.innerHTML = this.innerHTML; // This messes up any values set (e.g: checked on checkboxes)
- // Lets teleport the children instead
- while (this.firstChild) {
- pageContentElement.appendChild(this.firstChild);
- }
+ show() {
+ this._isShowing = true;
+ this._lastShowHandler++;
- pageElement.classList.remove('opacity-0');
+ pageTitleElement.textContent = this.getAttribute('title');
+ // pageContentElement.innerHTML = this.innerHTML; // This messes up any values set (e.g: checked on checkboxes)
+ // Lets teleport the children instead
+ while (this.firstChild) {
+ pageContentElement.appendChild(this.firstChild);
}
- isShowing() {
- return this._isShowing;
- }
+ pageElement.classList.remove('opacity-0');
+ }
- hide() {
- this._isShowing = false;
+ isShowing() {
+ return this._isShowing;
+ }
- const showHandler = this._lastShowHandler;
+ hide() {
+ this._isShowing = false;
- pageElement.classList.add('opacity-0');
+ const showHandler = this._lastShowHandler;
- // Move the children back after the animation
- setTimeout(() => {
- // Don't move them if the show handler changed, meaning the page was shown again
- // (happens when spam clicking)
- if (showHandler !== this._lastShowHandler) {
- return;
- }
+ pageElement.classList.add('opacity-0');
- while (pageContentElement.firstChild) {
- this.appendChild(pageContentElement.firstChild);
- }
- }, 500);
- }
+ // Move the children back after the animation
+ setTimeout(() => {
+ // Don't move them if the show handler changed, meaning the page was shown again
+ // (happens when spam clicking)
+ if (showHandler !== this._lastShowHandler) {
+ return;
+ }
+
+ while (pageContentElement.firstChild) {
+ this.appendChild(pageContentElement.firstChild);
+ }
+ }, 500);
+ }
});
// Source: https://codepen.io/marcusparsons/pen/NMyzgR
function makeDraggable(element) {
- let currentPosX = 0, currentPosY = 0, previousPosX = 0, previousPosY = 0;
- element.querySelector('[x-draggable-handle]').onmousedown = dragMouseDown;
-
- function dragMouseDown(e) {
- e.preventDefault();
- // Get the mouse cursor position and set the initial previous positions to begin
- previousPosX = e.clientX;
- previousPosY = e.clientY;
- // When the mouse is let go, call the closing event
- document.onmouseup = closeDragElement;
- // call a function whenever the cursor moves
- document.onmousemove = elementDrag;
- }
-
- function elementDrag(e) {
- e.preventDefault();
- // Calculate the new cursor position by using the previous x and y positions of the mouse
- currentPosX = previousPosX - e.clientX;
- currentPosY = previousPosY - e.clientY;
- // Replace the previous positions with the new x and y positions of the mouse
- previousPosX = e.clientX;
- previousPosY = e.clientY;
- // Set the element's new position
- element.style.top = (element.offsetTop - currentPosY) + 'px';
- element.style.left = (element.offsetLeft - currentPosX) + 'px';
- }
-
- function closeDragElement() {
- document.onmouseup = null;
- document.onmousemove = null;
- }
+ let currentPosX = 0, currentPosY = 0, previousPosX = 0, previousPosY = 0;
+ element.querySelector('[x-draggable-handle]').onmousedown = dragMouseDown;
+
+ function dragMouseDown(e) {
+ e.preventDefault();
+ // Get the mouse cursor position and set the initial previous positions to begin
+ previousPosX = e.clientX;
+ previousPosY = e.clientY;
+ // When the mouse is let go, call the closing event
+ document.onmouseup = closeDragElement;
+ // call a function whenever the cursor moves
+ document.onmousemove = elementDrag;
+ }
+
+ function elementDrag(e) {
+ e.preventDefault();
+ // Calculate the new cursor position by using the previous x and y positions of the mouse
+ currentPosX = previousPosX - e.clientX;
+ currentPosY = previousPosY - e.clientY;
+ // Replace the previous positions with the new x and y positions of the mouse
+ previousPosX = e.clientX;
+ previousPosY = e.clientY;
+ // Set the element's new position
+ element.style.top = (element.offsetTop - currentPosY) + 'px';
+ element.style.left = (element.offsetLeft - currentPosX) + 'px';
+ }
+
+ function closeDragElement() {
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
}
document.querySelectorAll('[x-draggable]').forEach(makeDraggable);
+/**
+ * Loads the mountable content info from the game, populating the list
+ * and setting up the event listeners for mounting and unmounting content.
+ */
function initialize() {
- GameUI.LoadMountableContentInfo(function (registeredMountableContent) {
- registeredMountableContent.forEach((content, index) => {
- const contentItemElement = contentItemTemplateElement.content.firstElementChild.cloneNode(true);
-
- contentItemElement.classList.add(index % 2 === 0 ? 'bg-white/10' : 'bg-white/5');
+ GameUI.LoadMountableContentInfo(function (registeredMountableContent) {
+ // sort it by name first
+ registeredMountableContent.sort((a, b) => a.name.localeCompare(b.name));
+ registeredMountableContent.forEach((content, index) => {
+ const contentItemElement = contentItemTemplateElement.content.firstElementChild.cloneNode(true);
+
+ contentItemElement.classList.add(index % 2 === 0 ? 'bg-white/10' : 'bg-white/5');
+
+ const iconElement = contentItemElement.querySelector('img');
+ iconElement.src = content.icon;
+ iconElement.alt = content.name;
+
+ const nameElement = contentItemElement.querySelector('span');
+ nameElement.textContent = content.name;
+
+ const inputElement = contentItemElement.querySelector('input');
+ inputElement.value = content.id;
+ inputElement.checked = content.mounted;
+
+ inputElement.addEventListener('change', () => {
+ showLoadingOverlay('Mounting content...', function (finish) {
+ if (inputElement.checked) {
+ GameUI.MountGameContent(inputElement.value, function (wasSuccessful) {
+ if (!wasSuccessful) {
+ inputElement.checked = false;
+ }
+
+ finish();
+ });
+ } else {
+ GameUI.UnmountGameContent(inputElement.value, function (wasSuccessful) {
+ if (!wasSuccessful) {
+ inputElement.checked = true;
+ }
- const iconElement = contentItemElement.querySelector('img');
- iconElement.src = content.icon;
- iconElement.alt = content.name;
+ finish();
+ });
+ }
+ });
+ });
- const nameElement = contentItemElement.querySelector('span');
- nameElement.textContent = content.name;
+ contentItemElement.addEventListener('click', (ev) => {
+ // Don't toggle the checkbox if the click was on the checkbox itself
+ // otherwise we immediately toggle it back
+ if (ev.target.tagName === 'INPUT') {
+ return;
+ }
- const inputElement = contentItemElement.querySelector('input');
- inputElement.value = content.id;
- inputElement.checked = content.mounted;
+ inputElement.checked = !inputElement.checked;
+ inputElement.dispatchEvent(new Event('change'));
+ });
- contentItemElement.addEventListener('click', () => {
- inputElement.checked = !inputElement.checked;
- });
-
- contentList.appendChild(contentItemElement);
- });
+ contentList.appendChild(contentItemElement);
});
+ });
}
window.addEventListener('interop:ready', () => {
- // Intro effect when the page is loaded
- document.body.classList.add('loaded');
+ // Intro effect when the page is loaded
+ document.body.classList.add('loaded');
- initialize();
+ initialize();
});
// When working in the browser we want to mock the GameUI API
window.addEventListener('interop:installmock', () => {
- window.GameUI = {
- LoadMountableContentInfo: function (callback) {
- callback([
- {
- id: 'gmod',
- name: 'Garry\'s Mod',
- mounted: true,
- icon: './images/game-icons/garrysmod.png',
- },
- {
- id: 'cstrike',
- name: 'Counter-Strike: Source',
- mounted: false,
- icon: './images/game-icons/cstrike.png',
- },
- {
- id: 'hl2',
- name: 'Dev Note: this mountable game data is mocked',
- mounted: false,
- icon: './images/game-icons/hl2.png',
- },
- ]);
+ window.GameUI = {
+ LoadMountableContentInfo: function (callback) {
+ callback([
+ {
+ id: 4000,
+ name: 'Garry\'s Mod',
+ mounted: true,
+ icon: './images/game-icons/garrysmod.png',
+ },
+ {
+ id: 240,
+ name: 'Counter-Strike: Source',
+ mounted: false,
+ icon: './images/game-icons/cstrike.png',
+ },
+ {
+ id: 220,
+ name: 'Dev Note: this mountable game data is mocked',
+ mounted: false,
+ icon: './images/game-icons/hl2.png',
},
- };
+ ]);
+ },
+ };
});
diff --git a/src/game/client/cdll_client_int.cpp b/src/game/client/cdll_client_int.cpp
index d862d1c638..fd9bf3a8dd 100644
--- a/src/game/client/cdll_client_int.cpp
+++ b/src/game/client/cdll_client_int.cpp
@@ -1065,7 +1065,7 @@ int CHLClient::Init( CreateInterfaceFn appSystemFactory,
#if defined( EXPERIMENT_SOURCE )
// Andrew; then mount everything the user wants to use.
- MountUserContent();
+ InitializeGameContentMounting();
// Finally, load all of the player's addons.
MountAddons();
diff --git a/src/game/client/client_base.vpc b/src/game/client/client_base.vpc
index a7a4285aae..2ba8316d1d 100644
--- a/src/game/client/client_base.vpc
+++ b/src/game/client/client_base.vpc
@@ -544,6 +544,7 @@ $Project
$File "lc_baseentity.cpp"
$File "lc_baseplayer.cpp"
$File "$SRCDIR\public\lcdll_int.cpp"
+ $File "$SRCDIR\public\lengine_shared.cpp"
$File "lcdll_util.cpp"
$File "$SRCDIR\public\lColor.cpp"
$File "$SRCDIR\public\tier1\lconvar.cpp"
diff --git a/src/game/client/experiment/ui/mainmenu.cpp b/src/game/client/experiment/ui/mainmenu.cpp
index 2c1b32c991..7157d95562 100644
--- a/src/game/client/experiment/ui/mainmenu.cpp
+++ b/src/game/client/experiment/ui/mainmenu.cpp
@@ -12,6 +12,7 @@
#include
#include "GameUI/IGameConsole.h"
#include "vgui/IInput.h"
+#include
using namespace vgui;
@@ -170,6 +171,8 @@ class MainMenuHTML : public HTML
{
AddJavascriptObject( "GameUI" );
AddJavascriptObjectCallback( "GameUI", "LoadMountableContentInfo" );
+ AddJavascriptObjectCallback( "GameUI", "MountGameContent" );
+ AddJavascriptObjectCallback( "GameUI", "UnmountGameContent" );
}
virtual void OnJavaScriptCallback( KeyValues *pData ) OVERRIDE
@@ -184,29 +187,52 @@ class MainMenuHTML : public HTML
// DevMsg( "Argument: %s (inside %s.%s call)\n", pArg->GetName(), pszObject, pszProperty );
// }
- if ( Q_strcmp( pszObject, "GameUI" ) == 0 )
+ // This panel only handles GameUI callbacks
+ if ( Q_strcmp( pszObject, "GameUI" ) != 0 )
+ return;
+
+ if ( Q_strcmp( pszProperty, "LoadMountableContentInfo" ) == 0 )
{
- if ( Q_strcmp( pszProperty, "LoadMountableContentInfo" ) == 0 )
+ KeyValues *pParameters = new KeyValues( "parameters" );
+ CUtlVector< mountableGame_t > &mountableGames = GetMountableGames();
+
+ KeyValues *pMountableGames = new KeyValues( "mountableGames" );
+ pParameters->AddSubKey( pMountableGames );
+
+ for ( int i = 0; i < mountableGames.Count(); i++ )
{
- // TODO: Load this automatically:
- KeyValues *pResponse = new KeyValues( "response" );
-
- KeyValues *pGarrysMod = new KeyValues( "" );
- pGarrysMod->SetString( "icon", "images/game-icons/garrysmod.png" );
- pGarrysMod->SetString( "name", "Garry's Mod" );
- pGarrysMod->SetString( "id", "garrysmod" );
- pGarrysMod->SetBool( "mounted", true );
- pResponse->AddSubKey( pGarrysMod );
-
- KeyValues *pCounterStrike = new KeyValues( "" );
- pCounterStrike->SetString( "icon", "images/game-icons/cstrike.png" );
- pCounterStrike->SetString( "name", "Counter-Strike: Source" );
- pCounterStrike->SetString( "id", "cstrike" );
- pCounterStrike->SetBool( "mounted", false );
- pResponse->AddSubKey( pCounterStrike );
-
- CallJavascriptObjectCallback( callbackId, pResponse );
+ mountableGame_t &game = mountableGames[i];
+
+ char icon[MAX_PATH];
+ Q_snprintf( icon, sizeof( icon ), "images/game-icons/%s.png", game.directoryName );
+
+ KeyValues *pGame = new KeyValues( "" );
+ pGame->SetString( "icon", icon );
+ pGame->SetString( "name", game.name );
+ pGame->SetInt( "id", game.appId );
+ pGame->SetBool( "mounted", game.isMounted );
+ pMountableGames->AddSubKey( pGame );
}
+
+ CallJavascriptObjectCallback( callbackId, pParameters );
+ }
+ else if ( Q_strcmp( pszProperty, "MountGameContent" ) == 0 )
+ {
+ KeyValues *pArguments = pData->FindKey( "arguments" );
+ int nGameAppId = pArguments->GetInt( "1" );
+
+ KeyValues *pResponse = new KeyValues( "parameters" );
+ pResponse->SetBool( "wasSuccessful", MountGameContentByAppId( nGameAppId ) );
+ CallJavascriptObjectCallback( callbackId, pResponse );
+ }
+ else if ( Q_strcmp( pszProperty, "UnmountGameContent" ) == 0 )
+ {
+ KeyValues *pArguments = pData->FindKey( "arguments" );
+ int nGameAppId = pArguments->GetInt( "1" );
+
+ KeyValues *pResponse = new KeyValues( "parameters" );
+ pResponse->SetBool( "wasSuccessful", UnmountGameContentByAppId( nGameAppId ) );
+ CallJavascriptObjectCallback( callbackId, pResponse );
}
}
};
diff --git a/src/game/client/scripted_controls/lHTML.cpp b/src/game/client/scripted_controls/lHTML.cpp
index 6d81dcc77e..51cef89d11 100644
--- a/src/game/client/scripted_controls/lHTML.cpp
+++ b/src/game/client/scripted_controls/lHTML.cpp
@@ -44,7 +44,9 @@ void LHTML::OnJavaScriptCallback( KeyValues *pData )
// Push all arguments in 'arguments' as a table
lua_newtable( L );
- for ( KeyValues *pArg = pData->FindKey( "arguments" ); pArg; pArg = pArg->GetNextKey() )
+ KeyValues *pArguments = pData->FindKey( "arguments" );
+
+ for ( KeyValues *pArg = pArguments->GetFirstSubKey(); pArg; pArg = pArg->GetNextKey() )
{
lua_pushstring( L, pArg->GetName() );
lua_pushstring( L, pArg->GetString() );
diff --git a/src/game/server/gameinterface.cpp b/src/game/server/gameinterface.cpp
index 4deafe0882..f20f80d661 100644
--- a/src/game/server/gameinterface.cpp
+++ b/src/game/server/gameinterface.cpp
@@ -698,7 +698,7 @@ bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory,
#if defined( EXPERIMENT_SOURCE )
// Andrew; then mount everything the user wants to use.
- MountUserContent();
+ InitializeGameContentMounting();
// Finally, load all of the player's addons.
MountAddons();
diff --git a/src/game/server/server_base.vpc b/src/game/server/server_base.vpc
index 935685ddae..9ff0e3f40a 100644
--- a/src/game/server/server_base.vpc
+++ b/src/game/server/server_base.vpc
@@ -714,7 +714,8 @@ $Project
$File "$SRCDIR\public\tier0\ldbg.cpp"
$File "$SRCDIR\game\shared\leffect_dispatch_data.cpp"
$File "$SRCDIR\game\shared\lentitylist.cpp"
- $File "$SRCDIR\public\leiface.cpp"
+ $File "$SRCDIR\public\lengine.cpp"
+ $File "$SRCDIR\public\lengine_shared.cpp"
$File "$SRCDIR\public\lfilesystem.cpp"
$File "$SRCDIR\public\lgametrace.cpp"
$File "$SRCDIR\public\lglobalvars_base.cpp"
diff --git a/src/game/shared/experiment/util/jsontokv.cpp b/src/game/shared/experiment/util/jsontokv.cpp
index 2c2ef8fc72..72124fbe6c 100644
--- a/src/game/shared/experiment/util/jsontokv.cpp
+++ b/src/game/shared/experiment/util/jsontokv.cpp
@@ -41,9 +41,31 @@ inline void MapNode( Value::ConstMemberIterator itr, KeyValues *kv )
pKv->SetName( itr->name.GetString() );
for ( Value::ConstValueIterator arrItr = itr->value.Begin(); arrItr != itr->value.End(); ++arrItr )
{
- // We can only support arrays of objects... for now
if ( arrItr->GetType() == kObjectType )
IterObject( nullptr, arrItr->GetObject(), pKv );
+ else
+ {
+ char keyName[12];
+ pKv->GetNextKeyName( keyName, sizeof( keyName ) );
+
+ if ( arrItr->GetType() == kNumberType )
+ {
+ if ( arrItr->IsInt() )
+ pKv->SetInt( keyName, arrItr->GetInt() );
+ else if ( arrItr->IsUint64() )
+ pKv->SetUint64( keyName, arrItr->GetUint64() );
+ else if ( arrItr->IsLosslessFloat() )
+ pKv->SetFloat( keyName, arrItr->GetFloat() );
+ else // Default to float
+ pKv->SetFloat( keyName, arrItr->GetDouble() );
+ }
+ else if ( arrItr->GetType() == kStringType )
+ pKv->SetString( keyName, arrItr->GetString() );
+ else if ( arrItr->GetType() == kTrueType || arrItr->GetType() == kFalseType )
+ pKv->SetBool( keyName, arrItr->GetType() == kTrueType );
+ else
+ DevWarning( "Unsupported data type in JSON array %i!\n", arrItr->GetType() );
+ }
// MOM_TODO: theoretically any array can work, it's just the keys would be disregarded, like the array of objects are
// Consider creating garbage keys for used values, and iterating over only values of the array.
// The thing to keep in mind is that whoever is reading the array would know what data type it should be...
@@ -111,7 +133,7 @@ inline void WriteObject(KeyValues* kv, PrettyWriter< StringBuffer >& writer)
inline void WriteObjectOrArray( KeyValues *kv, PrettyWriter< StringBuffer > &writer )
{
- const char *pName = kv->GetFirstSubKey()->GetName();
+ const char *pName = kv->GetFirstSubKey() ? kv->GetFirstSubKey()->GetName() : nullptr;
const bool bIsObject = pName && *pName;
if ( bIsObject )
diff --git a/src/game/shared/lsrcinit.cpp b/src/game/shared/lsrcinit.cpp
index 236212a206..305ee616ec 100644
--- a/src/game/shared/lsrcinit.cpp
+++ b/src/game/shared/lsrcinit.cpp
@@ -51,6 +51,7 @@ static const luaL_RegForState luasrclibs[] = {
{ LUA_EFFECTDATALIBNAME, luaopen_CEffectData, REALM_SHARED },
{ LUA_EFLIBNAME, luaopen_EF, REALM_SHARED | REALM_GAMEUI },
{ LUA_ENGINEFLAGSLIBNAME, luaopen_FL, REALM_SHARED | REALM_GAMEUI },
+ { LUA_ENGINELIBNAME, luaopen_engine_shared, REALM_SHARED | REALM_GAMEUI },
{ LUA_ENGINELIBNAME, luaopen_engine, REALM_SHARED | REALM_GAMEUI },
{ LUA_ENTLISTLIBNAME, luaopen_EntityList, REALM_SHARED },
{ LUA_EXPERIMENTPLAYERLIBNAME, luaopen_CExperiment_Player_shared, REALM_SHARED },
diff --git a/src/game/shared/luasrclib.h b/src/game/shared/luasrclib.h
index 3a42e7497f..87e99291bf 100644
--- a/src/game/shared/luasrclib.h
+++ b/src/game/shared/luasrclib.h
@@ -75,6 +75,7 @@ LUALIB_API int( luaopen_debugoverlay )( lua_State *L );
LUALIB_API int( luaopen_EF )( lua_State *L );
#define LUA_ENGINELIBNAME "Engine"
+LUALIB_API int( luaopen_engine_shared )( lua_State *L );
LUALIB_API int( luaopen_engine )( lua_State *L );
#define LUA_ENTLISTLIBNAME "EntityList"
diff --git a/src/game/shared/mountaddons.cpp b/src/game/shared/mountaddons.cpp
index 62d04063d5..a72e73001d 100644
--- a/src/game/shared/mountaddons.cpp
+++ b/src/game/shared/mountaddons.cpp
@@ -39,7 +39,7 @@ void MountAddon( const char *addonName )
if ( bGetCurrentDirectory )
V_SetCurrentDirectory( currentDirectoryPath );
-#ifdef GAME_DLL
+#ifdef GAME_DLL // Prevent the message from printing twice in clients
ConColorMsg( Color( 200, 255, 0, 255 ), "Mounting addon \"%s\"...\n", addonName );
#endif
diff --git a/src/game/shared/mountsteamcontent.cpp b/src/game/shared/mountsteamcontent.cpp
index 0b40419dc6..915efe6b04 100644
--- a/src/game/shared/mountsteamcontent.cpp
+++ b/src/game/shared/mountsteamcontent.cpp
@@ -13,38 +13,14 @@
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
-struct mountableGame_t
-{
- const char *name;
- int appId;
- CUtlVector< const char * > vpks;
-
- mountableGame_t( const char *gameName, int gameId )
- : name( gameName ), appId( gameId ) {}
-
- mountableGame_t( const mountableGame_t &other )
- : name( other.name ), appId( other.appId )
- {
- for ( int i = 0; i < other.vpks.Count(); i++ )
- {
- vpks.AddToTail( other.vpks[i] );
- }
- }
-
- void AddVPK( const char *vpkPath )
- {
- vpks.AddToTail( vpkPath );
- }
-};
-
static CUtlVector< mountableGame_t > g_GamePaths;
-#define REGISTER_GAME( game_name, game_id ) \
- g_GamePaths.AddToTail( mountableGame_t( game_name, game_id ) );
+#define REGISTER_GAME( pSteamApps, GameName, GameDirectory, GameAppId ) \
+ g_GamePaths.AddToTail( mountableGame_t( GameName, GameDirectory, GameAppId, pSteamApps->BIsAppInstalled( GameAppId ), pSteamApps->BIsSubscribedApp( GameAppId ) ) );
-#define REGISTER_GAME_WITH_VPK( game_name, game_id, ... ) \
+#define REGISTER_GAME_WITH_VPK( pSteamApps, GameName, GameDirectory, GameAppId, ... ) \
{ \
- mountableGame_t game( game_name, game_id ); \
+ mountableGame_t game( GameName, GameDirectory, GameAppId, pSteamApps->BIsAppInstalled( GameAppId ), pSteamApps->BIsSubscribedApp( GameAppId ) ); \
const char *vpkPaths[] = { __VA_ARGS__ }; \
for ( int i = 0; i < ARRAYSIZE( vpkPaths ); i++ ) \
{ \
@@ -73,17 +49,17 @@ void AddSearchPathByAppId( int nAppId )
char fullPath[MAX_PATH];
// Commented because it's wrong anyway. g_GamePaths[2] always pointed to cstrike :/
- //if ( g_GamePaths[i].appId == HL1MP_APPID )
+ // if ( g_GamePaths[i].appId == HL1MP_APPID )
//{
// // Andrew; Half-Life Deathmatch: Source requires Half-Life: Source's path added!!
- // Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[2].name );
+ // Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[2].directoryName );
// filesystem->AddSearchPath( fullPath, "GAME", PATH_ADD_TO_TAIL );
//}
if ( g_GamePaths[i].appId != nAppId )
continue;
- Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[i].name );
+ Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[i].directoryName );
filesystem->AddSearchPath( fullPath, "GAME", PATH_ADD_TO_TAIL );
auto &vpkPaths = g_GamePaths[i].vpks;
@@ -93,23 +69,89 @@ void AddSearchPathByAppId( int nAppId )
Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, vpkPaths[j] );
filesystem->AddSearchPath( fullPath, "GAME", PATH_ADD_TO_TAIL );
}
+
+ g_GamePaths[i].isMounted = true;
+
+#ifdef CLIENT_DLL // Prevent the message from printing twice in clients
+ ConColorMsg( Color( 200, 255, 0, 255 ), "Mounting game content \"%s\"...\n", g_GamePaths[i].name );
+#endif
+
+ // We've found the game, no need to continue searching.
+ break;
}
}
-void MountUserContent()
+void RemoveSearchPathByAppId( int nAppId )
{
- REGISTER_GAME( "hl2", 220 );
- REGISTER_GAME( "cstrike", 240 );
- REGISTER_GAME( "hl1", 280 );
- REGISTER_GAME( "dod", 300 );
- REGISTER_GAME( "lostcoast", 340 );
- REGISTER_GAME( "hl1mp", HL1MP_APPID );
- REGISTER_GAME( "episodic", 380 );
- REGISTER_GAME( "portal", 400 );
- REGISTER_GAME( "ep2", 420 );
- REGISTER_GAME( "tf", 440 );
-
- REGISTER_GAME_WITH_VPK( "garrysmod", 4000, "garrysmod/fallback_dir.vpk", "garrysmod/garrysmod_dir.vpk" );
+ ISteamApps *pSteamApps = steamapicontext->SteamApps();
+
+ if ( !pSteamApps->BIsAppInstalled( nAppId ) )
+ {
+ Warning( "Failed to unmount content for App ID %i (not installed)\n", nAppId );
+ return;
+ }
+
+ char appInstallPath[MAX_PATH * 2];
+ pSteamApps->GetAppInstallDir( nAppId, appInstallPath, sizeof( appInstallPath ) );
+
+ for ( int i = 0; i < g_GamePaths.Count(); i++ )
+ {
+ char fullPath[MAX_PATH];
+
+ if ( g_GamePaths[i].appId != nAppId )
+ continue;
+
+ Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[i].directoryName );
+ filesystem->RemoveSearchPath( fullPath );
+
+ auto &vpkPaths = g_GamePaths[i].vpks;
+
+ for ( int j = 0; j < vpkPaths.Count(); j++ )
+ {
+ Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, vpkPaths[j] );
+ filesystem->RemoveSearchPath( fullPath );
+ }
+
+ g_GamePaths[i].isMounted = false;
+
+#ifdef CLIENT_DLL // Prevent the message from printing twice in clients
+ ConColorMsg( Color( 200, 255, 0, 255 ), "Unmounting game content \"%s\"...\n", g_GamePaths[i].name );
+#endif
+
+ // We've found the game, no need to continue searching.
+ break;
+ }
+
+ // Andrew; Half-Life Deathmatch: Source requires Half-Life: Source's path added!!
+ // if ( nAppId == HL1MP_APPID )
+ //{
+ // Q_snprintf( fullPath, sizeof( fullPath ), "%s/%s", appInstallPath, g_GamePaths[2].directoryName );
+ // filesystem->RemoveSearchPath( fullPath );
+ //}
+}
+
+CUtlVector< mountableGame_t > &GetMountableGames()
+{
+ return g_GamePaths;
+}
+
+void InitializeGameContentMounting()
+{
+ ISteamApps *pSteamApps = steamapicontext->SteamApps();
+
+ REGISTER_GAME( pSteamApps, "Half-Life 2", "hl2", 220 );
+ REGISTER_GAME( pSteamApps, "Half-Life", "hl1", 280 );
+
+ REGISTER_GAME( pSteamApps, "Day of Defeat: Source", "dod", 300 );
+ REGISTER_GAME( pSteamApps, "Half-Life 2: Lost Coast", "lostcoast", 340 );
+ REGISTER_GAME( pSteamApps, "Half-Life Deathmatch: Source", "hl1mp", HL1MP_APPID );
+ REGISTER_GAME( pSteamApps, "Half-Life 2: Episode One", "episodic", 380 );
+ REGISTER_GAME( pSteamApps, "Portal", "portal", 400 );
+ REGISTER_GAME( pSteamApps, "Half-Life 2: Episode Two", "ep2", 420 );
+ REGISTER_GAME( pSteamApps, "Team Fortress 2", "tf", 440 );
+
+ REGISTER_GAME_WITH_VPK( pSteamApps, "Counter-Strike: Source", "cstrike", 240, "cstrike/cstrike_pak_dir.vpk" );
+ REGISTER_GAME_WITH_VPK( pSteamApps, "Garry's Mod", "garrysmod", 4000, "garrysmod/fallback_dir.vpk", "garrysmod/garrysmod_dir.vpk" );
KeyValues *pMainFile, *pFileSystemInfo;
#ifdef CLIENT_DLL
@@ -145,17 +187,162 @@ void MountUserContent()
for ( KeyValues *pKey = pFileSystemInfo->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey() )
{
- if ( strcmp( pKey->GetName(), "AppId" ) != 0 )
- continue;
+ // pKey->GetName() is garrysmod/cstrike/etc
+ int nAppId = pKey->GetInt();
- int nExtraContentId = pKey->GetInt();
-
- if ( nExtraContentId )
+ if ( nAppId )
{
- AddSearchPathByAppId( nExtraContentId );
+ AddSearchPathByAppId( nAppId );
}
}
}
pMainFile->deleteThis();
}
+
+///
+/// Adds the specified game to the gamecontent.txt file and mounts the content for the specified game.
+///
+/// The AppID of the game to mount the content for.
+/// Whether the game was successfully mounted.
+bool MountGameContentByAppId( int nAppId )
+{
+ mountableGame_t *pGameToMount = nullptr;
+
+ for ( int i = 0; i < g_GamePaths.Count(); i++ )
+ {
+ if ( g_GamePaths[i].appId == nAppId )
+ {
+ pGameToMount = &g_GamePaths[i];
+ break;
+ }
+ }
+
+ if ( !pGameToMount )
+ {
+ Warning( "Failed to mount content for game with AppID: %i (game not found)\n", nAppId );
+ return false;
+ }
+
+ KeyValues *pMainFile, *pFileSystemInfo;
+ pMainFile = new KeyValues( "gamecontent.txt" );
+
+#ifdef CLIENT_DLL
+ const char *gamePath = engine->GetGameDirectory();
+#else
+ char gamePath[256];
+ engine->GetGameDir( gamePath, 256 );
+ Q_StripTrailingSlash( gamePath );
+#endif
+
+ // On linux because of case sensitiviy we need to check for both.
+ const char *paths[] = {
+#ifdef _LINUX
+ "%s/GameContent.txt",
+#endif
+ "%s/gamecontent.txt" };
+
+ bool bFound = false;
+
+ for ( int i = 0; i < ARRAYSIZE( paths ); i++ )
+ {
+ if ( !pMainFile->LoadFromFile( filesystem, UTIL_VarArgs( paths[i], gamePath ), CONTENT_SEARCH_PATH ) )
+ continue;
+
+ pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
+
+ if ( !pFileSystemInfo )
+ continue;
+
+ pFileSystemInfo->SetInt( pGameToMount->directoryName, pGameToMount->appId );
+
+ pMainFile->SaveToFile( filesystem, UTIL_VarArgs( paths[i], gamePath ), CONTENT_SEARCH_PATH );
+ bFound = true;
+
+ // We've found the game, no need to continue searching.
+ break;
+ }
+
+ pMainFile->deleteThis();
+
+ if ( !bFound )
+ {
+ Warning( "Failed to mount content for game %s (GameContent.txt not found)\n", pGameToMount->name );
+ return false;
+ }
+
+ AddSearchPathByAppId( pGameToMount->appId );
+ return true;
+}
+
+bool UnmountGameContentByAppId( int nAppId )
+{
+ mountableGame_t *pGameToUnmount = nullptr;
+
+ for ( int i = 0; i < g_GamePaths.Count(); i++ )
+ {
+ if ( g_GamePaths[i].appId == nAppId )
+ {
+ pGameToUnmount = &g_GamePaths[i];
+ break;
+ }
+ }
+
+ if ( !pGameToUnmount )
+ {
+ Warning( "Failed to unmount content for game with AppID: %i (game not found)\n", nAppId );
+ return false;
+ }
+
+ KeyValues *pMainFile, *pFileSystemInfo;
+ pMainFile = new KeyValues( "gamecontent.txt" );
+
+ #ifdef CLIENT_DLL
+ const char *gamePath = engine->GetGameDirectory();
+ #else
+ char gamePath[256];
+ engine->GetGameDir( gamePath, 256 );
+ Q_StripTrailingSlash( gamePath );
+ #endif
+
+ // On linux because of case sensitiviy we need to check for both.
+ const char *paths[] = {
+ #ifdef _LINUX
+ "%s/GameContent.txt",
+ #endif
+ "%s/gamecontent.txt" };
+
+ bool bFound = false;
+
+ for ( int i = 0; i < ARRAYSIZE( paths ); i++ )
+ {
+ if ( !pMainFile->LoadFromFile( filesystem, UTIL_VarArgs( paths[i], gamePath ), CONTENT_SEARCH_PATH ) )
+ continue;
+
+ pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
+
+ if ( !pFileSystemInfo )
+ continue;
+
+ KeyValues *pSubKey = pFileSystemInfo->FindKey( pGameToUnmount->directoryName );
+ pFileSystemInfo->RemoveSubKey( pSubKey );
+
+ pMainFile->SaveToFile( filesystem, UTIL_VarArgs( paths[i], gamePath ), CONTENT_SEARCH_PATH );
+ bFound = true;
+
+ // We've found the game, no need to continue searching.
+ break;
+ }
+
+ pMainFile->deleteThis();
+
+ if ( !bFound )
+ {
+ Warning( "Failed to unmount content for game %s (GameContent.txt not found)\n", pGameToUnmount->name );
+ return false;
+ }
+
+ RemoveSearchPathByAppId( pGameToUnmount->appId );
+
+ return true;
+}
diff --git a/src/game/shared/mountsteamcontent.h b/src/game/shared/mountsteamcontent.h
index f3d6834f20..ced991542a 100644
--- a/src/game/shared/mountsteamcontent.h
+++ b/src/game/shared/mountsteamcontent.h
@@ -11,6 +11,58 @@
#pragma once
#endif
-void MountUserContent();
+struct mountableGame_t
+{
+ // Directory inside the game's root directory where the game's content is stored
+ // e.g: garrysmod, cstrike, hl2
+ const char *directoryName;
+
+ // The Steam AppID of the game
+ int appId;
+
+ // The human-readable name of the game
+ const char *name;
+
+ // Paths to VPK files that should be mounted
+ CUtlVector< const char * > vpks;
+
+ bool isMounted;
+ bool isOwned;
+ bool isInstalled;
+
+ mountableGame_t( const char *gameName, const char *directoryName, int gameId, bool isInstalled, bool isOwned )
+ : name( gameName ),
+ directoryName( directoryName ),
+ appId( gameId ),
+ isMounted( false ),
+ isOwned( isOwned ),
+ isInstalled( isInstalled )
+ {
+ }
+
+ mountableGame_t( const mountableGame_t &other )
+ : name( other.name ),
+ directoryName( other.directoryName ),
+ appId( other.appId ),
+ isMounted( other.isMounted ),
+ isOwned( other.isOwned ),
+ isInstalled( other.isInstalled )
+ {
+ for ( int i = 0; i < other.vpks.Count(); i++ )
+ {
+ vpks.AddToTail( other.vpks[i] );
+ }
+ }
+
+ void AddVPK( const char *vpkPath )
+ {
+ vpks.AddToTail( vpkPath );
+ }
+};
+
+void InitializeGameContentMounting();
+bool MountGameContentByAppId( int nAppId );
+bool UnmountGameContentByAppId( int nAppId );
+CUtlVector< mountableGame_t > &GetMountableGames();
#endif // MOUNTSTEAMCONTENT_H
diff --git a/src/lib/public/tier1.lib b/src/lib/public/tier1.lib
index afa4d4a363..a3a1479121 100644
--- a/src/lib/public/tier1.lib
+++ b/src/lib/public/tier1.lib
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:edbfc8b0bc42291a39a6769d5d0929898917618277ca8585ba21ea6b7a7b503e
-size 4694178
+oid sha256:3bcbd48fa03f414fb16641c84bd22bc5a466b03baeed7f4bd41cf1d9d2c6b952
+size 4694814
diff --git a/src/lib/public/vgui_controls.lib b/src/lib/public/vgui_controls.lib
index 8a1d7cd314..ee4d9fb18c 100644
--- a/src/lib/public/vgui_controls.lib
+++ b/src/lib/public/vgui_controls.lib
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:72613ef7b535161a9988c823f1235a21fe91236897c86cc5491ba7142c271d5d
-size 19481116
+oid sha256:700de62224899a3b880e08c7f9588a75898e4af339dd72be7e7e5702d09a33cd
+size 19481222
diff --git a/src/public/lcdll_int.cpp b/src/public/lcdll_int.cpp
index 81ebfa6eb4..21d95ec878 100644
--- a/src/public/lcdll_int.cpp
+++ b/src/public/lcdll_int.cpp
@@ -22,12 +22,6 @@ static int engine_ActivateOccluder( lua_State *L )
return 0;
}
-static int engine_ChangeTeam( lua_State *L )
-{
- engine->ChangeTeam( luaL_checkstring( L, 1 ) );
- return 0;
-}
-
static int engine_CheckKeyTrapping( lua_State *L )
{
ButtonCode_t code;
@@ -137,12 +131,6 @@ static int engine_GameLumpVersion( lua_State *L )
return 1;
}
-static int engine_GetAppID( lua_State *L )
-{
- lua_pushinteger( L, engine->GetAppID() );
- return 1;
-}
-
static int engine_GetClientConVarValue( lua_State *L )
{
int iPlayer;
@@ -218,24 +206,6 @@ static int engine_GetLastTimeStamp( lua_State *L )
return 1;
}
-static int engine_GetServerAddress( lua_State *L )
-{
- lua_pushstring( L, g_pGameInfoStore->GetServerAddress() );
- return 1;
-}
-
-static int engine_GetServerName( lua_State *L )
-{
- lua_pushstring( L, g_pGameInfoStore->GetServerName() );
- return 1;
-}
-
-static int engine_GetLevelName( lua_State *L )
-{
- lua_pushstring( L, engine->GetLevelName() );
- return 1;
-}
-
static int engine_GetLightForPoint( lua_State *L )
{
Vector v = engine->GetLightForPoint( luaL_checkvector( L, 1 ), luaL_checkboolean( L, 2 ) );
@@ -264,12 +234,6 @@ static int engine_GetMainMenuBackgroundName( lua_State *L )
return 1;
}
-static int engine_GetMapEntitiesString( lua_State *L )
-{
- lua_pushstring( L, engine->GetMapEntitiesString() );
- return 1;
-}
-
static int engine_GetMaxClients( lua_State *L )
{
lua_pushinteger( L, engine->GetMaxClients() );
@@ -388,12 +352,6 @@ static int engine_IsHLTV( lua_State *L )
return 1;
}
-static int engine_IsInEditMode( lua_State *L )
-{
- lua_pushboolean( L, engine->IsInEditMode() );
- return 1;
-}
-
static int engine_IsInGame( lua_State *L )
{
lua_pushboolean( L, engine->IsInGame() );
@@ -406,24 +364,12 @@ static int engine_IsLevelMainMenuBackground( lua_State *L )
return 1;
}
-static int engine_IsLowViolence( lua_State *L )
-{
- lua_pushboolean( L, engine->IsLowViolence() );
- return 1;
-}
-
static int engine_IsOccluded( lua_State *L )
{
luaL_checkboolean( L, engine->IsOccluded( luaL_checkvector( L, 1 ), luaL_checkvector( L, 2 ) ) );
return 1;
}
-static int engine_IsPaused( lua_State *L )
-{
- lua_pushboolean( L, engine->IsPaused() );
- return 1;
-}
-
static int engine_IsPlayingDemo( lua_State *L )
{
lua_pushboolean( L, engine->IsPlayingDemo() );
@@ -512,36 +458,6 @@ static int engine_ResetDemoInterpolation( lua_State *L )
return 0;
}
-static int engine_SentenceGroupIndexFromName( lua_State *L )
-{
- lua_pushinteger( L, engine->SentenceGroupIndexFromName( luaL_checkstring( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceGroupNameFromIndex( lua_State *L )
-{
- lua_pushstring( L, engine->SentenceGroupNameFromIndex( luaL_checknumber( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceIndexFromName( lua_State *L )
-{
- lua_pushinteger( L, engine->SentenceIndexFromName( luaL_checkstring( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceLength( lua_State *L )
-{
- lua_pushnumber( L, engine->SentenceLength( luaL_checknumber( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceNameFromIndex( lua_State *L )
-{
- lua_pushstring( L, engine->SentenceNameFromIndex( luaL_checknumber( L, 1 ) ) );
- return 1;
-}
-
static int engine_ServerCmd( lua_State *L )
{
engine->ServerCmd( luaL_checkstring( L, 1 ), luaL_checkboolean( L, 2 ) );
@@ -584,15 +500,8 @@ static int engine_SupportsHDR( lua_State *L )
return 1;
}
-static int engine_Time( lua_State *L )
-{
- lua_pushnumber( L, engine->Time() );
- return 1;
-}
-
static const luaL_Reg enginelib[] = {
{ "ActivateOccluder", engine_ActivateOccluder },
- { "ChangeTeam", engine_ChangeTeam },
{ "CheckKeyTrapping", engine_CheckKeyTrapping },
{ "IsKeyTrapping", engine_IsKeyTrapping },
@@ -609,21 +518,16 @@ static const luaL_Reg enginelib[] = {
{ "EngineStats_BeginFrame", engine_EngineStats_BeginFrame },
{ "EngineStats_EndFrame", engine_EngineStats_EndFrame },
{ "GameLumpSize", engine_GameLumpSize },
- { "GetAppID", engine_GetAppID },
{ "GetClientConVarValue", engine_GetClientConVarValue },
{ "GetClientConVarValueAsNumber", engine_GetClientConVarValueAsNumber },
{ "GetDXSupportLevel", engine_GetDXSupportLevel },
{ "GetEngineBuildNumber", engine_GetEngineBuildNumber },
{ "GetGameDirectory", engine_GetGameDirectory },
{ "GetLastTimeStamp", engine_GetLastTimeStamp },
- { "GetServerAddress", engine_GetServerAddress },
- { "GetServerName", engine_GetServerName },
- { "GetLevelName", engine_GetLevelName },
{ "GetLightForPoint", engine_GetLightForPoint },
{ "GetLightForPointFast", engine_GetLightForPointFast },
{ "GetLocalPlayer", engine_GetLocalPlayer },
{ "GetMainMenuBackgroundName", engine_GetMainMenuBackgroundName },
- { "GetMapEntitiesString", engine_GetMapEntitiesString },
{ "GetMaxClients", engine_GetMaxClients },
{ "GetMostRecentSaveGame", engine_GetMostRecentSaveGame },
{ "GetPlayerForUserID", engine_GetPlayerForUserID },
@@ -639,12 +543,9 @@ static const luaL_Reg enginelib[] = {
{ "IsDrawingLoadingImage", engine_IsDrawingLoadingImage },
{ "IsHammerRunning", engine_IsHammerRunning },
{ "IsHLTV", engine_IsHLTV },
- { "IsInEditMode", engine_IsInEditMode },
{ "IsInGame", engine_IsInGame },
{ "IsLevelMainMenuBackground", engine_IsLevelMainMenuBackground },
- { "IsLowViolence", engine_IsLowViolence },
{ "IsOccluded", engine_IsOccluded },
- { "IsPaused", engine_IsPaused },
{ "IsPlayingDemo", engine_IsPlayingDemo },
{ "IsPlayingTimeDemo", engine_IsPlayingTimeDemo },
{ "IsRecordingDemo", engine_IsRecordingDemo },
@@ -659,11 +560,6 @@ static const luaL_Reg enginelib[] = {
{ "OnStorageDeviceDetached", engine_OnStorageDeviceDetached },
{ "REMOVED_SteamProcessCall", engine_REMOVED_SteamProcessCall },
{ "ResetDemoInterpolation", engine_ResetDemoInterpolation },
- { "SentenceGroupIndexFromName", engine_SentenceGroupIndexFromName },
- { "SentenceGroupNameFromIndex", engine_SentenceGroupNameFromIndex },
- { "SentenceIndexFromName", engine_SentenceIndexFromName },
- { "SentenceLength", engine_SentenceLength },
- { "SentenceNameFromIndex", engine_SentenceNameFromIndex },
{ "ServerCmd", engine_ServerCmd },
{ "SetMapLoadFailed", engine_SetMapLoadFailed },
{ "SetRestrictClientCommands", engine_SetRestrictClientCommands },
@@ -671,7 +567,6 @@ static const luaL_Reg enginelib[] = {
{ "Sound_ExtraUpdate", engine_Sound_ExtraUpdate },
{ "StartXboxExitingProcess", engine_StartXboxExitingProcess },
{ "SupportsHDR", engine_SupportsHDR },
- { "Time", engine_Time },
{ NULL, NULL } };
/*
diff --git a/src/public/leiface.cpp b/src/public/lengine.cpp
similarity index 71%
rename from src/public/leiface.cpp
rename to src/public/lengine.cpp
index d0b2c639f6..e2b9691c1d 100644
--- a/src/public/leiface.cpp
+++ b/src/public/lengine.cpp
@@ -35,12 +35,6 @@ static int engine_ChangeLevel( lua_State *L )
return 0;
}
-static int engine_ChangeTeam( lua_State *L )
-{
- engine->ChangeTeam( luaL_checkstring( L, 1 ) );
- return 0;
-}
-
static int engine_CheckAreasConnected( lua_State *L )
{
lua_pushinteger( L, engine->CheckAreasConnected( luaL_checkinteger( L, 1 ), luaL_checkinteger( L, 2 ) ) );
@@ -80,12 +74,6 @@ static int engine_ForceSimpleMaterial( lua_State *L )
return 0;
}
-static int engine_GetAppID( lua_State *L )
-{
- lua_pushinteger( L, engine->GetAppID() );
- return 1;
-}
-
static int engine_GetClientConVarValue( lua_State *L )
{
int iPlayer;
@@ -118,53 +106,6 @@ static int engine_GetEntityCount( lua_State *L )
return 1;
}
-static int engine_GetServerAddress( lua_State *L )
-{
- lua_pushstring( L, g_pGameInfoStore->GetServerAddress() );
- return 1;
-}
-
-static int engine_GetServerName( lua_State *L )
-{
- lua_pushstring( L, g_pGameInfoStore->GetServerName() );
- return 1;
-}
-
-static int engine_GetLevelName( lua_State *L )
-{
- char szMap[MAX_PATH + 1] = "";
- Q_strncpy( szMap, gpGlobals->mapname.ToCStr(), ARRAYSIZE( szMap ) );
- lua_pushstring( L, szMap );
- return 1;
-}
-
-static int engine_GetPlayerByAddress( lua_State *L )
-{
- CBasePlayer *pPlayer = g_pGameInfoStore->GetPlayerByAddress( luaL_checkstring( L, 1 ) );
-
- if ( pPlayer )
- {
- CBaseEntity::PushLuaInstanceSafe( L, pPlayer );
- return 1;
- }
-
- return 0;
-}
-
-static int engine_GetGameDir( lua_State *L )
-{
- char gamePath[256];
- engine->GetGameDir( gamePath, 256 );
- lua_pushstring( L, gamePath );
- return 1;
-}
-
-static int engine_GetMapEntitiesString( lua_State *L )
-{
- lua_pushstring( L, engine->GetMapEntitiesString() );
- return 1;
-}
-
static int engine_GetMostRecentlyLoadedFileName( lua_State *L )
{
lua_pushstring( L, engine->GetMostRecentlyLoadedFileName() );
@@ -219,24 +160,12 @@ static int engine_IsInCommentaryMode( lua_State *L )
return 1;
}
-static int engine_IsInEditMode( lua_State *L )
-{
- lua_pushinteger( L, engine->IsInEditMode() );
- return 1;
-}
-
static int engine_IsInternalBuild( lua_State *L )
{
lua_pushboolean( L, engine->IsInternalBuild() );
return 1;
}
-static int engine_IsLowViolence( lua_State *L )
-{
- lua_pushboolean( L, engine->IsLowViolence() );
- return 1;
-}
-
static int engine_IsMapValid( lua_State *L )
{
lua_pushinteger( L, engine->IsMapValid( luaL_checkstring( L, 1 ) ) );
@@ -249,12 +178,6 @@ static int engine_IsModelPrecached( lua_State *L )
return 1;
}
-static int engine_IsPaused( lua_State *L )
-{
- lua_pushboolean( L, engine->IsPaused() );
- return 1;
-}
-
static int engine_LightStyle( lua_State *L )
{
engine->LightStyle( luaL_checkinteger( L, 1 ), luaL_checkstring( L, 2 ) );
@@ -321,36 +244,6 @@ static int engine_PrecacheSentenceFile( lua_State *L )
return 1;
}
-static int engine_SentenceGroupIndexFromName( lua_State *L )
-{
- lua_pushinteger( L, engine->SentenceGroupIndexFromName( luaL_checkstring( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceGroupNameFromIndex( lua_State *L )
-{
- lua_pushstring( L, engine->SentenceGroupNameFromIndex( luaL_checkinteger( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceIndexFromName( lua_State *L )
-{
- lua_pushinteger( L, engine->SentenceIndexFromName( luaL_checkstring( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceLength( lua_State *L )
-{
- lua_pushnumber( L, engine->SentenceLength( luaL_checkinteger( L, 1 ) ) );
- return 1;
-}
-
-static int engine_SentenceNameFromIndex( lua_State *L )
-{
- lua_pushstring( L, engine->SentenceNameFromIndex( luaL_checkinteger( L, 1 ) ) );
- return 1;
-}
-
static int engine_ServerCommand( lua_State *L )
{
const char *pszCommandString = luaL_checkstring( L, 1 );
@@ -380,38 +273,17 @@ static int engine_SetDedicatedServerBenchmarkMode( lua_State *L )
return 0;
}
-static int engine_Time( lua_State *L )
-{
- lua_pushnumber( L, engine->Time() );
- return 1;
-}
-
-static int engine_GetSoundDuration( lua_State *L )
-{
- float flDuration = enginesound->GetSoundDuration( luaL_checkstring( L, 1 ) );
- lua_pushnumber( L, flDuration );
- return 1;
-}
-
static const luaL_Reg enginelib[] = {
{ "AllowImmediateEdictReuse", engine_AllowImmediateEdictReuse },
{ "ChangeLevel", engine_ChangeLevel },
- { "ChangeTeam", engine_ChangeTeam },
{ "CheckAreasConnected", engine_CheckAreasConnected },
{ "CopyFile", engine_CopyFile },
{ "CreateFakeClient", engine_CreateFakeClient },
{ "ForceExactFile", engine_ForceExactFile },
{ "ForceSimpleMaterial", engine_ForceSimpleMaterial },
- { "GetAppID", engine_GetAppID },
{ "GetClientConVarValue", engine_GetClientConVarValue },
{ "GetClientConVarValueAsNumber", engine_GetClientConVarValueAsNumber },
{ "GetEntityCount", engine_GetEntityCount },
- { "GetServerAddress", engine_GetServerAddress },
- { "GetServerName", engine_GetServerName },
- { "GetLevelName", engine_GetLevelName },
- { "GetPlayerByAddress", engine_GetPlayerByAddress },
- { "GetGameDir", engine_GetGameDir },
- { "GetMapEntitiesString", engine_GetMapEntitiesString },
{ "GetMostRecentlyLoadedFileName", engine_GetMostRecentlyLoadedFileName },
{ "GetPlayerNetInfo", engine_GetPlayerNetInfo },
{ "GetSaveFileName", engine_GetSaveFileName },
@@ -421,12 +293,9 @@ static const luaL_Reg enginelib[] = {
{ "IsDedicatedServer", engine_IsDedicatedServer },
{ "IsGenericPrecached", engine_IsGenericPrecached },
{ "IsInCommentaryMode", engine_IsInCommentaryMode },
- { "IsInEditMode", engine_IsInEditMode },
{ "IsInternalBuild", engine_IsInternalBuild },
- { "IsLowViolence", engine_IsLowViolence },
{ "IsMapValid", engine_IsMapValid },
{ "IsModelPrecached", engine_IsModelPrecached },
- { "IsPaused", engine_IsPaused },
{ "LoadAdjacentEnts", engine_LoadAdjacentEnts },
{ "LoadGameState", engine_LoadGameState },
{ "LockNetworkStringTables", engine_LockNetworkStringTables },
@@ -437,17 +306,10 @@ static const luaL_Reg enginelib[] = {
{ "PrecacheGeneric", engine_PrecacheGeneric },
{ "PrecacheModel", engine_PrecacheModel },
{ "PrecacheSentenceFile", engine_PrecacheSentenceFile },
- { "SentenceGroupIndexFromName", engine_SentenceGroupIndexFromName },
- { "SentenceGroupNameFromIndex", engine_SentenceGroupNameFromIndex },
- { "SentenceIndexFromName", engine_SentenceIndexFromName },
- { "SentenceLength", engine_SentenceLength },
- { "SentenceNameFromIndex", engine_SentenceNameFromIndex },
{ "ServerCommand", engine_ServerCommand },
{ "ServerExecute", engine_ServerExecute },
{ "SetAreaPortalState", engine_SetAreaPortalState },
{ "SetDedicatedServerBenchmarkMode", engine_SetDedicatedServerBenchmarkMode },
- { "Time", engine_Time },
- { "GetSoundDuration", engine_GetSoundDuration },
{ NULL, NULL } };
/*
diff --git a/src/public/lengine_shared.cpp b/src/public/lengine_shared.cpp
new file mode 100644
index 0000000000..00762bc777
--- /dev/null
+++ b/src/public/lengine_shared.cpp
@@ -0,0 +1,216 @@
+#include "cbase.h"
+#include "luamanager.h"
+#include "luasrclib.h"
+#include "lbaseentity_shared.h"
+#include "lbaseplayer_shared.h"
+#include "linetchannelinfo.h"
+#include "engine/IEngineSound.h"
+#include
+#include
+#include
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+#include
+
+static int engine_ChangeTeam( lua_State *L )
+{
+ engine->ChangeTeam( luaL_checkstring( L, 1 ) );
+ return 0;
+}
+
+static int engine_GetAppID( lua_State *L )
+{
+ lua_pushinteger( L, engine->GetAppID() );
+ return 1;
+}
+
+static int engine_GetServerAddress( lua_State *L )
+{
+ lua_pushstring( L, g_pGameInfoStore->GetServerAddress() );
+ return 1;
+}
+
+static int engine_GetServerName( lua_State *L )
+{
+ lua_pushstring( L, g_pGameInfoStore->GetServerName() );
+ return 1;
+}
+
+static int engine_GetLevelName( lua_State *L )
+{
+#ifdef CLIENT_DLL
+ lua_pushstring( L, engine->GetLevelName() );
+#else
+ char szMap[MAX_PATH + 1] = "";
+ Q_strncpy( szMap, gpGlobals->mapname.ToCStr(), ARRAYSIZE( szMap ) );
+ lua_pushstring( L, szMap );
+#endif
+ return 1;
+}
+
+static int engine_GetPlayerByAddress( lua_State *L )
+{
+ CBasePlayer *pPlayer = g_pGameInfoStore->GetPlayerByAddress( luaL_checkstring( L, 1 ) );
+
+ if ( pPlayer )
+ {
+ CBaseEntity::PushLuaInstanceSafe( L, pPlayer );
+ return 1;
+ }
+
+ return 0;
+}
+
+static int engine_GetGameDir( lua_State *L )
+{
+#ifdef CLIENT_DLL
+ const char *gamePath = engine->GetGameDirectory();
+#else
+ char gamePath[MAX_PATH];
+ engine->GetGameDir( gamePath, MAX_PATH );
+ Q_StripTrailingSlash( gamePath );
+#endif
+ lua_pushstring( L, gamePath );
+
+ return 1;
+}
+
+static int engine_GetMapEntitiesString( lua_State *L )
+{
+ lua_pushstring( L, engine->GetMapEntitiesString() );
+ return 1;
+}
+
+static int engine_GetMountableGames( lua_State *L )
+{
+ lua_newtable( L );
+
+ CUtlVector< mountableGame_t > &mountableGames = GetMountableGames();
+
+ for ( int i = 0; i < mountableGames.Count(); i++ )
+ {
+ lua_pushinteger( L, i + 1 );
+ lua_newtable( L );
+
+ lua_pushstring( L, "name" );
+ lua_pushstring( L, mountableGames[i].name );
+ lua_settable( L, -3 );
+
+ lua_pushstring( L, "directoryName" );
+ lua_pushstring( L, mountableGames[i].directoryName );
+ lua_settable( L, -3 );
+
+ lua_pushstring( L, "isMounted" );
+ lua_pushboolean( L, mountableGames[i].isMounted );
+ lua_settable( L, -3 );
+
+ lua_pushstring( L, "isOwned" );
+ lua_pushboolean( L, mountableGames[i].isOwned );
+ lua_settable( L, -3 );
+
+ lua_pushstring( L, "isInstalled" );
+ lua_pushboolean( L, mountableGames[i].isInstalled );
+ lua_settable( L, -3 );
+
+ lua_pushstring( L, "appId" );
+ lua_pushinteger( L, mountableGames[i].appId );
+ lua_settable( L, -3 );
+
+ lua_settable( L, -3 );
+ }
+
+ return 1;
+}
+
+static int engine_IsInEditMode( lua_State *L )
+{
+ lua_pushinteger( L, engine->IsInEditMode() );
+ return 1;
+}
+
+static int engine_IsLowViolence( lua_State *L )
+{
+ lua_pushboolean( L, engine->IsLowViolence() );
+ return 1;
+}
+
+static int engine_IsPaused( lua_State *L )
+{
+ lua_pushboolean( L, engine->IsPaused() );
+ return 1;
+}
+
+static int engine_SentenceGroupIndexFromName( lua_State *L )
+{
+ lua_pushinteger( L, engine->SentenceGroupIndexFromName( luaL_checkstring( L, 1 ) ) );
+ return 1;
+}
+
+static int engine_SentenceGroupNameFromIndex( lua_State *L )
+{
+ lua_pushstring( L, engine->SentenceGroupNameFromIndex( luaL_checkinteger( L, 1 ) ) );
+ return 1;
+}
+
+static int engine_SentenceIndexFromName( lua_State *L )
+{
+ lua_pushinteger( L, engine->SentenceIndexFromName( luaL_checkstring( L, 1 ) ) );
+ return 1;
+}
+
+static int engine_SentenceLength( lua_State *L )
+{
+ lua_pushnumber( L, engine->SentenceLength( luaL_checkinteger( L, 1 ) ) );
+ return 1;
+}
+
+static int engine_SentenceNameFromIndex( lua_State *L )
+{
+ lua_pushstring( L, engine->SentenceNameFromIndex( luaL_checkinteger( L, 1 ) ) );
+ return 1;
+}
+
+static int engine_Time( lua_State *L )
+{
+ lua_pushnumber( L, engine->Time() );
+ return 1;
+}
+
+static int engine_GetSoundDuration( lua_State *L )
+{
+ float flDuration = enginesound->GetSoundDuration( luaL_checkstring( L, 1 ) );
+ lua_pushnumber( L, flDuration );
+ return 1;
+}
+
+static const luaL_Reg enginelib[] = {
+ { "ChangeTeam", engine_ChangeTeam },
+ { "GetAppID", engine_GetAppID },
+ { "GetServerAddress", engine_GetServerAddress },
+ { "GetServerName", engine_GetServerName },
+ { "GetLevelName", engine_GetLevelName },
+ { "GetPlayerByAddress", engine_GetPlayerByAddress },
+ { "GetGameDir", engine_GetGameDir },
+ { "GetMapEntitiesString", engine_GetMapEntitiesString },
+ { "GetMountableGames", engine_GetMountableGames },
+ { "IsInEditMode", engine_IsInEditMode },
+ { "IsLowViolence", engine_IsLowViolence },
+ { "IsPaused", engine_IsPaused },
+ { "SentenceGroupIndexFromName", engine_SentenceGroupIndexFromName },
+ { "SentenceGroupNameFromIndex", engine_SentenceGroupNameFromIndex },
+ { "SentenceIndexFromName", engine_SentenceIndexFromName },
+ { "SentenceLength", engine_SentenceLength },
+ { "SentenceNameFromIndex", engine_SentenceNameFromIndex },
+ { "Time", engine_Time },
+ { "GetSoundDuration", engine_GetSoundDuration },
+ { NULL, NULL } };
+
+/*
+** Open engine library
+*/
+LUALIB_API int luaopen_engine_shared( lua_State *L )
+{
+ luaL_register( L, LUA_ENGINELIBNAME, enginelib );
+ return 1;
+}
diff --git a/src/public/tier1/KeyValues.h b/src/public/tier1/KeyValues.h
index 97ae69c23e..bf09e4a406 100644
--- a/src/public/tier1/KeyValues.h
+++ b/src/public/tier1/KeyValues.h
@@ -134,6 +134,7 @@ class KeyValues
KeyValues *FindKey(const char *keyName, bool bCreate = false);
KeyValues *FindKey(int keySymbol) const;
KeyValues *CreateNewKey(); // creates a new key, with an autogenerated name. name is guaranteed to be an integer, of value 1 higher than the highest other integer key name
+ KeyValues *GetNextKeyName( char *newKeyName, int newKeyNameBufferSize );
void AddSubKey( KeyValues *pSubkey ); // Adds a subkey. Make sure the subkey isn't a child of some other keyvalues
void RemoveSubKey(KeyValues *subKey); // removes a subkey from the list, DOES NOT DELETE IT
diff --git a/src/tier1/KeyValues.cpp b/src/tier1/KeyValues.cpp
index 7152950946..8559e836ab 100644
--- a/src/tier1/KeyValues.cpp
+++ b/src/tier1/KeyValues.cpp
@@ -1085,10 +1085,18 @@ KeyValues *KeyValues::FindKey( const char *keyName, bool bCreate )
//-----------------------------------------------------------------------------
KeyValues *KeyValues::CreateNewKey()
{
+ char buf[12];
+ KeyValues *pLastChild = GetNextKeyName( buf, sizeof( buf ) );
+
+ return CreateKeyUsingKnownLastChild( buf, pLastChild );
+}
+
+KeyValues *KeyValues::GetNextKeyName( char *newKeyName, int newKeyNameBufferSize )
+{
+ KeyValues *pLastChild = NULL;
int newID = 1;
// search for any key with higher values
- KeyValues *pLastChild = NULL;
for ( KeyValues *dat = m_pSub; dat != NULL; dat = dat->m_pPeer )
{
// case-insensitive string compare
@@ -1101,10 +1109,9 @@ KeyValues *KeyValues::CreateNewKey()
pLastChild = dat;
}
- char buf[12];
- Q_snprintf( buf, sizeof( buf ), "%d", newID );
+ Q_snprintf( newKeyName, newKeyNameBufferSize, "%d", newID );
- return CreateKeyUsingKnownLastChild( buf, pLastChild );
+ return pLastChild;
}
//-----------------------------------------------------------------------------
diff --git a/src/vgui2/vgui_controls/HTML.cpp b/src/vgui2/vgui_controls/HTML.cpp
index 7da70c3e95..56d1b3b697 100644
--- a/src/vgui2/vgui_controls/HTML.cpp
+++ b/src/vgui2/vgui_controls/HTML.cpp
@@ -342,6 +342,7 @@ void HTML::AddJavascriptObjectCallback( const char *pszObjectName, const char *p
" args = Array.prototype.slice.call(arguments); "
" } "
" let __cId = window.g_Interop.__callbackQueue.push(callback)-1; "
+ " console.log('Callback ID: ' + __cId + ' with args: ' + JSON.stringify(args)); "
" alert('%s' + JSON.stringify({object:'%s',property:'%s',arguments:args,callbackId:__cId})); "
"}; ",
pszObjectName,
@@ -367,7 +368,7 @@ void HTML::CallJavascriptObjectCallback( int callbackId, KeyValues *args )
CJsonToKeyValues::ConvertKeyValuesToJson( args, szJson, sizeof( szJson ) );
char szScript[8096];
- Q_snprintf( szScript, sizeof( szScript ), "if (typeof window.g_Interop.__callbackQueue[%i] === 'function') window.g_Interop.__callbackQueue[%i](%s);", callbackId, callbackId, szJson );
+ Q_snprintf( szScript, sizeof( szScript ), "if (typeof window.g_Interop.__callbackQueue[%i] === 'function') window.g_Interop.__callbackQueue[%i].apply(null, Object.values(%s));", callbackId, callbackId, szJson );
m_SteamAPIContext.SteamHTMLSurface()->ExecuteJavascript( m_unBrowserHandle, szScript );
}
@@ -380,11 +381,14 @@ void HTML::OnJavaScriptCallback( KeyValues *pData )
{
// const char *pszObject = pData->GetString( "object" );
// const char *pszProperty = pData->GetString( "property" );
+ //KeyValues *pArguments = pData->FindKey( "arguments" );
- // for ( KeyValues *pArg = pData->FindKey( "arguments" ); pArg; pArg = pArg->GetNextKey() )
+ //for ( KeyValues *pArg = pArguments->GetFirstSubKey(); pArg; pArg = pArg->GetNextKey() )
//{
- // DevMsg( "Argument: %s (inside %s.%s call)\n", pArg->GetName(), pszObject, pszProperty );
- // }
+ // lua_pushstring( L, pArg->GetName() );
+ // lua_pushstring( L, pArg->GetString() );
+ // lua_settable( L, -3 );
+ //}
}
///