Skip to content

Commit 7eb90d6

Browse files
author
web3blind
committed
Use archive events for stale catch-up
1 parent 0fc5aad commit 7eb90d6

4 files changed

Lines changed: 131 additions & 33 deletions

File tree

app/index.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@
111111
<script src="lib/viz.min.js"></script>
112112

113113
<!-- Config -->
114-
<script src="js/config.js?v=20260621c"></script>
114+
<script src="js/config.js?v=20260621d"></script>
115115

116116
<!-- I18n -->
117-
<script src="js/i18n/ru.js?v=20260621c"></script>
118-
<script src="js/i18n/en.js?v=20260621c"></script>
117+
<script src="js/i18n/ru.js?v=20260621d"></script>
118+
<script src="js/i18n/en.js?v=20260621d"></script>
119119

120120
<!-- Utilities -->
121121
<script src="js/utils/helpers.js"></script>
@@ -125,7 +125,7 @@
125125
<!-- Blockchain layer -->
126126
<script src="js/blockchain/connection.js"></script>
127127
<script src="js/blockchain/account.js"></script>
128-
<script src="js/blockchain/history-source.js?v=20260621c"></script>
128+
<script src="js/blockchain/history-source.js?v=20260621d"></script>
129129
<script src="js/blockchain/broadcast.js"></script>
130130
<script src="js/blockchain/invite.js"></script>
131131

@@ -152,7 +152,7 @@
152152
<script src="js/engine/duel.js"></script>
153153
<script src="js/engine/duel-state.js"></script>
154154
<script src="js/engine/validator.js"></script>
155-
<script src="js/engine/guild.js?v=20260621c"></script>
155+
<script src="js/engine/guild.js?v=20260621d"></script>
156156
<script src="js/engine/territory.js"></script>
157157
<script src="js/engine/crafting.js"></script>
158158
<script src="js/engine/enchanting.js"></script>
@@ -186,8 +186,8 @@
186186
<script src="js/ui/screens/inventory.js"></script>
187187
<script src="js/ui/screens/chronicle.js"></script>
188188
<script src="js/ui/screens/duel.js"></script>
189-
<script src="js/ui/screens/arena.js?v=20260621c"></script>
190-
<script src="js/ui/screens/guild.js?v=20260621c"></script>
189+
<script src="js/ui/screens/arena.js?v=20260621d"></script>
190+
<script src="js/ui/screens/guild.js?v=20260621d"></script>
191191
<script src="js/ui/screens/map.js"></script>
192192
<script src="js/ui/screens/marketplace.js"></script>
193193
<script src="js/ui/screens/crafting.js"></script>

