Summary
Prints are used in Verse to debug our code, they are only useful in test sessions where creators can see them.
They are invisible in private/public version but still impact servers performance. This is a huge issue as some of the maps use prints even every server tick or print big blocks of logs on certain actions. So far I haven’t seen anyone being aware of this issue.
Please select what you are reporting on:
Verse
What Type of Bug are you experiencing?
Stability
Steps to Reproduce
Create verse device and place it in a map:
hello_world_device := class(creative_device):
OnBegin<override>()<suspends>:void=
loop:
for(X:=0..5000):
Print("test")
Sleep(0.0)
Create private or even public version of the map.
Join the map, try to move and see how slow and laggy the game is.
As a comparison, comment out the Print line to see that the loop itself doesn’t impact performance.
Expected Result
No optimization drop in private and public version. Print calls should be completely stripped from final builds.
Observed Result
Lags in private/public versions.
Platform(s)
All
Island Code
7693-8137-7027 (click the button device to see fps drops)
Upload an image
A bit more complex example from the map to make sure empty loop wasnt stripped:
using. /Verse.org/Simulation
using. /Verse.org/Simulation/Tags
using. /Verse.org/Assets
using. /Verse.org/Verse
using. /Verse.org/Random
using. /Verse.org/Colors
using. /Verse.org/Colors/NamedColors
using. /Verse.org/Native
using. /Verse.org/Concurrency
using. /UnrealEngine.com/Temporary
using. /UnrealEngine.com/Temporary/UI
using. /UnrealEngine.com/Temporary/SpatialMath
using. /UnrealEngine.com/Temporary/Diagnostics
using. /UnrealEngine.com/Temporary/Curves
using. /Fortnite.com/UI
using. /Fortnite.com/Devices
using. /Fortnite.com/Devices/CreativeAnimation
using. /Fortnite.com/Devices/CreativeAnimation/InterpolationTypes
using. /Fortnite.com/Vehicles
using. /Fortnite.com/Teams
using. /Fortnite.com/Playspaces
using. /Fortnite.com/Game
using. /Fortnite.com/FortPlayerUtilities
using. /Fortnite.com/Characters
using. /Fortnite.com/AI
hello_world_device := class(creative_device):
@editable Button:button_device = button_device{}
OnBegin<override>()<suspends>:void=
Sleep(0.0)
loop:
Ag := Button.InteractedWithEvent.Await()
spawn. tick_rate_debug{}.InitPlayerAsync(Ag, event(){})
var Sum:int= 0
loop:
race:
Button.InteractedWithEvent.Await()
loop:
for(X:=0..5000):
Print("test")
set Sum += AddInt(1,2)
Print("Sum: {Sum}")
set Sum = 0
Sleep(0.0)
Button.InteractedWithEvent.Await()
race:
Button.InteractedWithEvent.Await()
loop:
for(X:=0..5000):
set Sum += AddInt(1,2)
Print("Sum: {Sum}")
set Sum = 0
Sleep(0.0)
AddInt(A:int, B:int):int=
A + B
tick_rate_debug<public> := class():
TickRateText<localizes><private>(TickRateString : string) : message = {"{TickRateString} TPS"}
TickRateTextBlock<private> : text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultJustification := text_justification.Right
var AverageTickRate : float = 0.0
GraphMaker<private> : graph_maker = graph_maker {NumBars := 30}
Canvas<private> : canvas = canvas{}
InitPlayerAsync(FirstPlayer:agent, PlayerRemoved:event())<suspends>:void=
GraphMaker.Init()
LabelSlot := canvas_slot:
Anchors := anchors:
Minimum := vector2{X := 1.0, Y := 1.0}
Maximum := vector2{X := 1.0, Y := 1.0}
Alignment := vector2{X := 1.0, Y := 1.0 }
Offsets := margin{Left := -195.0, Top := -8.0}
Widget := TickRateTextBlock
GraphSlot := canvas_slot:
Anchors := anchors:
Minimum := vector2{X := 1.0, Y := 1.0}
Maximum := vector2{X := 1.0, Y := 1.0}
Alignment := vector2{X := 1.0, Y := 1.0 }
Offsets := margin {Left := -10.0, Top := -10.0, Right := 170.0, Bottom := 40.0}
SizeToContent := false
Widget := GraphMaker.Canvas
Canvas.AddWidget(LabelSlot)
Canvas.AddWidget(GraphSlot)
if:
PlayerUI := GetPlayerUI[player[FirstPlayer]]
then:
PlayerUI.AddWidget(Canvas)
var PreviousTime : float = GetSimulationElapsedTime()
loop:
Sleep(0.0)
CurrentTime := GetSimulationElapsedTime()
DeltaTime := CurrentTime - PreviousTime
set PreviousTime = CurrentTime
TickRate := 1.0 / DeltaTime
#TickRate := GetRandomFloat(0.0,30.0) # use this to test the graph
set AverageTickRate += (TickRate - AverageTickRate) / 3.0 # smooth it out so the text doesn't flicker distractingly as much
TickRateTextBlock.SetText(TickRateText(GetRoundedString(AverageTickRate,2)))
BarPercent := TickRate/30.0
GraphMaker.PushValue(BarPercent)
GetRoundedString<private>(Input : float, NumDigitsAfterDecimal : int) : string =
if:
Multiplier := Pow(10.0, NumDigitsAfterDecimal * 1.0)
RoundedValue := float[Round[Input*Multiplier]*1.0] / Multiplier
BeforeDecimal := Floor[RoundedValue]
AfterDecimal := Abs(Round[(RoundedValue - BeforeDecimal*1.0)*Multiplier])
var AfterDecimalString : string = ToString(AfterDecimal)
#pad the number after the decimal with leading zeroes
for (It := 0..(NumDigitsAfterDecimal-AfterDecimalString.Length-1)):
set AfterDecimalString = array{'0'} + AfterDecimalString
then:
"{BeforeDecimal}.{AfterDecimalString}"
else:
ToString(Input)
graph_maker := class():
NumBars : int
Canvas : canvas = canvas{}
var ColorBlocks<private> : []color_block = array{}
var Values : []float = array{}
BackgroundSlot : canvas_slot = canvas_slot:
Widget := color_block:
DefaultColor := NamedColors.White
DefaultOpacity := 0.3
Anchors := anchors:
Minimum := vector2{X := 0.0, Y := 0.0}
Maximum := vector2{X := 1.0, Y := 1.0}
Init() : void =
Canvas.AddWidget(BackgroundSlot)
for(B := 0..NumBars-1):
set Values += array {0.0} #default the bar to all zeroes
set ColorBlocks += array:
ColorBlock : color_block = color_block:
DefaultColor := NamedColors.White
DefaultOpacity := 0.8
RedrawAllBars()
# shift all the values down by one and then replace the last value
PushValue(Value : float) : void =
for:
B := 0..NumBars-2
Val := Values[B+1]
set Values[B] = Val
do:
RedrawBar(B)
if(set Values[NumBars-1] = Value) then RedrawBar(NumBars-1)
SetValue(Index : int, Value : float) : void =
if (set Values[Index] = Value) then RedrawBar(Index)
RedrawAllBars() : void =
for (B := 0..NumBars-1) do RedrawBar(B)
RedrawBar(Index : int): void =
BarWidth := 1.0 / (1.0*NumBars)
if:
ColorBlock := ColorBlocks[Index]
Value := Values[Index]
then:
# we do Value*Value for blue so that the transition color tends to be more orange
ColorBlock.SetColor(color {R := 1.0, G := Lerp(0.0,1.0,Value), B := Lerp(0.0,1.0,Value*Value)})
# we create a new slot each time because they are immutable and we need to change the anchors
# and also they are structs so it's a pretty lightweight operation anyway
NewSlot := canvas_slot:
Widget := ColorBlock
Anchors := anchors:
Minimum := vector2{X := Index*BarWidth + 0.0, Y := 1.0-Clamp(Value,0.0,1.0)}
Maximum := vector2{X := Index*BarWidth + BarWidth, Y := 1.0}
SizeToContent := false
# we need to add a slot, but being re-added also forces the ColorBlock color to actually update
# the first time we run this, the widget won't actually be on the canvas, but that's fine, RemoveWidget() can't fail
Canvas.RemoveWidget(ColorBlock)
Canvas.AddWidget(NewSlot)
