Skip to content

Commit e8fbc0a

Browse files
committed
Improved file caching
Removed cache-busting query parameters from all file requests to allow browsers to cache files properly. The "update map" button revalidates all content but is now also able to make use of the browser cache, by using Fetch's `cache: "no-cache"` option which allows the browser to make a conditional request to check if the file has changed and then fall back to the cached version if it hasn't.
1 parent b8baa22 commit e8fbc0a

File tree

10 files changed

+516
-56
lines changed

10 files changed

+516
-56
lines changed

common/webapp/.eslintrc.cjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/* eslint-env node */
22
module.exports = {
33
root: true,
4+
env: {
5+
es2022: true
6+
},
47
'extends': [
58
'plugin:vue/vue3-essential',
69
'eslint:recommended'

common/webapp/src/js/BlueMapApp.js

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ import "./BlueMap";
2626
import {MapViewer} from "./MapViewer";
2727
import {MapControls} from "./controls/map/MapControls";
2828
import {FreeFlightControls} from "./controls/freeflight/FreeFlightControls";
29-
import {FileLoader, MathUtils, Vector3} from "three";
29+
import {MathUtils, Vector3} from "three";
3030
import {Map as BlueMapMap} from "./map/Map";
31-
import {alert, animate, EasingFunctions, generateCacheHash} from "./util/Utils";
31+
import {alert, animate, EasingFunctions} from "./util/Utils";
3232
import {MainMenu} from "./MainMenu";
3333
import {PopupMarker} from "./PopupMarker";
3434
import {MarkerSet} from "./markers/MarkerSet";
3535
import {getLocalStorage, round, setLocalStorage} from "./Utils";
36+
import {RevalidatingFileLoader} from "./util/RevalidatingFileLoader";
3637
import {i18n, setLanguage} from "../i18n";
3738
import {PlayerMarkerManager} from "./markers/PlayerMarkerManager";
3839
import {NormalMarkerManager} from "./markers/NormalMarkerManager";
@@ -310,7 +311,7 @@ export class BlueMapApp {
310311
let map = new BlueMapMap(mapId, settings.mapDataRoot + "/" + mapId, settings.liveDataRoot + "/" + mapId, this.loadBlocker, this.mapViewer.events);
311312
maps.push(map);
312313

313-
return map.loadSettings(this.mapViewer.tileCacheHash)
314+
return map.loadSettings(this.mapViewer.revalidatedUrls)
314315
.catch(error => {
315316
alert(this.events, `Failed to load settings for map '${map.data.id}':` + error, "warning");
316317
});
@@ -366,9 +367,10 @@ export class BlueMapApp {
366367
*/
367368
loadSettings() {
368369
return new Promise((resolve, reject) => {
369-
let loader = new FileLoader();
370+
let loader = new RevalidatingFileLoader();
371+
loader.setRevalidatedUrls(new Set()); // force no-cache requests
370372
loader.setResponseType("json");
371-
loader.load("settings.json?" + generateCacheHash(),
373+
loader.load("settings.json",
372374
resolve,
373375
() => {},
374376
() => reject("Failed to load the settings.json!")
@@ -382,9 +384,10 @@ export class BlueMapApp {
382384
*/
383385
loadPlayerData(map) {
384386
return new Promise((resolve, reject) => {
385-
let loader = new FileLoader();
387+
let loader = new RevalidatingFileLoader();
388+
loader.setRevalidatedUrls(new Set()); // force no-cache requests
386389
loader.setResponseType("json");
387-
loader.load(map.data.liveDataRoot + "/live/players.json?" + generateCacheHash(),
390+
loader.load(map.data.liveDataRoot + "/live/players.json",
388391
fileData => {
389392
if (!fileData) reject(`Failed to parse '${this.fileUrl}'!`);
390393
else resolve(fileData);
@@ -636,11 +639,11 @@ export class BlueMapApp {
636639
return;
637640
}
638641

639-
// Only reuse the user's tile cash hash if the current browser navigation event is not a reload.
640-
// If it's a reload, we assume the user is troubleshooting and actually wants to refresh the map.
642+
// If it's a reload, we assume the user is troubleshooting and actually
643+
// wants to fully refresh the map.
641644
const [entry] = performance.getEntriesByType("navigation");
642-
if (entry.type != "reload") {
643-
this.mapViewer.clearTileCache(this.loadUserSetting("tileCacheHash", this.mapViewer.tileCacheHash));
645+
if (entry.type === "reload") {
646+
this.mapViewer.clearTileCache();
644647
}
645648

646649
this.mapViewer.superSampling = this.loadUserSetting("superSampling", this.mapViewer.data.superSampling);
@@ -665,7 +668,6 @@ export class BlueMapApp {
665668
if (!this.settings.useCookies) return;
666669

667670
this.saveUserSetting("resetSettings", false);
668-
this.saveUserSetting("tileCacheHash", this.mapViewer.tileCacheHash);
669671

670672
this.saveUserSetting("superSampling", this.mapViewer.data.superSampling);
671673
this.saveUserSetting("hiresViewDistance", this.mapViewer.data.loadedHiresViewDistance);

common/webapp/src/js/MapViewer.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {Map} from "./map/Map";
2727
import {SkyboxScene} from "./skybox/SkyboxScene";
2828
import {ControlsManager} from "./controls/ControlsManager";
2929
import Stats from "./util/Stats";
30-
import {alert, dispatchEvent, elementOffset, generateCacheHash, htmlToElement, softClamp} from "./util/Utils";
30+
import {alert, dispatchEvent, elementOffset, htmlToElement, softClamp} from "./util/Utils";
3131
import {TileManager} from "./map/TileManager";
3232
import {HIRES_VERTEX_SHADER} from "./map/hires/HiresVertexShader";
3333
import {HIRES_FRAGMENT_SHADER} from "./map/hires/HiresFragmentShader";
@@ -79,7 +79,12 @@ export class MapViewer {
7979
loadedLowresViewDistance: 2000,
8080
});
8181

82-
this.tileCacheHash = generateCacheHash();
82+
/** @import { RevalidatingFileLoader } from "./util/RevalidatingFileLoader" */
83+
/**
84+
* Used by {@link RevalidatingFileLoader}.
85+
* @type {Set<string> | undefined}
86+
*/
87+
this.revalidatedUrls = undefined;
8388

8489
this.stats = new Stats();
8590
this.stats.hide();
@@ -405,7 +410,7 @@ export class MapViewer {
405410
this.map = map;
406411

407412
if (this.map && this.map.isMap) {
408-
return map.load(HIRES_VERTEX_SHADER, HIRES_FRAGMENT_SHADER, LOWRES_VERTEX_SHADER, LOWRES_FRAGMENT_SHADER, this.data.uniforms, this.tileCacheHash)
413+
return map.load(HIRES_VERTEX_SHADER, HIRES_FRAGMENT_SHADER, LOWRES_VERTEX_SHADER, LOWRES_FRAGMENT_SHADER, this.data.uniforms, this.revalidatedUrls)
409414
.then(() => {
410415
for (let texture of this.map.loadedTextures){
411416
this.renderer.initTexture(texture);
@@ -462,15 +467,13 @@ export class MapViewer {
462467
}
463468
}
464469

465-
clearTileCache(newTileCacheHash) {
466-
if (!newTileCacheHash) newTileCacheHash = generateCacheHash();
467-
468-
this.tileCacheHash = newTileCacheHash;
470+
clearTileCache() {
471+
this.revalidatedUrls = new Set();
469472
if (this.map) {
470473
for (let i = 0; i < this.map.lowresTileManager.length; i++) {
471-
this.map.lowresTileManager[i].tileLoader.tileCacheHash = this.tileCacheHash;
474+
this.map.lowresTileManager[i].tileLoader.revalidatedUrls = this.revalidatedUrls;
472475
}
473-
this.map.hiresTileManager.tileLoader.tileCacheHash = this.tileCacheHash;
476+
this.map.hiresTileManager.tileLoader.revalidatedUrls = this.revalidatedUrls;
474477
}
475478
}
476479

common/webapp/src/js/map/LowresTileLoader.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
*/
2525
import {pathFromCoords} from "../util/Utils";
2626
import {
27-
TextureLoader,
2827
Mesh,
2928
PlaneGeometry,
3029
FrontSide,
@@ -34,23 +33,25 @@ import {
3433
NearestMipMapLinearFilter,
3534
Vector2
3635
} from "three";
36+
import {RevalidatingTextureLoader} from "../util/RevalidatingTextureLoader";
3737

3838
export class LowresTileLoader {
3939

40-
constructor(tilePath, tileSettings, lod, vertexShader, fragmentShader, uniforms, loadBlocker = () => Promise.resolve(), tileCacheHash = 0) {
40+
constructor(tilePath, tileSettings, lod, vertexShader, fragmentShader, uniforms, loadBlocker = () => Promise.resolve(), revalidatedUrls) {
4141
Object.defineProperty( this, 'isLowresTileLoader', { value: true } );
4242

4343
this.tilePath = tilePath;
4444
this.tileSettings = tileSettings;
4545
this.lod = lod;
4646
this.loadBlocker = loadBlocker;
47-
this.tileCacheHash = tileCacheHash;
47+
this.revalidatedUrls = revalidatedUrls;
4848

4949
this.vertexShader = vertexShader;
5050
this.fragmentShader = fragmentShader;
5151
this.uniforms = uniforms;
5252

53-
this.textureLoader = new TextureLoader();
53+
this.textureLoader = new RevalidatingTextureLoader();
54+
this.textureLoader.setRevalidatedUrls(this.revalidatedUrls);
5455
this.geometry = new PlaneGeometry(
5556
tileSettings.tileSize.x + 1, tileSettings.tileSize.z + 1,
5657
Math.ceil(100 / (lod * 2)), Math.ceil(100 / (lod * 2))
@@ -66,7 +67,8 @@ export class LowresTileLoader {
6667

6768
//await this.loadBlocker();
6869
return new Promise((resolve, reject) => {
69-
this.textureLoader.load(tileUrl + '?' + this.tileCacheHash,
70+
this.textureLoader.setRevalidatedUrls(this.revalidatedUrls);
71+
this.textureLoader.load(tileUrl,
7072
async texture => {
7173
texture.anisotropy = 1;
7274
texture.generateMipmaps = false;

common/webapp/src/js/map/Map.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import {
2626
ClampToEdgeWrapping,
2727
Color,
28-
FileLoader,
2928
FrontSide,
3029
NearestFilter,
3130
NearestMipMapLinearFilter,
@@ -34,6 +33,7 @@ import {
3433
Texture,
3534
Vector3
3635
} from "three";
36+
import {RevalidatingFileLoader} from "../util/RevalidatingFileLoader";
3737
import {alert, dispatchEvent, getPixel, hashTile, stringToImage, vecArrToObj} from "../util/Utils";
3838
import {TileManager} from "./TileManager";
3939
import {TileLoader} from "./TileLoader";
@@ -110,14 +110,14 @@ export class Map {
110110
* @param lowresVertexShader {string}
111111
* @param lowresFragmentShader {string}
112112
* @param uniforms {object}
113-
* @param tileCacheHash {number}
113+
* @param revalidatedUrls {Set<string> | undefined}
114114
* @returns {Promise<void>}
115115
*/
116-
load(hiresVertexShader, hiresFragmentShader, lowresVertexShader, lowresFragmentShader, uniforms, tileCacheHash = 0) {
116+
load(hiresVertexShader, hiresFragmentShader, lowresVertexShader, lowresFragmentShader, uniforms, revalidatedUrls) {
117117
this.unload()
118118

119-
let settingsPromise = this.loadSettings(tileCacheHash);
120-
let textureFilePromise = this.loadTexturesFile(tileCacheHash);
119+
let settingsPromise = this.loadSettings(revalidatedUrls);
120+
let textureFilePromise = this.loadTexturesFile(revalidatedUrls);
121121

122122
this.lowresMaterial = this.createLowresMaterial(lowresVertexShader, lowresFragmentShader, uniforms);
123123

@@ -133,7 +133,7 @@ export class Map {
133133
this.hiresMaterial,
134134
this.data.hires,
135135
this.loadBlocker,
136-
tileCacheHash
136+
revalidatedUrls
137137
), this.onTileLoad("hires"), this.onTileUnload("hires"), this.events);
138138
this.hiresTileManager.scene.matrixWorldAutoUpdate = false;
139139

@@ -147,7 +147,7 @@ export class Map {
147147
lowresFragmentShader,
148148
uniforms,
149149
async () => {},
150-
tileCacheHash
150+
revalidatedUrls
151151
), this.onTileLoad("lowres"), this.onTileUnload("lowres"), this.events);
152152
this.lowresTileManager[i].scene.matrixWorldAutoUpdate = false;
153153
}
@@ -160,8 +160,8 @@ export class Map {
160160
* Loads the settings of this map
161161
* @returns {Promise<void>}
162162
*/
163-
loadSettings(tileCacheHash) {
164-
return this.loadSettingsFile(tileCacheHash)
163+
loadSettings(revalidatedUrls) {
164+
return this.loadSettingsFile(revalidatedUrls)
165165
.then(worldSettings => {
166166
this.data.name = worldSettings.name ? worldSettings.name : this.data.name;
167167

@@ -259,13 +259,14 @@ export class Map {
259259
* Loads the settings.json file for this map
260260
* @returns {Promise<Object>}
261261
*/
262-
loadSettingsFile(tileCacheHash) {
262+
loadSettingsFile(revalidatedUrls) {
263263
return new Promise((resolve, reject) => {
264264
alert(this.events, `Loading settings for map '${this.data.id}'...`, "fine");
265265

266-
let loader = new FileLoader();
266+
let loader = new RevalidatingFileLoader();
267+
loader.setRevalidatedUrls(revalidatedUrls);
267268
loader.setResponseType("json");
268-
loader.load(this.data.settingsUrl + "?" + tileCacheHash,
269+
loader.load(this.data.settingsUrl,
269270
resolve,
270271
() => {},
271272
() => reject(`Failed to load the settings.json for map: ${this.data.id}`)
@@ -277,13 +278,14 @@ export class Map {
277278
* Loads the textures.json file for this map
278279
* @returns {Promise<Object>}
279280
*/
280-
loadTexturesFile(tileCacheHash) {
281+
loadTexturesFile(revalidatedUrls) {
281282
return new Promise((resolve, reject) => {
282283
alert(this.events, `Loading textures for map '${this.data.id}'...`, "fine");
283284

284-
let loader = new FileLoader();
285+
let loader = new RevalidatingFileLoader();
286+
loader.setRevalidatedUrls(revalidatedUrls);
285287
loader.setResponseType("json");
286-
loader.load(this.data.texturesUrl + "?" + tileCacheHash,
288+
loader.load(this.data.texturesUrl,
287289
resolve,
288290
() => {},
289291
() => reject(`Failed to load the textures.json for map: ${this.data.id}`)

common/webapp/src/js/map/TileLoader.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
* THE SOFTWARE.
2424
*/
2525
import {pathFromCoords} from "../util/Utils";
26-
import {BufferGeometryLoader, FileLoader, Mesh, Material} from "three";
26+
import {BufferGeometryLoader, Mesh, Material} from "three";
2727
import {PRBMLoader} from "./hires/PRBMLoader";
28+
import {RevalidatingFileLoader} from "../util/RevalidatingFileLoader";
2829

2930
export class TileLoader {
3031

@@ -37,21 +38,22 @@ export class TileLoader {
3738
* translate: {x: number, z: number}
3839
* }}
3940
* @param loadBlocker {function: Promise}
40-
* @param tileCacheHash {number}
41+
* @param revalidatedUrls {Set<string> | undefined}
4142
*/
42-
constructor(tilePath, material, tileSettings, loadBlocker = () => Promise.resolve(), tileCacheHash = 0) {
43+
constructor(tilePath, material, tileSettings, loadBlocker = () => Promise.resolve(), revalidatedUrls) {
4344
Object.defineProperty( this, 'isTileLoader', { value: true } );
4445

4546
this.tilePath = tilePath;
4647
this.material = material;
4748
this.tileSettings = tileSettings;
4849

49-
this.tileCacheHash = tileCacheHash;
50+
this.revalidatedUrls = revalidatedUrls;
5051

5152
this.loadBlocker = loadBlocker;
5253

53-
this.fileLoader = new FileLoader();
54+
this.fileLoader = new RevalidatingFileLoader();
5455
this.fileLoader.setResponseType('arraybuffer');
56+
this.fileLoader.setRevalidatedUrls(this.revalidatedUrls);
5557

5658
this.bufferGeometryLoader = new PRBMLoader();
5759
}
@@ -60,7 +62,8 @@ export class TileLoader {
6062
let tileUrl = this.tilePath + pathFromCoords(tileX, tileZ) + '.prbm';
6163

6264
return new Promise((resolve, reject) => {
63-
this.fileLoader.load(tileUrl + '?' + this.tileCacheHash,
65+
this.fileLoader.setRevalidatedUrls(this.revalidatedUrls);
66+
this.fileLoader.load(tileUrl,
6467
async data => {
6568

6669
await this.loadBlocker();

common/webapp/src/js/markers/MarkerManager.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2323
* THE SOFTWARE.
2424
*/
25-
import {FileLoader} from "three";
2625
import {MarkerSet} from "./MarkerSet";
27-
import {alert, generateCacheHash} from "../util/Utils";
26+
import {alert} from "../util/Utils";
27+
import {RevalidatingFileLoader} from "../util/RevalidatingFileLoader";
2828

2929
/**
3030
* A manager for loading and updating markers from a file
@@ -116,9 +116,10 @@ export class MarkerManager {
116116
*/
117117
loadMarkerFile() {
118118
return new Promise((resolve, reject) => {
119-
let loader = new FileLoader();
119+
let loader = new RevalidatingFileLoader();
120+
loader.setRevalidatedUrls(new Set()); // force no-cache requests
120121
loader.setResponseType("json");
121-
loader.load(this.fileUrl + "?" + generateCacheHash(),
122+
loader.load(this.fileUrl,
122123
markerFileData => {
123124
if (!markerFileData) reject(`Failed to parse '${this.fileUrl}'!`);
124125
else resolve(markerFileData);
@@ -129,4 +130,4 @@ export class MarkerManager {
129130
});
130131
}
131132

132-
}
133+
}

0 commit comments

Comments
 (0)