app/js/ui/app.js

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,92 @@ var App = (function() {
611611
* Process a batch of blocks sequentially, then save checkpoint.
612612
*/
613613
function _processBlockBatch(startBlock, endBlock, chainHead) {
614+
if (_shouldUseArchiveEventBatch(startBlock, endBlock, chainHead)) {
615+
_processArchiveEventBatch(startBlock, endBlock, chainHead, function(usedArchive) {
616+
if (!usedArchive) {
617+
_processBlockBatchFromRpc(startBlock, endBlock, chainHead);
618+
}
619+
});
620+
return;
621+
}
622+
623+
_processBlockBatchFromRpc(startBlock, endBlock, chainHead);
624+
}
625+
626+
function _shouldUseArchiveEventBatch(startBlock, endBlock, chainHead) {
627+
return typeof HistorySource !== 'undefined' &&
628+
HistorySource.getEventsRange &&
629+
chainHead - startBlock > 1000;
630+
}
631+
632+
function _processArchiveEventBatch(startBlock, endBlock, chainHead, done) {
633+
HistorySource.getEventsRange({
634+
protocol: VizMagicConfig.PROTOCOLS.VM,
635+
start: startBlock,
636+
end: endBlock,
637+
limit: 1000
638+
}, function(err, events) {
639+
if (err || !events) {
640+
done(false);
641+
return;
642+
}
643+
644+
events = events || [];
645+
events.sort(function(a, b) {
646+
if ((a.blockNum || 0) !== (b.blockNum || 0)) return (a.blockNum || 0) - (b.blockNum || 0);
647+
if ((a.txIndex || 0) !== (b.txIndex || 0)) return (a.txIndex || 0) - (b.txIndex || 0);
648+
return (a.opIndex || 0) - (b.opIndex || 0);
649+
});
650+
651+
var grouped = {};
652+
var order = [];
653+
for (var i = 0; i < events.length; i++) {
654+
var ev = events[i];
655+
var blockNum = ev.blockNum || 0;
656+
if (!blockNum) continue;
657+
if (!grouped[blockNum]) {
658+
grouped[blockNum] = {
659+
vmActions: [],
660+
voicePosts: [],
661+
veEvents: [],
662+
awards: [],
663+
blockHash: ev.block_id || ev.previous || '',
664+
blockNum: blockNum,
665+
timestamp: ev.timestamp || ''
666+
};
667+
order.push(blockNum);
668+
}
669+
var parsedAction = VMProtocol.parseAction(ev.raw && ev.raw.json ? ev.raw.json : JSON.stringify(ev.payload || {}));
670+
if (parsedAction) {
671+
grouped[blockNum].vmActions.push({
672+
sender: ev.sender || '',
673+
action: parsedAction,
674+
blockNum: blockNum,
675+
raw: ev.raw || {}
676+
});
677+
}
678+
}
679+
680+
var eventsCollected = [];
681+
for (var j = 0; j < order.length; j++) {
682+
var processed = grouped[order[j]];
683+
var stateEvents = StateEngine.processBlock(processed);
684+
for (var k = 0; k < stateEvents.length; k++) {
685+
eventsCollected.push(stateEvents[k]);
686+
}
687+
}
688+
689+
// Archive mode intentionally skips empty blocks. Advance the head so
690+
// stale checkpoints can catch up in seconds without replaying tens
691+
// of thousands of empty blocks through RPC.
692+
var state = StateEngine.getState();
693+
state.headBlock = endBlock;
694+
_finishProcessedBatch(endBlock, chainHead, eventsCollected);
695+
done(true);
696+
});
697+
}
698+
699+
function _processBlockBatchFromRpc(startBlock, endBlock, chainHead) {
614700
var eventsCollected = [];
615701

616702
BlockProcessor.processBlockRange(startBlock, endBlock, function(processed, blockNum) {
@@ -620,36 +706,40 @@ var App = (function() {
620706
eventsCollected.push(events[i]);
621707
}
622708
}, function(err) {
623-
_lastPolledBlock = endBlock;
709+
_finishProcessedBatch(endBlock, chainHead, eventsCollected);
710+
});
711+
}
624712

625-
// Emit game events to EventBus for UI reactivity
626-
for (var i = 0; i < eventsCollected.length; i++) {
627-
var evt = eventsCollected[i];
628-
if (evt.type) {
629-
Helpers.EventBus.emit(evt.type, evt);
630-
}
713+
function _finishProcessedBatch(endBlock, chainHead, eventsCollected) {
714+
_lastPolledBlock = endBlock;
715+
716+
// Emit game events to EventBus for UI reactivity
717+
for (var i = 0; i < eventsCollected.length; i++) {
718+
var evt = eventsCollected[i];
719+
if (evt.type) {
720+
Helpers.EventBus.emit(evt.type, evt);
631721
}
722+
}
632723

633-
// Save checkpoint periodically (every batch)
634-
StateEngine.saveCheckpoint(function(cpErr) {
635-
if (cpErr) {
636-
console.log('App: Checkpoint save error:', cpErr);
637-
}
724+
// Save checkpoint periodically (every batch)
725+
StateEngine.saveCheckpoint(function(cpErr) {
726+
if (cpErr) {
727+
console.log('App: Checkpoint save error:', cpErr);
728+
}
638729

639-
_updateSyncStatus(_calculateSyncPercent(endBlock, chainHead));
640-
_refreshActiveScreenAfterSync(eventsCollected);
730+
_updateSyncStatus(_calculateSyncPercent(endBlock, chainHead));
731+
_refreshActiveScreenAfterSync(eventsCollected);
641732

642-
// If there are more blocks to process, continue immediately
643-
if (endBlock < chainHead) {
644-
var nextStart = endBlock + 1;
645-
var nextEnd = _nextCatchupBatchEnd(nextStart, chainHead);
646-
_processBlockBatch(nextStart, nextEnd, chainHead);
647-
} else {
648-
_syncStartBlock = 0;
649-
_updateSyncStatus(100);
650-
_pollBusy = false;
651-
}
652-
});
733+
// If there are more blocks to process, continue immediately
734+
if (endBlock < chainHead) {
735+
var nextStart = endBlock + 1;
736+
var nextEnd = _nextCatchupBatchEnd(nextStart, chainHead);
737+
_processBlockBatch(nextStart, nextEnd, chainHead);
738+
} else {
739+
_syncStartBlock = 0;
740+
_updateSyncStatus(100);
741+
_pollBusy = false;
742+
}
653743
});
654744
}
655745

@@ -672,6 +762,7 @@ var App = (function() {
672762
map: true,
673763
chronicle: true,
674764
guild: true,
765+
arena: true,
675766
marketplace: true,
676767
leaderboard: true
677768
};

app/sw.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Viz Magic — Service Worker
2-
var CACHE_NAME = 'viz-magic-v15';
2+
var CACHE_NAME = 'viz-magic-v16';
33
var ASSETS = [
44
'/',
55
'/index.html',

tests/player-bug-regressions.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ test('stale checkpoint catch-up keeps using scaled batches after first batch', f
128128
assert.ok(/var nextEnd = _nextCatchupBatchEnd\(nextStart, chainHead\)/.test(appJs), 'continued catch-up should not fall back to fixed 10-block batches');
129129
});
130130

131+
test('large stale checkpoint catch-up uses archive events instead of replaying empty blocks', function () {
132+
assert.ok(/function _processArchiveEventBatch\(startBlock, endBlock, chainHead, done\)/.test(appJs), 'app should have archive event catch-up path');
133+
assert.ok(/HistorySource\.getEventsRange/.test(appJs), 'archive catch-up should query event ranges');
134+
assert.ok(/state\.headBlock = endBlock/.test(appJs), 'archive catch-up should advance checkpoint past empty blocks');
135+
assert.ok(/arena: true/.test(appJs), 'arena should refresh when duel events arrive during catch-up');
136+
});
137+
131138
test('guild joining explains and enforces preparation requirements', function () {
132139
assert.ok(/var GUILD_JOIN_MIN_LEVEL = 4/.test(guildJs), 'guild join level gate should be explicit');
133140
assert.ok(/guild_join_requirements/.test(guildJs), 'guild screen should explain join requirements');

0 commit comments

Comments
 (0)