Page MenuHomeWildfire Games

Fix lobby chat performance issue.
ClosedPublic

Authored by wraitii on Feb 23 2019, 4:18 AM.

Details

Reviewers
vladislavbelov
nani
Group Reviewers
Restricted Owners Package(Owns No Changed Paths)
Restricted Owners Package(Owns No Changed Paths)
Commits
rP24306: Fix Lobby/MP Gamesetup chat lag with many messages.
Summary

New chat text is added as a copy of all previous chat and new one, use CList so it is not. Also adds a handy JS Interface addItem method to add new comments efficiently.
With this fix you can add a new message every onTick for 5000 ticks and not notice any performance hit (not like the current one that at 500 lines starts to drop fps).


Note that only List gets the additional JS function, even though COList inherits from it, because COList doesn't implement the clever addText setup.

Test Plan

Check text displays correctly when added.

Diff Detail

Lint
Lint Skipped
Unit
Unit Tests Skipped

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes

This is extremely non-trivial to fix in CText, as Gui::GenerateText supports wrapping around images. If we break down text in substrings, we will break this behaviour.

CText inherits GUITextOwner that stores a vector of texts and provides an AddText function, that's insurmountably incompatible?

source/gui/CList.cpp
90

That's much better than before! Even cleaner would be registering it as a custom JSClass and registering the variable the way it should be directly instead of late on first JSObject* CList::GetJSObject() call and then checking that for initialized every call.

In D1781#89177, @elexis wrote:

This is extremely non-trivial to fix in CText, as Gui::GenerateText supports wrapping around images. If we break down text in substrings, we will break this behaviour.

CText inherits GUITextOwner that stores a vector of texts and provides an AddText function, that's insurmountably incompatible?

I think I was trying a little too hard there perhaps. My point was that if you take a paragraph with images, and split that in several text elements (to enable inserting in the middle etc.) then you have a serious problem.

If you let the user add different paragraphs and treat each as its own text bit, then it works better and it sounds doable.

source/gui/CList.cpp
90

I originally did not want to use a different JSClass, but I suppose we don't actually care that our elements are different classes, so it could be done that way.

So the JS registration seems unimprovable i.e. ideal, beside the cast.

source/gui/CList.cpp
79

(unneeded variable initialised)

82

