mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-02-17 08:50:58 -05:00
Many improvements to real-time streaming and flags.
This commit is contained in:
parent
2dca4d72fc
commit
be07f9cfb9
5 changed files with 54 additions and 35 deletions
|
@ -460,7 +460,7 @@ self.finalizePreload = function finalizePreload() {
|
|||
};
|
||||
|
||||
self.addFlagEvent = function addFlagEvent(flagEvent) {
|
||||
if(!self.world || self.world.framesSerializedSoFar == self.world.frames.length) return;
|
||||
if(!self.world) return;
|
||||
self.world.addFlagEvent(flagEvent);
|
||||
};
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
'camera:zoom-updated': 'onZoomUpdated'
|
||||
'playback:real-time-playback-started': 'onRealTimePlaybackStarted'
|
||||
'playback:real-time-playback-ended': 'onRealTimePlaybackEnded'
|
||||
#'god:world-load-progress-changed': -> console.log 'it is actually', @world.age
|
||||
|
||||
shortcuts:
|
||||
'ctrl+\\, ⌘+\\': 'onToggleDebug'
|
||||
|
@ -123,7 +124,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@showLevel()
|
||||
@updateState true if @loaded
|
||||
# TODO: synchronize both ways of choosing whether to show coords (@world via UI System or @options via World Select modal)
|
||||
if @world.showCoordinates and @options.coords
|
||||
if @world.showCoordinates and @options.coords and not @coordinateDisplay
|
||||
@coordinateDisplay = new CoordinateDisplay camera: @camera
|
||||
@surfaceTextLayer.addChild @coordinateDisplay
|
||||
@onFrameChanged()
|
||||
|
@ -369,10 +370,19 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
|
||||
@setWorld event.world
|
||||
@onFrameChanged(true)
|
||||
fastForwardBuffer = 2 # Make sure that real-time playback doesn't need to buffer more than this many seconds.
|
||||
if @playing and (ffToFrame = Math.min(event.firstChangedFrame, @frameBeforeCast, event.world.frames.length)) and ffToFrame > @currentFrame + fastForwardBuffer * @world.frameRate
|
||||
fastForwardBuffer = 2
|
||||
if @playing and not @realTime and (ffToFrame = Math.min(event.firstChangedFrame, @frameBeforeCast, @world.frames.length)) and ffToFrame > @currentFrame + fastForwardBuffer * @world.frameRate
|
||||
@fastForwardingToFrame = ffToFrame
|
||||
@fastForwardingSpeed = Math.max 4, 4 * 90 / (@world.maxTotalFrames * @world.dt)
|
||||
else if @realTime
|
||||
lag = (@world.frames.length - 1) * @world.dt - @world.age
|
||||
intendedLag = @world.realTimeBufferMax + @world.dt
|
||||
if lag > intendedLag * 1.2
|
||||
@fastForwardingToFrame = @world.frames.length - @world.realTimeBufferMax * @world.frameRate
|
||||
@fastForwardingSpeed = lag / intendedLag
|
||||
else
|
||||
@fastForwardingToFrame = @fastForwardingSpeed = null
|
||||
#console.log "on new world, lag", lag, "intended lag", intendedLag, "fastForwardingToFrame", @fastForwardingToFrame, "speed", @fastForwardingSpeed, "cause we are at", @world.age, "of", @world.frames.length * @world.dt
|
||||
|
||||
# initialization
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ System = require 'lib/world/system'
|
|||
PROGRESS_UPDATE_INTERVAL = 100
|
||||
DESERIALIZATION_INTERVAL = 10
|
||||
REAL_TIME_BUFFER_MIN = 2 * PROGRESS_UPDATE_INTERVAL
|
||||
REAL_TIME_BUFFER_MAX = 5 * PROGRESS_UPDATE_INTERVAL
|
||||
REAL_TIME_BUFFER_MAX = 3 * PROGRESS_UPDATE_INTERVAL
|
||||
REAL_TIME_BUFFERED_WAIT_INTERVAL = 0.5 * PROGRESS_UPDATE_INTERVAL
|
||||
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
|
||||
|
||||
module.exports = class World
|
||||
|
@ -25,6 +26,7 @@ module.exports = class World
|
|||
headless: false # Whether we are just simulating for goal states instead of all serialized results
|
||||
framesSerializedSoFar: 0
|
||||
apiProperties: ['age', 'dt']
|
||||
realTimeBufferMax: REAL_TIME_BUFFER_MAX / 1000
|
||||
constructor: (@userCodeMap, classMap) ->
|
||||
# classMap is needed for deserializing Worlds, Thangs, and other classes
|
||||
@classMap = classMap ? {Vector: Vector, Rectangle: Rectangle, Thang: Thang, Ellipse: Ellipse, LineSegment: LineSegment}
|
||||
|
@ -100,6 +102,35 @@ module.exports = class World
|
|||
frameToLoadUntil = @totalFrames
|
||||
i = @frames.length
|
||||
while i < frameToLoadUntil and i < @totalFrames
|
||||
t2 = now()
|
||||
if @realTime
|
||||
shouldUpdateProgress = @shouldUpdateRealTimePlayback t2
|
||||
shouldDelayRealTimeSimulation = not shouldUpdateProgress and @shouldDelayRealTimeSimulation t2
|
||||
else
|
||||
shouldUpdateProgress = t2 - t1 > PROGRESS_UPDATE_INTERVAL
|
||||
shouldDelayRealTimeSimulation = false
|
||||
if shouldUpdateProgress or shouldDelayRealTimeSimulation
|
||||
if shouldUpdateProgress
|
||||
@lastRealTimeUpdate = i * @dt if @realTime
|
||||
#console.log 'we think it is now', (t2 - @worldLoadStartTime) / 1000, 'so delivering', @lastRealTimeUpdate
|
||||
loadProgressCallback? i / @totalFrames unless @preloading
|
||||
t1 = t2
|
||||
if t2 - @t0 > 1000
|
||||
console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)' unless @realTime
|
||||
@t0 = t2
|
||||
continueFn = =>
|
||||
return if @destroyed
|
||||
if loadUntilFrame
|
||||
@loadFrames(loadedCallback,errorCallback,loadProgressCallback, skipDeferredLoading, loadUntilFrame)
|
||||
else
|
||||
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading)
|
||||
if skipDeferredLoading
|
||||
continueFn()
|
||||
else
|
||||
delay = if shouldDelayRealTimeSimulation then REAL_TIME_BUFFERED_WAIT_INTERVAL else 0
|
||||
setTimeout(continueFn, delay)
|
||||
return
|
||||
|
||||
if @debugging
|
||||
for thang in @thangs when thang.isProgrammable
|
||||
userCode = @userCodeMap[thang.id] ? {}
|
||||
|
@ -116,33 +147,7 @@ module.exports = class World
|
|||
for error in (@unhandledRuntimeErrors ? [])
|
||||
return unless errorCallback error # errorCallback tells us whether the error is recoverable
|
||||
@unhandledRuntimeErrors = []
|
||||
t2 = now()
|
||||
if @realTime
|
||||
shouldUpdateProgress = @shouldUpdateRealTimePlayback t2
|
||||
shouldDelayRealTimeSimulation = not shouldUpdateProgress and @shouldDelayRealTimeSimulation t2
|
||||
else
|
||||
shouldUpdateProgress = t2 - t1 > PROGRESS_UPDATE_INTERVAL
|
||||
shouldDelayRealTimeSimulation = false
|
||||
if shouldUpdateProgress or shouldDelayRealTimeSimulation
|
||||
if shouldUpdateProgress
|
||||
@lastRealTimeUpdate = i * @dt if @realTime
|
||||
loadProgressCallback? i / @totalFrames unless @preloading
|
||||
t1 = t2
|
||||
if t2 - @t0 > 1000
|
||||
console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)'
|
||||
@t0 = t2
|
||||
continueFn = =>
|
||||
return if @destroyed
|
||||
if loadUntilFrame
|
||||
@loadFrames(loadedCallback,errorCallback,loadProgressCallback, skipDeferredLoading, loadUntilFrame)
|
||||
else
|
||||
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading)
|
||||
if skipDeferredLoading
|
||||
continueFn()
|
||||
else
|
||||
delay = if shouldDelayRealTimeSimulation then PROGRESS_UPDATE_INTERVAL else 0
|
||||
setTimeout(continueFn, delay)
|
||||
return
|
||||
|
||||
unless @debugging
|
||||
@ended = true
|
||||
system.finish @thangs for system in @systems
|
||||
|
@ -159,6 +164,8 @@ module.exports = class World
|
|||
|
||||
shouldUpdateRealTimePlayback: (t) ->
|
||||
return false unless @realTime
|
||||
return false if @frames.length * @dt is @lastRealTimeUpdate
|
||||
timeLoaded = @frames.length * @dt * 1000
|
||||
timeSinceStart = t - @worldLoadStartTime
|
||||
remainingBuffer = @lastRealTimeUpdate * 1000 - timeSinceStart
|
||||
remainingBuffer < REAL_TIME_BUFFER_MIN
|
||||
|
@ -475,7 +482,7 @@ module.exports = class World
|
|||
w.ended = true
|
||||
nFrames = endFrame - startFrame
|
||||
totalCPUTime = perf.t3 - perf.t0 + perf.framesCPUTime
|
||||
console.log 'Deserialization:', totalCPUTime.toFixed(0) + 'ms (' + (totalCPUTime / nFrames).toFixed(3) + 'ms per frame).', perf.batches, 'batches. Did', startFrame, 'to', endFrame, 'in', (perf.t4 - perf.t0).toFixed(0) + 'ms wall clock time.'
|
||||
#console.log 'Deserialization:', totalCPUTime.toFixed(0) + 'ms (' + (totalCPUTime / nFrames).toFixed(3) + 'ms per frame).', perf.batches, 'batches. Did', startFrame, 'to', endFrame, 'in', (perf.t4 - perf.t0).toFixed(0) + 'ms wall clock time.'
|
||||
if false
|
||||
console.log ' Deserializing--constructing new World:', (perf.t1 - perf.t0).toFixed(2) + 'ms'
|
||||
console.log ' Deserializing--Thangs and ScriptNotes:', (perf.t2 - perf.t1).toFixed(2) + 'ms'
|
||||
|
|
|
@ -48,7 +48,7 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
onStageMouseDown: (e) ->
|
||||
return unless @flagColor and @realTime
|
||||
pos = x: e.worldPos.x, y: e.worldPos.y
|
||||
flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: @world.dt * @world.frames.length + 1, active: true
|
||||
flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: @world.dt * @world.frames.length, active: true
|
||||
@flags[@flagColor] = flag
|
||||
@flagHistory.push flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
|
@ -57,7 +57,7 @@ module.exports = class LevelFlagsView extends CocoView
|
|||
removeFlag: (e) ->
|
||||
delete @flags[e.color]
|
||||
console.log e.color, 'deleted'
|
||||
flag = player: me.id, team: me.team, color: e.color, time: @world.dt * @world.frames.length + 1, active: false
|
||||
flag = player: me.id, team: me.team, color: e.color, time: @world.dt * @world.frames.length, active: false
|
||||
@flagHistory.push flag
|
||||
Backbone.Mediator.publish 'level:flag-updated', flag
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
@$el.find('#music-button').toggleClass('music-on', me.get('music'))
|
||||
|
||||
onSetLetterbox: (e) ->
|
||||
return if @realTime
|
||||
@togglePlaybackControls !e.on
|
||||
@disabled = e.on
|
||||
|
||||
|
@ -211,6 +212,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
$('#volume-button', @$el).removeClass('disabled')
|
||||
|
||||
onEnableControls: (e) ->
|
||||
return if @realTime
|
||||
if not e.controls or 'playback' in e.controls
|
||||
@disabled = false
|
||||
$('button', @$el).removeClass('disabled')
|
||||
|
|
Loading…
Reference in a new issue