(If they are part of the namespace (like with the other JSInterface GUI class specs), then one doesn'T have to modify GetJSObject in order to register a new function, but can do it independently.)

90

Actually cant do it on init but have to refer to first use, since that was a design choice, from its definition in IGUIObject.cpp:

Cache the object when somebody first asks for it, because otherwise
we end up doing far too much object allocation.

source/gui/IGUIObject.h
503

righteous to make them protected, let the inheriting classes access, but there is no reason to make it public yet

source/gui/scripting/JSInterface_CList.cpp
32 ↗(On Diff #8426)

dynamic_cast(static_cast()) worked for me without the DerivedPointer in D2136. Also matches the theory that dynamic_casts are used for polymorphism?

source/gui/scripting/JSInterface_CList.h
25 ↗(On Diff #8426)

ok

elexis added inline comments.Jul 30 2019, 8:10 PM
source/gui/scripting/JSInterface_CList.cpp
32 ↗(On Diff #8426)

A greater problem with the GetDerivedPointer approach is that it won't work if there is a class that inherits two classes that inherit IGUIObject, because this statement is calling GetDerivedPointer from the base class, so it will only return one of the two classes.

Consider for example class CText : public IGUIScrollBarOwner, public IGUITextOwner or class CList : public IGUIScrollBarOwner, public IGUITextOwner. If there is a JSInterface for the ScrollBar, TextOwner (D2136) then one of the two sets of registered functions should break.

elexis retitled this revision from Fix chat performance issue. to Fix lobby chat performance issue..Sep 10 2019, 9:21 PM

Other than the problem that the entire chattext is rebuilt instead of partially rebuilt (only appended line built), there is another effect, similar to the one in #3386:
If there are N different GUI messages in the queue (that will then be pulled on the same onTick), there will be N updates to that GUIObject, but the GUI object only needs to be updated once per frame.
With this patch, only the N addList consecutive entries are built, so that's good.

I still think however that this thing should not become a CList but a CInput with selectable readonly and tag-parsed text.
I had started writing the patch in P167, but it's not so easy.
This feature would also be used for the Intro.txt Manual page, and for the Terms of Service page.

We can still add the CList patch, but it still means that we have to rewrite the entire feature from scratch if we want to serve the use case of selecting the colorized text with partial text rebuilds.

wraitii planned changes to this revision.EditedMay 24 2020, 9:54 AM

A look at this with a pair of fresh eyes and some better understanding of JS and SpiderMonkey (I think).
Recap: how our GUI works:

  • We have C++ objects, inheriting 1+ GUI classes. Most are children of IGUIObjects, though some (Scrollbar, TextOwner) are "capabilities" that are added by composition (-ish, they're inherited but those classes keep a reference to the GUI object instead of calling this). That's since D2325, following itself D2136. Fundamentally, that was a change towards Composition, which is preferred here I think. Anyways, irrelevant here.
  • We have JS objects. JS objects can't inherit multiple prototypes. Thus, we can only have on JSCLass per C++ type _if_ composition over inheritance is the rule. Even with the rework above, I don't think we really fit the mold.
  • The JS objects have a custom JSClass, with a custom prototype and a getter/setter hook (which gets moved around in SM52, but that's fine).

So we have three code-paths to get C++ values from JS:

  • Settings, such as .caption. These are not defined on the JS object, but instead the getProperty hook returns the value from the C++ object directly. As such, they are of course not enumerable.
  • Class Functions, such as .focus and .blur, are defined directly on the prototype via JS_InitClass.
  • Custom functions (.getTextSize), since D2136, are defined directly on the object, as properties.

In SM parlance, our objects are NONNATIVE objects, i.e. they really have no JS-only values (in fact, you can't set any property from JS). We must implement manually, in C++, all functionality.
The approach of a custom JSCLass for this, like we do, is recommended against in Modern SM, starting with SM52 I think, in favour of using a Proxy Object.
In the comments for Proxy.h on modern SM, you see this:

* Native objects with custom JSClassOps / ObjectOps are used when the object
* behaves very similar to a normal object such as the ArrayObject and it's
* length property. Most usages wrapping a C++ or other type should prefer
* using a Proxy. Using the proxy approach makes it much easier to create an
* ECMAScript and JIT compatible object, particularly if using an appropriate
* base class.

This is valuable because if somebody were to rework this, we'd probably want to implement a Proxy object instead.

Anyways. What we want, from a C++ perspective, is to be able to add custom properties (not values or functions) to GUI objects that define a C++ Object. Define some of those only on those objects (eg here "addText" only for List objects).
To me, the simplest solution for that is the following:

  • Each C++ Object-Class defines a JSI_XX class. That class is a Proxy.
  • When a GUIObject's script is requested, we create a new proxy instance.

What this means:

  • The actual GUIObjects are very thin wrappers -> save on memory, we possibly might even skip the lazy initialisation scheme.
  • We can easily define arbitrary functions and values in c++ by calling "RegisterScriptFunctions" and "RegisterScriptSettings" when initially creating the prototype. This supports composition, with the assumption that composited-objects won't accidentally overwrite properties (doesn't seem like a big problem).

This design does not require adding more and more exceptions to the "GetSetting" property hook, since we'll simply follow the prototype chain, and it works with c++ virtual functions : define a "GetPrototype" function and IGUIObject will do the rest.

With that being said, I will proceed to work on this. Ping @Itms because this is related to SM52. Ping @elexis because this is of interest to you.

wraitii updated this revision to Diff 12012.May 25 2020, 8:35 PM

Same fix, rebased on top of D2768 and the gamesetup rewrite.

I haven't been able to figure out what the lobby Flushmessages did, so I moved the call.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/2229/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/vs2015-differential/1700/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/827/display/redirect

wraitii updated this revision to Diff 12020.May 26 2020, 8:58 AM
wraitii edited the summary of this revision. (Show Details)

Slightly cleaner code with D2768 improvements.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/2235/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/833/display/redirect

wraitii added inline comments.May 26 2020, 9:01 AM
source/gui/Scripting/JSInterface_IGUIObject.h
160 ↗(On Diff #12020)

Er, forgot to add the correct using here.

wraitii added inline comments.May 26 2020, 9:04 AM
source/gui/Scripting/JSInterface_IGUIObject.h
160 ↗(On Diff #12020)

This isn't detected at compile time because I'm using the actual return type of the function pointer in the template code, as the using isn't 'virtual'...

I think I can fix this with CRTP, but it might be a tad tricky.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/vs2015-differential/1706/display/redirect

wraitii updated this revision to Diff 14111.Nov 20 2020, 4:55 PM

Rebased on top of D2768.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/3707/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/2052/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/vs2015-differential/3153/display/redirect

Stan added inline comments.Nov 21 2020, 9:59 AM
source/gui/Scripting/JSInterface_CList.cpp
36 ↗(On Diff #14111)

Better name? maybe SCListData, SListData?

wraitii added inline comments.Nov 22 2020, 2:44 PM
source/gui/Scripting/JSInterface_CList.cpp
36 ↗(On Diff #14111)

it's anonymous in all 3 files and IMO not necessary. Further, I think this can get cleaned up in C++17 (or maybe I'll macro bits out then)

wraitii updated this revision to Diff 14144.Nov 22 2020, 2:46 PM

Rebased for CI. Will commit soon.

Successful build - Chance fights ever on the side of the prudent.

builderr-debug-macos.txt
../../../source/simulation2/scripting/JSInterface_Simulation.cpp:154:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        CFixedVector2D(-halfSize.X, -halfSize.Y),
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
In file included from ../../../source/graphics/tests/test_Camera.cpp:17:
/Users/wfg/Jenkins/workspace/macos-differential/source/graphics/tests/test_Camera.h:168:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        CVector3D(-101.0f, -101.0f, 101.0f),
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
In file included from ../../../source/simulation2/tests/test_SerializeTemplates.cpp:17:
/Users/wfg/Jenkins/workspace/macos-differential/source/simulation2/tests/test_SerializeTemplates.h:39:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        3, 0, 1, 4, 1, 5
                        ^~~~~~~~~~~~~~~~
                        {               }
1 warning generated.
builderr-release-macos.txt
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libnetwork.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/liblobby.a(precompiled.o) has no symbols
../../../source/simulation2/scripting/JSInterface_Simulation.cpp:154:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        CFixedVector2D(-halfSize.X, -halfSize.Y),
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libsimulation2.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libscriptinterface.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libengine.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libgraphics.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libatlas.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/libgui.a(precompiled.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/liblowlevel.a(dbghelp.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/liblowlevel.a(file_stats.o) has no symbols
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../../binaries/system/liblowlevel.a(vfs_path.o) has no symbols
In file included from ../../../source/graphics/tests/test_Camera.cpp:17:
/Users/wfg/Jenkins/workspace/macos-differential/source/graphics/tests/test_Camera.h:168:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        CVector3D(-101.0f, -101.0f, 101.0f),
                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
In file included from ../../../source/simulation2/tests/test_SerializeTemplates.cpp:17:
/Users/wfg/Jenkins/workspace/macos-differential/source/simulation2/tests/test_SerializeTemplates.h:39:4: warning: suggest braces around initialization of subobject [-Wmissing-braces]
                        3, 0, 1, 4, 1, 5
                        ^~~~~~~~~~~~~~~~
                        {               }
1 warning generated.

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/2077/display/redirect

Stan added inline comments.Nov 22 2020, 4:32 PM
source/gui/ObjectTypes/CList.h
72 ↗(On Diff #14144)

We don't support optionnal parameters?

appendMode kind of suggest it is some kind of enum, maybe just append?

source/gui/Scripting/JSInterface_CList.cpp
75 ↗(On Diff #14144)

Why is this one different from all the others? Shouldn't the function be called addItem for consistency?

Stan added inline comments.Nov 26 2020, 11:37 PM
source/gui/GUIObjectTypes.cpp
1 ↗(On Diff #14144)

Copyright

source/gui/ObjectTypes/CList.cpp
1 ↗(On Diff #14144)

Copyright

source/gui/ObjectTypes/CList.h
1 ↗(On Diff #14144)

Copyright

source/gui/ObjectTypes/COList.cpp
1 ↗(On Diff #14144)

Copyright header.

wraitii added inline comments.Nov 27 2020, 8:39 AM
source/gui/ObjectTypes/CList.h
72 ↗(On Diff #14144)

I kinda like the overload paradigm for this.

source/gui/Scripting/JSInterface_CList.cpp
75 ↗(On Diff #14144)

Can't, the C++ "addItem" functions can't be called from JS directly (at least before D2818).

Stan added inline comments.Nov 27 2020, 9:23 AM
binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatMessagesPanel.js
27 ↗(On Diff #14144)

I guess it can't be called append?

source/gui/ObjectTypes/CList.cpp
122 ↗(On Diff #14144)

Can't you add the ternary to the above check ?

And use a range loop afterwards ?
Actually you check thrice for appendmode

410 ↗(On Diff #14144)

Duplication with COList ?

wraitii added inline comments.Nov 27 2020, 9:30 AM
binaries/data/mods/public/gui/lobby/LobbyPage/Chat/ChatMessagesPanel.js
27 ↗(On Diff #14144)

It can, but I preferred to re-use the C++ name. I'm not sure it's obviously better.

source/gui/ObjectTypes/CList.cpp
122 ↗(On Diff #14144)

I'm not sure what you mean. In "append" mode, I only want to generate the latest item, not the whole thing. A range-for loop will never allow that.

410 ↗(On Diff #14144)

yeah, but not easily avoidable

Stan added inline comments.Nov 27 2020, 9:33 AM
source/gui/ObjectTypes/CList.cpp
122 ↗(On Diff #14144)

Right but you still check for that boolean three times :)

410 ↗(On Diff #14144)

Mmhh

wraitii added inline comments.Nov 30 2020, 3:49 PM
source/gui/ObjectTypes/CList.cpp
122 ↗(On Diff #14144)

Think I'm going to call that a feature, I think it makes the code more readable.

wraitii updated this revision to Diff 14299.Nov 30 2020, 3:50 PM

Rebased.

Build failure - The Moirai have given mortals hearts that can endure.

Link to build: https://jenkins.wildfiregames.com/job/vs2015-differential/3295/display/redirect

Stan added a comment.Nov 30 2020, 4:04 PM

Is it still extremely non trivial to use CTEXT instead, it's a bit weird to use "list" in the GUI when one means text?

In D1781#139501, @Stan wrote:

Is it still extremely non trivial to use CTEXT instead

Yes, nothing's changed on that front and this is still essentially @nani's original fix.

it's a bit weird to use "list" in the GUI when one means text?

it's really not that weird IMO. The chat messages are a list of sentences more than a cohesive text paragraph, which CText was intended for.

Build failure - The Moirai have given mortals hearts that can endure.

builderr-debug-macos.txt
In file included from ../../../source/ps/XML/XeroXMB.cpp:20:
In file included from ../../../source/ps/XML/Xeromyces.h:33:
../../../source/ps/XML/XeroXMB.h:214:15: warning: private field 'm_Pointer' is not used [-Wunused-private-field]
                const char* m_Pointer;
                            ^
../../../source/ps/XML/XeroXMB.h:283:15: warning: private field 'm_Pointer' is not used [-Wunused-private-field]
                const char* m_Pointer;
                            ^
2 warnings generated.
../../../source/gui/Scripting/JSInterface_CList.cpp:47:27: error: out-of-line definition of 'funcGetter' does not match any declaration in 'JSI_GUIProxy<CList>'
bool JSI_GUIProxy<CList>::funcGetter(CList* elem, const std::string& propName, JS::MutableHandleValue vp) const
                          ^~~~~~~~~~
1 error generated.
make[1]: *** [obj/gui_Debug/JSInterface_CList.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [gui] Error 2

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/2199/display/redirect

Build failure - The Moirai have given mortals hearts that can endure.

builderr-debug-gcc7.txt
../../../source/gui/Scripting/JSInterface_CList.cpp:47:6: error: template-id 'funcGetter<>' for 'bool JSI_GUIProxy<CList>::funcGetter(CList*, const string&, JS::MutableHandleValue) const' does not match any template declaration
 bool JSI_GUIProxy<CList>::funcGetter(CList* elem, const std::string& propName, JS::MutableHandleValue vp) const
      ^~~~~~~~~~~~~~~~~~~
../../../source/gui/Scripting/JSInterface_CList.cpp:47:107: note: saw 1 'template<>', need 2 for specializing a member function template
 bool JSI_GUIProxy<CList>::funcGetter(CList* elem, const std::string& propName, JS::MutableHandleValue vp) const
                                                                                                           ^~~~~
make[1]: *** [gui.make:271: obj/gui_Debug/JSInterface_CList.o] Error 1
make: *** [Makefile:137: gui] Error 2

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/3865/display/redirect

wraitii updated this revision to Diff 14316.Dec 1 2020, 9:43 AM

Fix the rebase, add scroll_bottom behaviour.

We _do_ have a lot of duplication between CText and CList... I feel like we should merge them together at some point, since the're relaly quite similar in a lot of aspects. Possibly make CText understand paragraphs or something. Images remain a pain point. ALternatively, we might want to make CText a lot more static, and CList be more like CDynamicText. Thoughts.

Anyways, this works.

Vulcan added a comment.Dec 1 2020, 9:58 AM

Build failure - The Moirai have given mortals hearts that can endure.

builderr-debug-macos.txt
ld: warning: text-based stub file /System/Library/Frameworks//CoreAudio.framework/CoreAudio.tbd and library file /System/Library/Frameworks//CoreAudio.framework/CoreAudio are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//AudioToolbox.framework/AudioToolbox.tbd and library file /System/Library/Frameworks//AudioToolbox.framework/AudioToolbox are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//ForceFeedback.framework/ForceFeedback.tbd and library file /System/Library/Frameworks//ForceFeedback.framework/ForceFeedback are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//CoreVideo.framework/CoreVideo.tbd and library file /System/Library/Frameworks//CoreVideo.framework/CoreVideo are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//Cocoa.framework/Cocoa.tbd and library file /System/Library/Frameworks//Cocoa.framework/Cocoa are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//IOKit.framework/IOKit.tbd and library file /System/Library/Frameworks//IOKit.framework/IOKit are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//QuartzCore.framework/QuartzCore.tbd and library file /System/Library/Frameworks//QuartzCore.framework/QuartzCore are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//Metal.framework/Metal.tbd and library file /System/Library/Frameworks//Metal.framework/Metal are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//OpenGL.framework/OpenGL.tbd and library file /System/Library/Frameworks//OpenGL.framework/OpenGL are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//Security.framework/Security.tbd and library file /System/Library/Frameworks//Security.framework/Security are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//OpenAL.framework/OpenAL.tbd and library file /System/Library/Frameworks//OpenAL.framework/OpenAL are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//ApplicationServices.framework/ApplicationServices.tbd and library file /System/Library/Frameworks//ApplicationServices.framework/ApplicationServices are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//Cocoa.framework/Cocoa.tbd and library file /System/Library/Frameworks//Cocoa.framework/Cocoa are out of sync. Falling back to library file for linking.
ld: warning: text-based stub file /System/Library/Frameworks//CoreFoundation.framework/CoreFoundation.tbd and library file /System/Library/Frameworks//CoreFoundation.framework/CoreFoundation are out of sync. Falling back to library file for linking.
ld: library not found for -lbc6h
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[1]: *** [../../../binaries/system/pyrogenesis_dbg] Error 1
make: *** [pyrogenesis] Error 2

Link to build: https://jenkins.wildfiregames.com/job/macos-differential/2213/display/redirect

This revision was not accepted when it landed; it landed in state Needs Review.Dec 1 2020, 10:42 AM
This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.