You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3284469 on 2017/02/02 by Saul.Abreu Fixed comment on Slate Brush for getting the resource object and what kind of object it may be. Change 3284410 on 2017/02/02 by Ben.Zeigler Add FSlateBrush subclass constructors that take resource objects, to create static brushes at startup time that refer to already loaded UTextures Change 3284381 on 2017/02/02 by Saul.Abreu #fortnite Added functionality to common button to actually use the single material brush setting and automatically create and expose access to a material instance dynamic. Also fixed some potential object lifetime issues in CommonButton. Change 3282423 on 2017/02/01 by Ben.Zeigler Fix issue with synchronous load of objects not invalidating cached nulls for asset ptrs. This was always an issue but my recent change to assetptr SynchronousLoad made it manifest more often. In the future we may want to invalidate cached nulls on object creation to handle cases like save games Change 3282265 on 2017/02/01 by Ian.Fox #UE4, #XMPP - Reduce verbosity of strophe receive-stanza log Change 3282159 on 2017/02/01 by Ben.Zeigler Fix issue where async loading null string reference would deadlock Change 3282054 on 2017/02/01 by James.Longstreet #fortnite #jira FORT-28234 Fix sceImeDialogTerm() warning We were calling sceImeDialogTerm() in cases where the dialog wasn't active. The only state it's legitimate to call sceImeDialogTerm() in is SCE_IME_DIALOG_STATUS_FINISHED. Change 3282051 on 2017/02/01 by James.Longstreet #fortnite #jira FORT-30021 Respect bVirtualKeyboardSendsTextChanged on PS4 Change 3281799 on 2017/02/01 by Nick.Cooper #Fortnite - Removed code on PS4 that was adding a EMouseButtons::Left press when the right shoulder button was pressed #jira FORT-35821 Change 3281771 on 2017/02/01 by Lukasz.Furman fixed end of path conditions breaking movement when path starts with a navlink and turn back below #jira FORT-36375 Change 3280579 on 2017/01/31 by Ben.Zeigler Remove ForEachProperty and switch AssetManager to use TPropertyValueIterator instead. Code review changes to TPropertyValueIterator. Add InitializeAssetBundlesFromMetadata to AssetManager which parses AssetBundles metadata at load/save time and uses that to assign asset references to specific bundles. Add RecursivelyExpandBundleData to allow recursively acquiring bundle dependencies. Add AssetBundles to the PropertyMetadata list. Sorted list and fixed some comment typos. Change it so the DataAsset factory won't show blueprintable native classes as valid, the editor doesn't like it when you have both blueprinted and non-blueprinted assets of the same base class. Change AssetPtr.LoadSynchronous to be const and to behave the same as Get(). This was a licensee complaint and the old behavior to null out the reference because of a transient load error can cause data loss. Change 3280176 on 2017/01/31 by Lukasz.Furman fixed missing navmesh update on replacing existing floor/roof #jira FORT-36369 Change 3279761 on 2017/01/31 by Saul.Abreu #fortnite Added ability to keep common buttons interactable even when they're selected, so they can still be clicked. Old default behavior is preserved. Change 3279678 on 2017/01/31 by Saul.Abreu #fortnite Moved GetCurrentInputType from UCommonActionWidget to UCommonUIContext for reuse. Change 3278593 on 2017/01/30 by Ben.Zeigler Delay initialization of asset registry tag filter list until it is needed, to make sure it catches modules that were loaded after the asset registry module. Fixes issues with tags not inherting to child classes Change 3278592 on 2017/01/30 by Ben.Zeigler Add TPropertyValueIterator to replace ForEachProperty. This is a recursive iterator instead of a predicate search, and also allows extracting the property chain, which I need for metadata parsing. AssetManager has an example of both uses, will delete ForEachProperty tomorrow. Change 3277859 on 2017/01/30 by Lukasz.Furman fixed navigation path postprocessing not working with vertical navlinks near start point required by taker portal up move #jira FORT-36570 Change 3277580 on 2017/01/30 by Ben.Zeigler #jira FORT-36662 Return streamable manager to always use hard references, weak references do not work during AddReferencedObjects at all, because of the unreachable flag that is set during GC. This returns the behavior of forcing assets to be "Force Deleted" if they were loaded by asset manager. Change 3276903 on 2017/01/29 by Bob.Tellez #BlueprintContext Updated copyright notice for 2017 Change3276902on 2017/01/29 by Bob.Tellez #CommonUI Updated some missed copyright notices for 2017 Change 3276731 on 2017/01/29 by Jeff.Campeau Forward modified warning messages Change 3276642 on 2017/01/28 by Jeff.Campeau Change specific errors from packaging multiple configurations into a single package into warnings. Change 3276228 on 2017/01/27 by Ben.Zeigler Fix crash/data loss when a Pin's DefaultObject points to an ObjectRedirector. FixObject will return redirectors so you need to correct for that Change 3276073 on 2017/01/27 by Ben.Zeigler Add some utility functions to core asset manager and streamablemanager and fix various editor interactions. Add startup and cook interaction hooks to AssetManager directly and call them form Engine in addition to the game-specific hooks. Add concept of BulkScanning to AssetManager, use this when scanning many directories. Fix issues with string asset references being wrong for blueprint classes, this would be easier if the assetdata pointed to the class and not the possibly-missing blueprint. Change StreamableManager to keep referenced objects as weak pointers. These pointers are effectively soft in the editor, hard outside of the editor. By storing them as raw pointers it was difficult to guarantee their safety without causing issues with deleting assets. Change 3276058 on 2017/01/27 by Ben.Zeigler Add UStruct::ForEachProperty, which recursively iterates properties of a struct and calls a lambda. Add some new BaseStructure accessors Change 3275981 on 2017/01/27 by Alex.Thurman Move Item Management Screen input handling into child activatable panels. #JIRA FORT-35759 #JIRA FORT-35758 Change 3275626 on 2017/01/27 by John.Pollard First pass at implementing net relevancy for replays * All connections are considered when determing if an actor is relevant * Enable by setting demo.UseNetRelevancy to 1 * Override cull distance with demo.CullDistanceOverride Change 3275221 on 2017/01/27 by John.Pollard FORT-36482 - Fix issue with using wrong serializer for re-mapping objects Change 3274149 on 2017/01/26 by Alex.Thurman Expose the Common Widget Switcher as part of the common UI plugin library. #fort Change 3274103 on 2017/01/26 by Michael.Trepka Fixed a crash on startup on Mac when using a shader cache populated during cooking and re-enabled cook time cache generation in Fortnite Change 3273867 on 2017/01/26 by James.Hopkin Enabled Stomp for PS4 #jira FORT-35517 Change 3273749 on 2017/01/26 by James.Hopkin Added libwebsocket libraries for PS4 #jira FORT-35517 Change 3273105 on 2017/01/26 by James.Hopkin Moved libwebsockets.h into platform-specific folders (in preparation for PS4 building against slightly more recent version) Change 3273020 on 2017/01/26 by Jeff.Campeau Stage manifest files from loose folder, not binary folder (appdata.bin is no longer created in the binary directory) Change 3272825 on 2017/01/25 by Saad.Nader #fort Added function to set the auto activation on or off on a common widget switcher. Updated CommonTabListWidget to temporarily turn off activation when setting a linked switcher. Added some missing delegate cleanup code. Change 3272598 on 2017/01/25 by Justin.Augspurger #fortnite Add activatable panel function that returns if an input handler is set. Change 3272411 on 2017/01/25 by Michael.Trepka Increased g.TimeoutForBlockOnRenderFence on Mac to 5 minutes Change 3271913 on 2017/01/25 by Lukasz.Furman fixed conditions of movement's destinaiton oveshot check to work with setup in FTest maps #jira FORT-36375 Change 3271723 on 2017/01/25 by Bob.Tellez #UE4 Disabled MfMedia on windows. Change 3271223 on 2017/01/25 by Jeff.Campeau Allow packaging to fall back to the engine directory for the lastchunk file if it's not present in the staged version. Change 3271066 on 2017/01/24 by Chris.Gagnon - Properly clean up the UIManager and Analog Cursor when game/pie exists. - FortAnalogCursor now sequesters the mouse during InputSuspension. - Change the callback order for activate and deactivate so the classes can chain activate. Change3271064on 2017/01/24 by Chris.Gagnon - NavigateToWidget() added to SlateApplication - SlistView and it's descendants now have the ability to navigate to the widget scrolled into view Change 3270778 on 2017/01/24 by Michael.Trepka Fixed and enabled cook time binary shader cache generation for Mac Change 3270645 on 2017/01/24 by Jeff.Campeau - New manifest generation (backward compatible) - True support for multiconfig packages - Settings all based in target settings in editor - Localized package resources with support to reduce redundancies - Resource table generation and manifest generation combined - Further reduced unneeded deploy copies #jira FORT-36413 Change 3270191 on 2017/01/24 by Lukasz.Furman reverted file unrelated to DecoyDistance fix Change 3270133 on 2017/01/24 by Lukasz.Furman fixed item scoring in DecoyDistance test #jira FORT-36034 Change 3269363 on 2017/01/24 by James.Hopkin #online #stomp Added error logging for loss of heartbeat #jira FORT-34763 Change 3268921 on 2017/01/23 by Saul.Abreu #fortnite Renamed CommonWidgetGroup to emphasize that it is an abstract base class. Added ability to get the selected button out of CommonButtonGroup. Change 3268913 on 2017/01/23 by Saul.Abreu #fortnite Recreated some minor changes to UEnumProperty so that TMap properties don't assert with enums as keys. Change 3268436 on 2017/01/23 by Michael.Trepka Added rhi.Metal.AllowRHIThread to allow games to disable RHI thread on Mac to be able to use shader cache, which currently is incompatible with RHI thread. Disabled RHI thread on Mac in Fortnite. Also, temporarily disabled Metal validation layer in Fortnite until I have more information on the cost of various levels of validation. Change 3266945 on 2017/01/20 by Bob.Tellez #UE4 Allowing more configurations to generate debug symbols Change 3266814 on 2017/01/20 by Bob.Tellez #UE4 Moved MfMedia video track handling to the render thread, which removes the need for some buffer management in MediaTextureResource.cpp. Also, Mfmedia now emits the PlaybackEndReached event. #JIRA FORT-31753 Change 3266541 on 2017/01/20 by Lukasz.Furman moved navigation export of building actor's static mesh into owning actor data instead of using parent chain, attempt to fix husks passing through walls that lost navigation data at some point during game #jira FORT-35741 Change 3266269 on 2017/01/20 by Fred.Kimberley Change async loading of gameplay cues so that the gameplay cue manager maintains ownership of the assets and can control their lifetime. Change 3266053 on 2017/01/20 by Michael.Trepka Fixed issues with shader cache not working properly with Mac Metal (but it still requires -norhithread to work at all). Enabled the shader cache by default if RHI thread is disabled. Change 3265585 on 2017/01/20 by Bart.Hawthorne Enable Oodle in Fortnite Change 3264678 on 2017/01/19 by Lukasz.Furman fixed crash on opening behavior trees with invalid decorator class (empty redirectors) #ue4 Change 3264473 on 2017/01/19 by Fred.Kimberley Tell the streamable manager to manage gameplay cue assets that are async loaded. #jira FORT-35171 Change 3262846 on 2017/01/18 by John.Pollard FORT-30352 - Fix by lowering network logging verbosity for benign condition Change 3262535 on 2017/01/18 by Michael.Trepka Fix for FORT-35776 Make sure to set reasterizer state when rendering with a material in FSlateRHIRenderingPolicy::DrawElements Change 3262386 on 2017/01/18 by John.Pollard Deprecate bPendingNetUpdate, NetUpdateTime and LastNetUpdateTime Change 3262375 on 2017/01/18 by Ian.Fox #UE4, #XMPP - Handle Message stanza errors #JIRA OGS-505 Change 3262262 on 2017/01/18 by John.Pollard Turn on adaptive network updates by default Change 3262215 on 2017/01/18 by Rob.Cannaday Fix for returned XMPP messages (to invalid recipient) triggering on message received delegates Change 3262094 on 2017/01/18 by Jamie.Dale Cook on the fly builds now resolve string asset references Change 3262091 on 2017/01/18 by Jamie.Dale Guarding against potentially invalid call to FString::Mid Change 3262089 on 2017/01/18 by Jamie.Dale Fixing RedirectCollector issues with projects outside the UE4 directory It was storing relative paths, but MakeStandardFilename wouldn't make a relative path for anything outside of the UE4 directory. In addition to this, some code was testing filters using package style paths, so I converted all the code to use package style paths instead. Change 3261201 on 2017/01/17 by Ben.Zeigler Perf improvements to PackageName that improve cooked load times by around a second. These string functions get called very often and Split is very slow, especially backwards searching or case insensitive. Change 3261098 on 2017/01/17 by John.Pollard Fix for FORT-35711 - Edited buildings do not always replicate correctly We were removing the actor from the network object list too soon Change3260515on 2017/01/17 by John.Abercrombie Fix MoveTo task ending with success when it's interrupted - Default the task to an invalid status rather than assuming we are successful #jira FORT-35497 - Defender can pick up a weapon from far away as they get knocked DBNO Change 3260343 on 2017/01/17 by Lukasz.Furman fixed end of path conditions for crowd simulation when using string pulled path #jira FORT-35713 Change 3259419 on 2017/01/16 by John.Pollard Network actor list fixes: * Don't add add actor to network list if it will just immediately get removed * Remove destroyed actors from actor list on clients * Make sure actor Role is set to correct value before adding to network actor list Change 3259104 on 2017/01/16 by Michael.Trepka Change the default for rhi.Metal.RuntimeDebugLevel to 2, as 3 is too expensive for Development builds and disable METAL_DEBUG_OPTIONS in Test builds Change 3259017 on 2017/01/16 by Saad.Nader #fort Added a missing remove delegate handler when widget is destructed. Change 3258901 on 2017/01/16 by Saad.Nader #fort Added the ability to remove an input action from the list of actions we are listening for in an activatable panel. Change 3258844 on 2017/01/16 by Ryan.Rauschkolb #fortnite Fixed issue where UUMGSequencePlayer:Tick would broadcast OnAnimationFinished before the final frame of the animation plays Change 3258734 on 2017/01/16 by Michael.Trepka Fixed a crash on exit on Mac in FCocoaWindow's windowWillResize: #jira FORT-35720 Change 3258353 on 2017/01/16 by James.Hopkin #xmpp Fixed UserJid constructor to be constructed by value - same efficiency, less code and allows any combination of rvalues and lvalues. Change 3257640 on 2017/01/13 by Saul.Abreu #fortnite #jira FORT-35387 Item Quantity List widget, not yet complete. Minor tweak to widget factory (for pooling) to support player controllers as "outer"s. In progress refactor of list of resources given for a mulch operation, using the item quantity list widget. Change 3257310 on 2017/01/13 by Bob.Tellez #UE4 Default stack size for windows is now configurable. There is a different number for windows editor targets than non-editor targets. Change 3257094 on 2017/01/13 by John.Pollard Refactor network actor list management to be more efficient * Move dormancy list management to FNetworkObjectList * Optimize actor network dormancy by removing actors from the active list that are dormant on all connections * Removed NetUpdateTime on actor, and now use the NextUpdateTime on FNetworkObjectInfo (these values are more hot in the cache too) * We now early out of the consider logic faster when possible * Remove other misc unused network state/code and general cleanup Change 3255891 on 2017/01/12 by Chris.Gagnon Added "Back" action to squads screens and armory landing. Added activation widget centering for squads screens. Added a couple Explicit navigations to get a better navigation experience. Added a bunch of Fkeys to the input binding table. Added PanelButton Widget. Change 3254809 on 2017/01/11 by Bob.Tellez #UE4 Crash fix for shader views that get destroyed but still have pointers to them in the SRV cache. Change 3254651 on 2017/01/11 by Bob.Tellez #UE4 Changed MfMedia track sync mode to Unbuffered since buffered causes a crash shortly after playing. #JIRA FORT-35566 Change 3254307 on 2017/01/11 by Lukasz.Furman fixed "Ftest start" command interfering with automation passes #jira FORT-35459 Change 3253625 on 2017/01/11 by Lukasz.Furman more accurate overshot detection for crowd simulation trying to reach last path corner #jira FORT-35502 Change 3252864 on 2017/01/10 by Lina.Halper fix for invalid anim curve issue when duplicating curves. #jira: FORT-35151 Change 3252427 on 2017/01/10 by Ben.Zeigler #jira UE-40390 Fix crash saving blueprint with an inherited DataTable/CurveTable reference. Delta serialization meant that the necessary name wasn't in the name table, so adding it manually now. Copied from CL #3252418 Change 3252344 on 2017/01/10 by Lukasz.Furman added navmesh tile observation to hotspots now they will be able to reevaluate unreachable slots if nearby navmesh is updated (active only when more than half melee slots is unreachable) #jira FORT-35450 Change 3251644 on 2017/01/09 by Saul.Abreu #fortnite #jira FORT-35388 Refactored common input so that the actions hold the per-platform key mappings. A config file holds the mapping of individual keys to their per-platform display data (icon-only for now). ALL ENTRIES IN THE INPUT ACTION DATA TABLE ARE NOW MISSING THEIR KEYS. RE-ADD THEM. I did test that it works. Change 3251118 on 2017/01/09 by David.Hamm Corrected ability system logging messages that are turning up in bug reports. Change 3250932 on 2017/01/09 by Bob.Tellez #UE4 Unshelved from DanielW. Fix for memory usage during map save for large maps Change 3250093 on 2017/01/06 by Jeff.Campeau libstrophe UE4 modifications Change 3249787 on 2017/01/06 by John.Pollard Add some replay/network stats Change 3248808 on 2017/01/05 by Chris.Gagnon Fix for ensuring Main Tabs properly activates it's content. Includes a pretty hacky delay, will need to deal with that at somepoint. Change 3248693 on 2017/01/05 by Chris.Gagnon NavigationEvent now gets populated with the modifier keys so that Shift and Ctrl behaviors of the list work. Change 3248647 on 2017/01/05 by Saul.Abreu Fixed shadowed variable warning in Create Event node. Change 3248358 on 2017/01/05 by Saul.Abreu Added return type/outputs to the signature displayed in the CreateEvent node. Also added tooltip describing the syntax for display since it's non-standard. Change 3247937 on 2017/01/05 by Chris.Gagnon - Refactored the Custom Navigation Event to be a part of the Viewport so that it functions properly with Multi PIE and doesn't interfere with the Editor while PIE is running. - Added the ability for an FReply to specify an explict navigation attempt directly. - Added ENavigationGenesis to the navigation system allowing SListView and STileView's bHandleGamepadEvents functionality to be hooked up again. Change 3247887 on 2017/01/05 by Bob.Tellez #UE4 UpdateExistingPackagePriorities does not work in EDL. It is now disabled. #JIRA FORT-35193 Change 3247770 on 2017/01/05 by Fred.Kimberley Fix an issue where PreAttributeBaseChange was not always being called and sometimes called after the attribute base value had changed. Change3247133on 2017/01/04 by Saul.Abreu UWidget designer method renaming to avoid extremely likely naming collisions. Change 3246507 on 2017/01/04 by Chris.Gagnon Created CommonBorder and UCommonBorderStyle very simple but will allow consistent sharing of styles. Cleaned up palette category usage and a few misc things. Updated the UI test material, and created a UI Test BorderStyle to utilize it. Change 3245517 on 2017/01/03 by Chris.Gagnon Copying over slate material changes to provide more functionalit. Added a UITest Material as an example Change 3245371 on 2017/01/03 by Lukasz.Furman fixed husks attacking props from far away #jira FORT-34655 Change 3245363 on 2017/01/03 by Justin.Sargent Tracked down a CEF viewport scaling issue to some changes made for supporting high DPI. After talking it over with Trepka, we decided to revert the specific change causing the CEF viewport regression. Trepka will be following up with a proper fix. #jira OPP-6513 Change 3244525 on 2017/01/02 by Chris.Gagnon Activatable panels now clear out action handlers when the slate widgets are released. Change 3244517 on 2017/01/02 by Chris.Gagnon New frontend major refactors. - New content api for UI States - New intro / outro functionality for activatable panels - New CommonWidgetStack widget - Landing pages - Navigation suport for SListView, STileView - Navigation changes - Lots of New UI layout changes and functionality changes - More things that I'm forgetting Change 3242434 on 2016/12/21 by Ben.Zeigler Improve package saving time by stopping export sorting from recursing into dependencies outside of the package. It has no control over them so it doesn't care about their load order. Change 3242433 on 2016/12/21 by Ben.Zeigler Small perf improvement for quad tree, stop it from constantly reallocating memory when removing nodes as they will likely get filled again or the node will get deleted Change 3242294 on 2016/12/21 by Bob.Tellez #UE4 Re-applying the fix for rendering editor primitives when r.EarlyZPassOnlyMaterialMasking is enabled Change 3241034 on 2016/12/20 by John.Abercrombie Add or UpdateBlueprintSearchMetadata when we don't have a TargetPlatform - Better fix for issue mentioned in CL 3241023 Change 3241023 on 2016/12/20 by John.Abercrombie Fixed UBlueprint::PreSave crashing when there is no TargetPlatform (default behavior) Change 3240988 on 2016/12/20 by Lukasz.Furman fixed melee defenders not finishing move then their goal is outside tether range #jira FORT-34673 Change 3240966 on 2016/12/20 by Ben.Zeigler Disable find in blueprint query when cooking for non editor platforms, saves around 50 seconds off of a Fortnite fast cook. UBlueprint::PreSave gets called even though they get filtered out of cooked builds, as the filtering is after PreSave. Change 3240898 on 2016/12/20 by Lukasz.Furman fixed memory corruption in template A* solver #fortnite Change 3239920 on 2016/12/19 by Ben.Zeigler Fix warning display for string asset references while cooking, now that failed to find errors add to KnownMissing, we need to check KnownMissing before doing the find, and turn off the internal warnings as the redirect collector has more context info Change 3239819 on 2016/12/19 by Lukasz.Furman fixed uninitialized debug draw delegate pointers #ue4 Change 3238789 on 2016/12/16 by Ben.Zeigler Fix issue where spawned cues triggered from async loads wouldn't have a proper world Fix issue where bShouldSyncLoad/bShouldAsyncLoad were backwards Change 3238782 on 2016/12/16 by Ben.Zeigler #jira FORT-34825 Fix issue where Macro CDOs had corrupted persistent ubergraph frames during blueprint compile on load, by changing it so no CDOs have persistent frames. This also saves memory as using persistent frames is incorrect for CDOs, things like latent functions do not make sense. Fix from Dan O'Connor Change 3238685 on 2016/12/16 by Bob.Tellez #UE4 Graceful recovery for actors that changed mobility between frames in TextureInstanceManager. #JIRA FORT-34833 Change 3238671 on 2016/12/16 by Ben.Zeigler Fix ensure opening widget palette view, it was trying to create asset data for trash classes becuase it was just doing a raw class iterator, which is no longer supported. Change 3238606 on 2016/12/16 by Rob.Cannaday Fix crash in FInternetAddrBSD::SetIp when InAddr is an empty string. #jira FORT-34826 Change 3238594 on 2016/12/16 by Ben.Zeigler #jira FORT-34704 Fix bNetTemporary actors to be created with reliable packets, to keep sending until their initial send is done. The code that used to resend incomplete net temporary actors appears to have stopped working sometime during UE4 networking refactors. Remove unused flags related to that code Change3238315on 2016/12/16 by Lukasz.Furman fixed composite navigation path usage for husks not controlled by crowd simulation #jira FORT-34509 Change 3238145 on 2016/12/16 by Lukasz.Furman fixed crash in EQS profiler #jira FORT-34831 Change 3237479 on 2016/12/15 by Ben.Zeigler Don't crash if cue manager has no world, not sure how it got into this state Change 3236992 on 2016/12/15 by Michael.Trepka Don't fall back to SM4 on Intel GPUs on Mac any more Change 3236929 on 2016/12/15 by Bob.Tellez #UE4 Fixed an ensure that was caused by an FResourceSizeEx being initialized with the wrong type Change 3236867 on 2016/12/15 by Bob.Tellez #Fortinte Submitted change from Gil to fix EDL crash loading into Outpost on PS4 #JIRA FORT-34794 Change 3236747 on 2016/12/15 by Ben.Zeigler Fortnite fixes for asset manager/async loading changes FortItemDefinitions now async load needed assets on demand. Currently this is only loading AttributeTemplate, which may not even be in use The blueprints needed for weapons are now async loaded when the player puts them on their quickbar, instead of being loaded once and staying in memory forever FortAssetManager is now being used in parallel to the loading code in FortGlobals, I will remove the FortGlobals code in the next checkin once I know things are working Change it so the MissionEventNames are loaded asynchronously when clicking the picker, this code was half completed already so I finished it up Change it so GameplayCueNotifies get async loaded on demand instead of async loaded at startup, this improves startup load times Change it so the CommonUIModule uses the global assetmanager instead of a passed in StreamableManagerHandler Item json changed because it got resorted, no actual json changes other than a few cosmetics that were added yesterday Change it so FortItem implements the mcp item interface directly, instead of FortWorldItem and FortAccountItem implementing it separately Change 3236746 on 2016/12/15 by Ben.Zeigler Add ProcessAsyncLoadingUntilComplete which will process async loading until a predicate is true or time runs out Change streamable manager to return a handle structure, that can be used to block or poll as needed. Active handles will keep objects in memory even after the load finishes FStreamableManager::SynchronousLoad now does high-priority-async-load-and-wait instead of doing a full async flush/static load object if asynch loading is in progress, this should make stalls much shorter when sync loading a single asset Deprecate some of the StreamableManager functions now that handles exist. The fact that SynchronousLoad kept an object from ever GCing was not expected behavior by most users Add Experimental feature AssetManager, which is a global singleton that supports loading assets on demand. It is disabled by default Add concept of PrimaryAssetID which is a Type:Name pair that globally identifies an asset. This is returned by GetPrimaryAssetId on UObject and is needed for the asset manager to work Add PrimaryAssetData class, which supports the primary asset and bundle concepts natively Add concept of an AssetBundleEntry/Data, which is a scoped map from name -> list of assets. If you modify an AssetBundleData it will get baked into the asset registry at runtime Fix KismetSystemLibrary and GameplayCueManager to use the new streaming functionality Change 3234031 on 2016/12/13 by Ian.Fox #UE4, #XMPP - Finish libstrophe MUC (Multi-User Chat) implementation - Pull history when joining channels - Handle configuring of XMPP channels we create (and global chat rooms if we managed to create them) [CL 3291644 by Bob Tellez in Main branch]
1562 lines
62 KiB
C#
1562 lines
62 KiB
C#
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
using System.Net.NetworkInformation;
|
|
using System.Threading;
|
|
using AutomationTool;
|
|
using UnrealBuildTool;
|
|
using Ionic.Zip;
|
|
|
|
public class AndroidPlatform : Platform
|
|
{
|
|
private const int DeployMaxParallelCommands = 6;
|
|
|
|
private const string TargetAndroidLocation = "obb/";
|
|
|
|
public AndroidPlatform()
|
|
: base(UnrealTargetPlatform.Android)
|
|
{
|
|
}
|
|
|
|
private static string GetSONameWithoutArchitecture(ProjectParams Params, string DecoratedExeName)
|
|
{
|
|
return Path.Combine(Path.GetDirectoryName(Params.ProjectGameExeFilename), DecoratedExeName) + ".so";
|
|
}
|
|
|
|
private static string GetFinalApkName(ProjectParams Params, string DecoratedExeName, bool bRenameUE4Game, string Architecture, string GPUArchitecture)
|
|
{
|
|
string ProjectDir = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(Params.RawProjectPath.FullName)), "Binaries/Android");
|
|
|
|
if (Params.Prebuilt)
|
|
{
|
|
ProjectDir = Path.Combine(Params.BaseStageDirectory, "Android");
|
|
}
|
|
|
|
// Apk's go to project location, not necessarily where the .so is (content only packages need to output to their directory)
|
|
string ApkName = Path.Combine(ProjectDir, DecoratedExeName) + Architecture + GPUArchitecture + ".apk";
|
|
|
|
// if the source binary was UE4Game, handle using it or switching to project name
|
|
if (Path.GetFileNameWithoutExtension(Params.ProjectGameExeFilename) == "UE4Game")
|
|
{
|
|
if (bRenameUE4Game)
|
|
{
|
|
// replace UE4Game with project name (only replace in the filename part)
|
|
ApkName = Path.Combine(Path.GetDirectoryName(ApkName), Path.GetFileName(ApkName).Replace("UE4Game", Params.ShortProjectName));
|
|
}
|
|
else
|
|
{
|
|
// if we want to use UE4 directly then use it from the engine directory not project directory
|
|
ApkName = ApkName.Replace(ProjectDir, Path.Combine(CmdEnv.LocalRoot, "Engine/Binaries/Android"));
|
|
}
|
|
}
|
|
|
|
return ApkName;
|
|
}
|
|
|
|
private static string GetFinalObbName(string ApkName)
|
|
{
|
|
// calculate the name for the .obb file
|
|
string PackageName = GetPackageInfo(ApkName, false);
|
|
if (PackageName == null)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_FailureGettingPackageInfo, "Failed to get package name from " + ApkName);
|
|
}
|
|
|
|
string PackageVersion = GetPackageInfo(ApkName, true);
|
|
if (PackageVersion == null || PackageVersion.Length == 0)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_FailureGettingPackageInfo, "Failed to get package version from " + ApkName);
|
|
}
|
|
|
|
if (PackageVersion.Length > 0)
|
|
{
|
|
int IntVersion = int.Parse(PackageVersion);
|
|
PackageVersion = IntVersion.ToString("0");
|
|
}
|
|
|
|
string ObbName = string.Format("main.{0}.{1}.obb", PackageVersion, PackageName);
|
|
|
|
// plop the .obb right next to the executable
|
|
ObbName = Path.Combine(Path.GetDirectoryName(ApkName), ObbName);
|
|
|
|
return ObbName;
|
|
}
|
|
|
|
private static string GetDeviceObbName(string ApkName)
|
|
{
|
|
string ObbName = GetFinalObbName(ApkName);
|
|
string PackageName = GetPackageInfo(ApkName, false);
|
|
return TargetAndroidLocation + PackageName + "/" + Path.GetFileName(ObbName);
|
|
}
|
|
|
|
private static string GetStorageQueryCommand()
|
|
{
|
|
if (Utils.IsRunningOnMono)
|
|
{
|
|
return "shell 'echo $EXTERNAL_STORAGE'";
|
|
}
|
|
else
|
|
{
|
|
return "shell \"echo $EXTERNAL_STORAGE\"";
|
|
}
|
|
}
|
|
|
|
private static string GetFinalBatchName(string ApkName, ProjectParams Params, string Architecture, string GPUArchitecture, bool bNoOBBInstall, bool bUninstall, UnrealTargetPlatform Target)
|
|
{
|
|
string Extension = ".bat";
|
|
switch (Target)
|
|
{
|
|
default:
|
|
case UnrealTargetPlatform.Win64:
|
|
Extension = ".bat";
|
|
break;
|
|
|
|
case UnrealTargetPlatform.Linux:
|
|
Extension = ".sh";
|
|
break;
|
|
|
|
case UnrealTargetPlatform.Mac:
|
|
Extension = ".command";
|
|
break;
|
|
}
|
|
return Path.Combine(Path.GetDirectoryName(ApkName), (bUninstall ? "Uninstall_" : "Install_") + Params.ShortProjectName + (!bNoOBBInstall ? "_" : "_NoOBBInstall_") + Params.ClientConfigsToBuild[0].ToString() + Architecture + GPUArchitecture + Extension);
|
|
}
|
|
|
|
private List<string> CollectPluginDataPaths(DeploymentContext SC)
|
|
{
|
|
// collect plugin extra data paths from target receipts
|
|
List<string> PluginExtras = new List<string>();
|
|
foreach (StageTarget Target in SC.StageTargets)
|
|
{
|
|
TargetReceipt Receipt = Target.Receipt;
|
|
var Results = Receipt.AdditionalProperties.Where(x => x.Name == "AndroidPlugin");
|
|
foreach (var Property in Results)
|
|
{
|
|
// Keep only unique paths
|
|
string PluginPath = Property.Value;
|
|
if (PluginExtras.FirstOrDefault(x => x == PluginPath) == null)
|
|
{
|
|
PluginExtras.Add(PluginPath);
|
|
Log("AndroidPlugin: {0}", PluginPath);
|
|
}
|
|
}
|
|
}
|
|
return PluginExtras;
|
|
}
|
|
|
|
public override void Package(ProjectParams Params, DeploymentContext SC, int WorkingCL)
|
|
{
|
|
IAndroidToolChain ToolChain = AndroidExports.CreateToolChain(Params.RawProjectPath);
|
|
var Architectures = ToolChain.GetAllArchitectures();
|
|
var GPUArchitectures = ToolChain.GetAllGPUArchitectures();
|
|
bool bMakeSeparateApks = UnrealBuildTool.AndroidExports.ShouldMakeSeparateApks();
|
|
|
|
var Deploy = AndroidExports.CreateDeploymentHandler(Params.RawProjectPath);
|
|
bool bPackageDataInsideApk = Deploy.PackageDataInsideApk(false);
|
|
|
|
string BaseApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, "", "");
|
|
Log("BaseApkName = {0}", BaseApkName);
|
|
|
|
// Create main OBB with entire contents of staging dir. This
|
|
// includes any PAK files, movie files, etc.
|
|
|
|
string LocalObbName = SC.StageDirectory.TrimEnd(new char[] {'/', '\\'})+".obb";
|
|
|
|
// Always delete the target OBB file if it exists
|
|
if (File.Exists(LocalObbName))
|
|
{
|
|
File.Delete(LocalObbName);
|
|
}
|
|
|
|
// Now create the OBB as a ZIP archive.
|
|
Log("Creating {0} from {1}", LocalObbName, SC.StageDirectory);
|
|
using (ZipFile ObbFile = new ZipFile(LocalObbName))
|
|
{
|
|
ObbFile.CompressionMethod = CompressionMethod.None;
|
|
ObbFile.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
|
|
ObbFile.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Never;
|
|
|
|
int ObbFileCount = 0;
|
|
ObbFile.AddProgress +=
|
|
delegate(object sender, AddProgressEventArgs e)
|
|
{
|
|
if (e.EventType == ZipProgressEventType.Adding_AfterAddEntry)
|
|
{
|
|
ObbFileCount += 1;
|
|
Log("[{0}/{1}] Adding {2} to OBB",
|
|
ObbFileCount, e.EntriesTotal,
|
|
e.CurrentEntry.FileName);
|
|
}
|
|
};
|
|
ObbFile.AddDirectory(SC.StageDirectory+"/"+SC.ShortProjectName, SC.ShortProjectName);
|
|
try
|
|
{
|
|
ObbFile.Save();
|
|
}
|
|
catch (Exception)
|
|
{
|
|
Log("Failed to build OBB: " + LocalObbName);
|
|
throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not build OBB {0}. The file may be too big to fit in an OBB (2 GiB limit)", LocalObbName);
|
|
}
|
|
}
|
|
|
|
// collect plugin extra data paths from target receipts
|
|
Deploy.SetAndroidPluginData(Architectures, CollectPluginDataPaths(SC));
|
|
|
|
foreach (string Architecture in Architectures)
|
|
{
|
|
foreach (string GPUArchitecture in GPUArchitectures)
|
|
{
|
|
string ApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
|
|
if (!SC.IsCodeBasedProject)
|
|
{
|
|
string UE4SOName = GetFinalApkName(Params, SC.StageExecutables[0], false, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
|
|
UE4SOName = UE4SOName.Replace(".apk", ".so");
|
|
if (FileExists_NoExceptions(UE4SOName) == false)
|
|
{
|
|
Log("Failed to find game .so " + UE4SOName);
|
|
throw new AutomationException(ExitCode.Error_MissingExecutable, "Stage Failed. Could not find .so {0}. You may need to build the UE4 project with your target configuration and platform.", UE4SOName);
|
|
}
|
|
}
|
|
|
|
|
|
if (!Params.Prebuilt)
|
|
{
|
|
string CookFlavor = SC.FinalCookPlatform.IndexOf("_") > 0 ? SC.FinalCookPlatform.Substring(SC.FinalCookPlatform.IndexOf("_")) : "";
|
|
string SOName = GetSONameWithoutArchitecture(Params, SC.StageExecutables[0]);
|
|
Deploy.PrepForUATPackageOrDeploy(Params.RawProjectPath, Params.ShortProjectName, SC.ProjectRoot, SOName, SC.LocalRoot + "/Engine", Params.Distribution, CookFlavor, false);
|
|
}
|
|
|
|
// Create APK specific OBB in case we have a detached OBB.
|
|
string DeviceObbName = "";
|
|
string ObbName = "";
|
|
if (!bPackageDataInsideApk)
|
|
{
|
|
DeviceObbName = GetDeviceObbName(ApkName);
|
|
ObbName = GetFinalObbName(ApkName);
|
|
CopyFile(LocalObbName, ObbName);
|
|
}
|
|
|
|
//figure out which platforms we need to create install files for
|
|
bool bNeedsPCInstall = false;
|
|
bool bNeedsMacInstall = false;
|
|
bool bNeedsLinuxInstall = false;
|
|
GetPlatformInstallOptions(SC, out bNeedsPCInstall, out bNeedsMacInstall, out bNeedsLinuxInstall);
|
|
|
|
//helper delegate to prevent code duplication but allow us access to all the local variables we need
|
|
var CreateInstallFilesAction = new Action<UnrealTargetPlatform>(Target =>
|
|
{
|
|
bool bIsPC = (Target == UnrealTargetPlatform.Win64);
|
|
// Write install batch file(s).
|
|
string BatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", false, false, Target);
|
|
string PackageName = GetPackageInfo(ApkName, false);
|
|
// make a batch file that can be used to install the .apk and .obb files
|
|
string[] BatchLines = GenerateInstallBatchFile(bPackageDataInsideApk, PackageName, ApkName, Params, ObbName, DeviceObbName, false, bIsPC);
|
|
File.WriteAllLines(BatchName, BatchLines);
|
|
// make a batch file that can be used to uninstall the .apk and .obb files
|
|
string UninstallBatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", false, true, Target);
|
|
BatchLines = GenerateUninstallBatchFile(bPackageDataInsideApk, PackageName, ApkName, Params, ObbName, DeviceObbName, false, bIsPC);
|
|
File.WriteAllLines(UninstallBatchName, BatchLines);
|
|
|
|
if (Utils.IsRunningOnMono)
|
|
{
|
|
CommandUtils.FixUnixFilePermissions(BatchName);
|
|
CommandUtils.FixUnixFilePermissions(UninstallBatchName);
|
|
//if(File.Exists(NoInstallBatchName))
|
|
//{
|
|
// CommandUtils.FixUnixFilePermissions(NoInstallBatchName);
|
|
//}
|
|
}
|
|
}
|
|
);
|
|
|
|
if (bNeedsPCInstall)
|
|
{
|
|
CreateInstallFilesAction.Invoke(UnrealTargetPlatform.Win64);
|
|
}
|
|
if (bNeedsMacInstall)
|
|
{
|
|
CreateInstallFilesAction.Invoke(UnrealTargetPlatform.Mac);
|
|
}
|
|
if (bNeedsLinuxInstall)
|
|
{
|
|
CreateInstallFilesAction.Invoke(UnrealTargetPlatform.Linux);
|
|
}
|
|
|
|
// If we aren't packaging data in the APK then lets write out a bat file to also let us test without the OBB
|
|
// on the device.
|
|
//String NoInstallBatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", true, false);
|
|
// if(!bPackageDataInsideApk)
|
|
//{
|
|
// BatchLines = GenerateInstallBatchFile(bPackageDataInsideApk, PackageName, ApkName, Params, ObbName, DeviceObbName, true);
|
|
// File.WriteAllLines(NoInstallBatchName, BatchLines);
|
|
//}
|
|
}
|
|
}
|
|
|
|
PrintRunTime();
|
|
}
|
|
|
|
private string[] GenerateInstallBatchFile(bool bPackageDataInsideApk, string PackageName, string ApkName, ProjectParams Params, string ObbName, string DeviceObbName, bool bNoObbInstall, bool bIsPC)
|
|
{
|
|
string[] BatchLines = null;
|
|
string ReadPermissionGrantCommand = "shell pm grant " + PackageName + " android.permission.READ_EXTERNAL_STORAGE";
|
|
string WritePermissionGrantCommand = "shell pm grant " + PackageName + " android.permission.WRITE_EXTERNAL_STORAGE";
|
|
|
|
if (!bIsPC)
|
|
{
|
|
string OBBInstallCommand = bNoObbInstall ? "shell 'rm -r $EXTERNAL_STORAGE/" + DeviceObbName + "'" : "push " + Path.GetFileName(ObbName) + " $STORAGE/" + DeviceObbName;
|
|
|
|
Log("Writing shell script for install with {0}", bPackageDataInsideApk ? "data in APK" : "separate obb");
|
|
BatchLines = new string[] {
|
|
"#!/bin/sh",
|
|
"cd \"`dirname \"$0\"`\"",
|
|
"ADB=",
|
|
"if [ \"$ANDROID_HOME\" != \"\" ]; then ADB=$ANDROID_HOME/platform-tools/adb; else ADB=" +Environment.GetEnvironmentVariable("ANDROID_HOME") + "/platform-tools/adb; fi",
|
|
"DEVICE=",
|
|
"if [ \"$1\" != \"\" ]; then DEVICE=\"-s $1\"; fi",
|
|
"echo",
|
|
"echo Uninstalling existing application. Failures here can almost always be ignored.",
|
|
"$ADB $DEVICE uninstall " + PackageName,
|
|
"echo",
|
|
"echo Installing existing application. Failures here indicate a problem with the device \\(connection or storage permissions\\) and are fatal.",
|
|
"$ADB $DEVICE install " + Path.GetFileName(ApkName),
|
|
"if [ $? -eq 0 ]; then",
|
|
"\techo",
|
|
bPackageDataInsideApk ? "" : "\techo Grant READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE to the apk for reading OBB file.",
|
|
bPackageDataInsideApk ? "" : "\t$ADB $DEVICE " + ReadPermissionGrantCommand,
|
|
bPackageDataInsideApk ? "" : "\t$ADB $DEVICE " + WritePermissionGrantCommand,
|
|
"\techo",
|
|
"\techo Removing old data. Failures here are usually fine - indicating the files were not on the device.",
|
|
"\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/" + Params.ShortProjectName + "'",
|
|
"\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/UE4CommandLine.txt" + "'",
|
|
"\t$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/" + TargetAndroidLocation + PackageName + "'",
|
|
bPackageDataInsideApk ? "" : "\techo",
|
|
bPackageDataInsideApk ? "" : "\techo Installing new data. Failures here indicate storage problems \\(missing SD card or bad permissions\\) and are fatal.",
|
|
bPackageDataInsideApk ? "" : "\tSTORAGE=$(echo \"`$ADB $DEVICE shell 'echo $EXTERNAL_STORAGE'`\" | cat -v | tr -d '^M')",
|
|
bPackageDataInsideApk ? "" : "\t$ADB $DEVICE " + OBBInstallCommand,
|
|
bPackageDataInsideApk ? "if [ 1 ]; then" : "\tif [ $? -eq 0 ]; then",
|
|
"\t\techo",
|
|
"\t\techo Installation successful",
|
|
"\t\texit 0",
|
|
"\tfi",
|
|
"fi",
|
|
"echo",
|
|
"echo There was an error installing the game or the obb file. Look above for more info.",
|
|
"echo",
|
|
"echo Things to try:",
|
|
"echo Check that the device (and only the device) is listed with \\\"$ADB devices\\\" from a command prompt.",
|
|
"echo Make sure all Developer options look normal on the device",
|
|
"echo Check that the device has an SD card.",
|
|
"exit 1"
|
|
};
|
|
}
|
|
else
|
|
{
|
|
string OBBInstallCommand = bNoObbInstall ? "shell rm -r %STORAGE%/" + DeviceObbName : "push " + Path.GetFileName(ObbName) + " %STORAGE%/" + DeviceObbName;
|
|
|
|
Log("Writing bat for install with {0}", bPackageDataInsideApk ? "data in APK" : "separate OBB");
|
|
BatchLines = new string[] {
|
|
"setlocal",
|
|
"set ANDROIDHOME=%ANDROID_HOME%",
|
|
"if \"%ANDROIDHOME%\"==\"\" set ANDROIDHOME="+Environment.GetEnvironmentVariable("ANDROID_HOME"),
|
|
"set ADB=%ANDROIDHOME%\\platform-tools\\adb.exe",
|
|
"set DEVICE=",
|
|
"if not \"%1\"==\"\" set DEVICE=-s %1",
|
|
"for /f \"delims=\" %%A in ('%ADB% %DEVICE% " + GetStorageQueryCommand() +"') do @set STORAGE=%%A",
|
|
"@echo.",
|
|
"@echo Uninstalling existing application. Failures here can almost always be ignored.",
|
|
"%ADB% %DEVICE% uninstall " + PackageName,
|
|
"@echo.",
|
|
"@echo Installing existing application. Failures here indicate a problem with the device (connection or storage permissions) and are fatal.",
|
|
"%ADB% %DEVICE% install " + Path.GetFileName(ApkName),
|
|
"@if \"%ERRORLEVEL%\" NEQ \"0\" goto Error",
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/" + Params.ShortProjectName,
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/UE4CommandLine.txt", // we need to delete the commandline in UE4Game or it will mess up loading
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/" + TargetAndroidLocation + PackageName,
|
|
bPackageDataInsideApk ? "" : "@echo.",
|
|
bPackageDataInsideApk ? "" : "@echo Installing new data. Failures here indicate storage problems (missing SD card or bad permissions) and are fatal.",
|
|
bPackageDataInsideApk ? "" : "%ADB% %DEVICE% " + OBBInstallCommand,
|
|
bPackageDataInsideApk ? "" : "if \"%ERRORLEVEL%\" NEQ \"0\" goto Error",
|
|
"@echo.",
|
|
bPackageDataInsideApk ? "" : "@echo Grant READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE to the apk for reading OBB file.",
|
|
bPackageDataInsideApk ? "" : "%ADB% %DEVICE% " + ReadPermissionGrantCommand,
|
|
bPackageDataInsideApk ? "" : "%ADB% %DEVICE% " + WritePermissionGrantCommand,
|
|
"@echo.",
|
|
"@echo Installation successful",
|
|
"goto:eof",
|
|
":Error",
|
|
"@echo.",
|
|
"@echo There was an error installing the game or the obb file. Look above for more info.",
|
|
"@echo.",
|
|
"@echo Things to try:",
|
|
"@echo Check that the device (and only the device) is listed with \"%ADB$ devices\" from a command prompt.",
|
|
"@echo Make sure all Developer options look normal on the device",
|
|
"@echo Check that the device has an SD card.",
|
|
"@pause"
|
|
};
|
|
}
|
|
return BatchLines;
|
|
}
|
|
|
|
private string[] GenerateUninstallBatchFile(bool bPackageDataInsideApk, string PackageName, string ApkName, ProjectParams Params, string ObbName, string DeviceObbName, bool bNoObbInstall, bool bIsPC)
|
|
{
|
|
string[] BatchLines = null;
|
|
|
|
if (!bIsPC)
|
|
{
|
|
Log("Writing shell script for uninstall with {0}", bPackageDataInsideApk ? "data in APK" : "separate obb");
|
|
BatchLines = new string[] {
|
|
"#!/bin/sh",
|
|
"cd \"`dirname \"$0\"`\"",
|
|
"ADB=",
|
|
"if [ \"$ANDROID_HOME\" != \"\" ]; then ADB=$ANDROID_HOME/platform-tools/adb; else ADB=" +Environment.GetEnvironmentVariable("ANDROID_HOME") + "/platform-tools/adb; fi",
|
|
"DEVICE=",
|
|
"if [ \"$1\" != \"\" ]; then DEVICE=\"-s $1\"; fi",
|
|
"echo",
|
|
"echo Uninstalling existing application. Failures here can almost always be ignored.",
|
|
"$ADB $DEVICE uninstall " + PackageName,
|
|
"echo",
|
|
"echo Removing old data. Failures here are usually fine - indicating the files were not on the device.",
|
|
"$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/" + Params.ShortProjectName + "'",
|
|
"$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/UE4Game/UE4CommandLine.txt" + "'",
|
|
"$ADB $DEVICE shell 'rm -r $EXTERNAL_STORAGE/" + TargetAndroidLocation + PackageName + "'",
|
|
"echo",
|
|
"echo Uninstall completed",
|
|
"exit 0",
|
|
};
|
|
}
|
|
else
|
|
{
|
|
Log("Writing bat for uninstall with {0}", bPackageDataInsideApk ? "data in APK" : "separate OBB");
|
|
BatchLines = new string[] {
|
|
"setlocal",
|
|
"set ANDROIDHOME=%ANDROID_HOME%",
|
|
"if \"%ANDROIDHOME%\"==\"\" set ANDROIDHOME="+Environment.GetEnvironmentVariable("ANDROID_HOME"),
|
|
"set ADB=%ANDROIDHOME%\\platform-tools\\adb.exe",
|
|
"set DEVICE=",
|
|
"if not \"%1\"==\"\" set DEVICE=-s %1",
|
|
"for /f \"delims=\" %%A in ('%ADB% %DEVICE% " + GetStorageQueryCommand() +"') do @set STORAGE=%%A",
|
|
"@echo.",
|
|
"@echo Uninstalling existing application. Failures here can almost always be ignored.",
|
|
"%ADB% %DEVICE% uninstall " + PackageName,
|
|
"@echo.",
|
|
"echo Removing old data. Failures here are usually fine - indicating the files were not on the device.",
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/" + Params.ShortProjectName,
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/UE4Game/UE4CommandLine.txt", // we need to delete the commandline in UE4Game or it will mess up loading
|
|
"%ADB% %DEVICE% shell rm -r %STORAGE%/" + TargetAndroidLocation + PackageName,
|
|
"@echo.",
|
|
"@echo Uninstall completed",
|
|
};
|
|
}
|
|
return BatchLines;
|
|
}
|
|
|
|
public override void GetFilesToArchive(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
if (SC.StageTargetConfigurations.Count != 1)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_OnlyOneTargetConfigurationSupported, "Android is currently only able to package one target configuration at a time, but StageTargetConfigurations contained {0} configurations", SC.StageTargetConfigurations.Count);
|
|
}
|
|
|
|
IAndroidToolChain ToolChain = AndroidExports.CreateToolChain(Params.RawProjectPath);
|
|
var Architectures = ToolChain.GetAllArchitectures();
|
|
var GPUArchitectures = ToolChain.GetAllGPUArchitectures();
|
|
bool bMakeSeparateApks = UnrealBuildTool.AndroidExports.ShouldMakeSeparateApks();
|
|
bool bPackageDataInsideApk = UnrealBuildTool.AndroidExports.CreateDeploymentHandler(Params.RawProjectPath).PackageDataInsideApk(false);
|
|
|
|
bool bAddedOBB = false;
|
|
foreach (string Architecture in Architectures)
|
|
{
|
|
foreach (string GPUArchitecture in GPUArchitectures)
|
|
{
|
|
string ApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "");
|
|
string ObbName = GetFinalObbName(ApkName);
|
|
//string NoOBBBatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", true, false);
|
|
|
|
// verify the files exist
|
|
if (!FileExists(ApkName))
|
|
{
|
|
throw new AutomationException(ExitCode.Error_AppNotFound, "ARCHIVE FAILED - {0} was not found", ApkName);
|
|
}
|
|
if (!bPackageDataInsideApk && !FileExists(ObbName))
|
|
{
|
|
throw new AutomationException(ExitCode.Error_ObbNotFound, "ARCHIVE FAILED - {0} was not found", ObbName);
|
|
}
|
|
|
|
SC.ArchiveFiles(Path.GetDirectoryName(ApkName), Path.GetFileName(ApkName));
|
|
if (!bPackageDataInsideApk && !bAddedOBB)
|
|
{
|
|
bAddedOBB = true;
|
|
SC.ArchiveFiles(Path.GetDirectoryName(ObbName), Path.GetFileName(ObbName));
|
|
}
|
|
|
|
bool bNeedsPCInstall = false;
|
|
bool bNeedsMacInstall = false;
|
|
bool bNeedsLinuxInstall = false;
|
|
GetPlatformInstallOptions(SC, out bNeedsPCInstall, out bNeedsMacInstall, out bNeedsLinuxInstall);
|
|
|
|
//helper delegate to prevent code duplication but allow us access to all the local variables we need
|
|
var CreateBatchFilesAndArchiveAction = new Action<UnrealTargetPlatform>(Target =>
|
|
{
|
|
string BatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", false, false, Target);
|
|
string UninstallBatchName = GetFinalBatchName(ApkName, Params, bMakeSeparateApks ? Architecture : "", bMakeSeparateApks ? GPUArchitecture : "", false, true, Target);
|
|
|
|
SC.ArchiveFiles(Path.GetDirectoryName(BatchName), Path.GetFileName(BatchName));
|
|
SC.ArchiveFiles(Path.GetDirectoryName(UninstallBatchName), Path.GetFileName(UninstallBatchName));
|
|
//SC.ArchiveFiles(Path.GetDirectoryName(NoOBBBatchName), Path.GetFileName(NoOBBBatchName));
|
|
}
|
|
);
|
|
|
|
//it's possible we will need both PC and Mac/Linux install files, do both
|
|
if (bNeedsPCInstall)
|
|
{
|
|
CreateBatchFilesAndArchiveAction(UnrealTargetPlatform.Win64);
|
|
}
|
|
if (bNeedsMacInstall)
|
|
{
|
|
CreateBatchFilesAndArchiveAction(UnrealTargetPlatform.Mac);
|
|
}
|
|
if (bNeedsLinuxInstall)
|
|
{
|
|
CreateBatchFilesAndArchiveAction(UnrealTargetPlatform.Linux);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GetPlatformInstallOptions(DeploymentContext SC, out bool bNeedsPCInstall, out bool bNeedsMacInstall, out bool bNeedsLinuxInstall)
|
|
{
|
|
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(SC.RawProjectPath), SC.StageTargetPlatform.PlatformType);
|
|
bool bGenerateAllPlatformInstall = false;
|
|
Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bCreateAllPlatformsInstall", out bGenerateAllPlatformInstall);
|
|
|
|
bNeedsPCInstall = bNeedsMacInstall = bNeedsLinuxInstall = false;
|
|
|
|
if (bGenerateAllPlatformInstall)
|
|
{
|
|
bNeedsPCInstall = bNeedsMacInstall = bNeedsLinuxInstall = true;
|
|
}
|
|
else
|
|
{
|
|
if (HostPlatform.Current.HostEditorPlatform == UnrealTargetPlatform.Mac)
|
|
{
|
|
bNeedsMacInstall = true;
|
|
}
|
|
else if (HostPlatform.Current.HostEditorPlatform == UnrealTargetPlatform.Linux)
|
|
{
|
|
bNeedsLinuxInstall = true;
|
|
}
|
|
else
|
|
{
|
|
bNeedsPCInstall = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private string GetAdbCommandLine(ProjectParams Params, string SerialNumber, string Args)
|
|
{
|
|
if (SerialNumber != "")
|
|
{
|
|
SerialNumber = "-s " + SerialNumber;
|
|
}
|
|
|
|
return string.Format("{0} {1}", SerialNumber, Args);
|
|
}
|
|
|
|
static string LastSpewFilename = "";
|
|
|
|
public string ADBSpewFilter(string Message)
|
|
{
|
|
if (Message.StartsWith("[") && Message.Contains("%]"))
|
|
{
|
|
int LastIndex = Message.IndexOf(":");
|
|
LastIndex = LastIndex == -1 ? Message.Length : LastIndex;
|
|
|
|
if (Message.Length > 7)
|
|
{
|
|
string Filename = Message.Substring(7, LastIndex - 7);
|
|
if (Filename == LastSpewFilename)
|
|
{
|
|
return null;
|
|
}
|
|
LastSpewFilename = Filename;
|
|
}
|
|
return Message;
|
|
}
|
|
return Message;
|
|
}
|
|
|
|
private IProcessResult RunAdbCommand(ProjectParams Params, string SerialNumber, string Args, string Input = null, ERunOptions Options = ERunOptions.Default)
|
|
{
|
|
string AdbCommand = Environment.ExpandEnvironmentVariables("%ANDROID_HOME%/platform-tools/adb" + (Utils.IsRunningOnMono ? "" : ".exe"));
|
|
if (Options.HasFlag(ERunOptions.AllowSpew) || Options.HasFlag(ERunOptions.SpewIsVerbose))
|
|
{
|
|
LastSpewFilename = "";
|
|
return Run(AdbCommand, GetAdbCommandLine(Params, SerialNumber, Args), Input, Options, SpewFilterCallback: new ProcessResult.SpewFilterCallbackType(ADBSpewFilter));
|
|
}
|
|
return Run(AdbCommand, GetAdbCommandLine(Params, SerialNumber, Args), Input, Options);
|
|
}
|
|
|
|
private string RunAndLogAdbCommand(ProjectParams Params, string SerialNumber, string Args, out int SuccessCode)
|
|
{
|
|
string AdbCommand = Environment.ExpandEnvironmentVariables("%ANDROID_HOME%/platform-tools/adb" + (Utils.IsRunningOnMono ? "" : ".exe"));
|
|
LastSpewFilename = "";
|
|
return RunAndLog(CmdEnv, AdbCommand, GetAdbCommandLine(Params, SerialNumber, Args), out SuccessCode, SpewFilterCallback: new ProcessResult.SpewFilterCallbackType(ADBSpewFilter));
|
|
}
|
|
|
|
public override void GetConnectedDevices(ProjectParams Params, out List<string> Devices)
|
|
{
|
|
Devices = new List<string>();
|
|
IProcessResult Result = RunAdbCommand(Params, "", "devices");
|
|
|
|
if (Result.Output.Length > 0)
|
|
{
|
|
string[] LogLines = Result.Output.Split(new char[] { '\n', '\r' });
|
|
bool FoundList = false;
|
|
for (int i = 0; i < LogLines.Length; ++i)
|
|
{
|
|
if (FoundList == false)
|
|
{
|
|
if (LogLines[i].StartsWith("List of devices attached"))
|
|
{
|
|
FoundList = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
string[] DeviceLine = LogLines[i].Split(new char[] { '\t' });
|
|
|
|
if (DeviceLine.Length == 2)
|
|
{
|
|
// the second param should be "device"
|
|
// if it's not setup correctly it might be "unattached" or "powered off" or something like that
|
|
// warning in that case
|
|
if (DeviceLine[1] == "device")
|
|
{
|
|
Devices.Add("@" + DeviceLine[0]);
|
|
}
|
|
else
|
|
{
|
|
CommandUtils.LogWarning("Device attached but in bad state {0}:{1}", DeviceLine[0], DeviceLine[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
private class TimeRegion : System.IDisposable
|
|
{
|
|
private System.DateTime StartTime { get; set; }
|
|
|
|
private string Format { get; set; }
|
|
|
|
private System.Collections.Generic.List<object> FormatArgs { get; set; }
|
|
|
|
public TimeRegion(string format, params object[] format_args)
|
|
{
|
|
Format = format;
|
|
FormatArgs = new List<object>(format_args);
|
|
StartTime = DateTime.UtcNow;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
double total_time = (DateTime.UtcNow - StartTime).TotalMilliseconds / 1000.0;
|
|
FormatArgs.Insert(0, total_time);
|
|
CommandUtils.Log(Format, FormatArgs.ToArray());
|
|
}
|
|
}
|
|
*/
|
|
|
|
public override bool RetrieveDeployedManifests(ProjectParams Params, DeploymentContext SC, string DeviceName, out List<string> UFSManifests, out List<string> NonUFSManifests)
|
|
{
|
|
UFSManifests = null;
|
|
NonUFSManifests = null;
|
|
|
|
// Query the storage path from the device
|
|
string DeviceStorageQueryCommand = GetStorageQueryCommand();
|
|
IProcessResult StorageResult = RunAdbCommand(Params, DeviceName, DeviceStorageQueryCommand, null, ERunOptions.AppMustExist);
|
|
String StorageLocation = StorageResult.Output.Trim();
|
|
string RemoteDir = StorageLocation + "/UE4Game/" + Params.ShortProjectName;
|
|
|
|
// Note: appends the device name to make the filename unique; these files will be deleted later during delta manifest generation
|
|
// Replace colon with underscore for legal filename (colon may be present for wifi connected devices)
|
|
string SanitizedDeviceName = DeviceName.Replace(":", "_");
|
|
|
|
// Try retrieving the UFS files manifest files from the device
|
|
string UFSManifestFileName = CombinePaths(SC.StageDirectory, SC.UFSDeployedManifestFileName + "_" + SanitizedDeviceName);
|
|
IProcessResult UFSResult = RunAdbCommand(Params, DeviceName, " pull " + RemoteDir + "/" + SC.UFSDeployedManifestFileName + " \"" + UFSManifestFileName + "\"", null, ERunOptions.AppMustExist);
|
|
if (!(UFSResult.Output.Contains("bytes") || UFSResult.Output.Contains("[100%]")))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Try retrieving the non UFS files manifest files from the device
|
|
string NonUFSManifestFileName = CombinePaths(SC.StageDirectory, SC.NonUFSDeployedManifestFileName + "_" + SanitizedDeviceName);
|
|
IProcessResult NonUFSResult = RunAdbCommand(Params, DeviceName, " pull " + RemoteDir + "/" + SC.NonUFSDeployedManifestFileName + " \"" + NonUFSManifestFileName + "\"", null, ERunOptions.AppMustExist);
|
|
if (!(NonUFSResult.Output.Contains("bytes") || NonUFSResult.Output.Contains("[100%]")))
|
|
{
|
|
// Did not retrieve both so delete one we did retrieve
|
|
File.Delete(UFSManifestFileName);
|
|
return false;
|
|
}
|
|
|
|
// Return the manifest files
|
|
UFSManifests = new List<string>();
|
|
UFSManifests.Add(UFSManifestFileName);
|
|
NonUFSManifests = new List<string>();
|
|
NonUFSManifests.Add(NonUFSManifestFileName);
|
|
|
|
return true;
|
|
}
|
|
|
|
internal class LongestFirst : IComparer<string>
|
|
{
|
|
public int Compare(string a, string b)
|
|
{
|
|
if (a.Length == b.Length) return a.CompareTo(b);
|
|
else return b.Length - a.Length;
|
|
}
|
|
}
|
|
|
|
public override void Deploy(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
foreach (var DeviceName in Params.DeviceNames)
|
|
{
|
|
string DeviceArchitecture = GetBestDeviceArchitecture(Params, DeviceName);
|
|
string GPUArchitecture = GetBestGPUArchitecture(Params, DeviceName);
|
|
|
|
string ApkName = GetFinalApkName(Params, SC.StageExecutables[0], true, DeviceArchitecture, GPUArchitecture);
|
|
|
|
// make sure APK is up to date (this is fast if so)
|
|
var Deploy = AndroidExports.CreateDeploymentHandler(Params.RawProjectPath);
|
|
if (!Params.Prebuilt)
|
|
{
|
|
string CookFlavor = SC.FinalCookPlatform.IndexOf("_") > 0 ? SC.FinalCookPlatform.Substring(SC.FinalCookPlatform.IndexOf("_")) : "";
|
|
string SOName = GetSONameWithoutArchitecture(Params, SC.StageExecutables[0]);
|
|
List<string> Architectures = new List<string>();
|
|
Architectures.Add(DeviceArchitecture);
|
|
Deploy.SetAndroidPluginData(Architectures, CollectPluginDataPaths(SC));
|
|
Deploy.PrepForUATPackageOrDeploy(Params.RawProjectPath, Params.ShortProjectName, SC.ProjectRoot, SOName, SC.LocalRoot + "/Engine", Params.Distribution, CookFlavor, true);
|
|
}
|
|
|
|
// now we can use the apk to get more info
|
|
string PackageName = GetPackageInfo(ApkName, false);
|
|
|
|
// Setup the OBB name and add the storage path (queried from the device) to it
|
|
string DeviceStorageQueryCommand = GetStorageQueryCommand();
|
|
IProcessResult Result = RunAdbCommand(Params, DeviceName, DeviceStorageQueryCommand, null, ERunOptions.AppMustExist);
|
|
String StorageLocation = Result.Output.Trim(); // "/mnt/sdcard";
|
|
string DeviceObbName = StorageLocation + "/" + GetDeviceObbName(ApkName);
|
|
string RemoteDir = StorageLocation + "/UE4Game/" + Params.ShortProjectName;
|
|
|
|
// determine if APK out of date
|
|
string APKLastUpdateTime = new FileInfo(ApkName).LastWriteTime.ToString();
|
|
bool bNeedAPKInstall = true;
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
// Check for apk installed with this package name on the device
|
|
IProcessResult InstalledResult = RunAdbCommand(Params, DeviceName, "shell pm list packages " + PackageName, null, ERunOptions.AppMustExist);
|
|
if (InstalledResult.Output.Contains(PackageName))
|
|
{
|
|
// See if apk is up to date on device
|
|
InstalledResult = RunAdbCommand(Params, DeviceName, "shell cat " + RemoteDir + "/APKFileStamp.txt", null, ERunOptions.AppMustExist);
|
|
if (InstalledResult.Output.StartsWith("APK: "))
|
|
{
|
|
if (InstalledResult.Output.Substring(5).Trim() == APKLastUpdateTime)
|
|
bNeedAPKInstall = false;
|
|
|
|
// Stop the previously running copy (uninstall/install did this before)
|
|
InstalledResult = RunAdbCommand(Params, DeviceName, "shell am force-stop " + PackageName, null, ERunOptions.AppMustExist);
|
|
if (InstalledResult.Output.Contains("Error"))
|
|
{
|
|
// force-stop not supported (Android < 3.0) so check if package is actually running
|
|
// Note: cannot use grep here since it may not be installed on device
|
|
InstalledResult = RunAdbCommand(Params, DeviceName, "shell ps", null, ERunOptions.AppMustExist);
|
|
if (InstalledResult.Output.Contains(PackageName))
|
|
{
|
|
// it is actually running so use the slow way to kill it (uninstall and reinstall)
|
|
bNeedAPKInstall = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// install new APK if needed
|
|
if (bNeedAPKInstall)
|
|
{
|
|
// try uninstalling an old app with the same identifier.
|
|
int SuccessCode = 0;
|
|
string UninstallCommandline = "uninstall " + PackageName;
|
|
RunAndLogAdbCommand(Params, DeviceName, UninstallCommandline, out SuccessCode);
|
|
|
|
// install the apk
|
|
string InstallCommandline = "install \"" + ApkName + "\"";
|
|
string InstallOutput = RunAndLogAdbCommand(Params, DeviceName, InstallCommandline, out SuccessCode);
|
|
int FailureIndex = InstallOutput.IndexOf("Failure");
|
|
|
|
// adb install doesn't always return an error code on failure, and instead prints "Failure", followed by an error code.
|
|
if (SuccessCode != 0 || FailureIndex != -1)
|
|
{
|
|
string ErrorMessage = string.Format("Installation of apk '{0}' failed", ApkName);
|
|
if (FailureIndex != -1)
|
|
{
|
|
string FailureString = InstallOutput.Substring(FailureIndex + 7).Trim();
|
|
if (FailureString != "")
|
|
{
|
|
ErrorMessage += ": " + FailureString;
|
|
}
|
|
}
|
|
if (ErrorMessage.Contains("OLDER_SDK"))
|
|
{
|
|
LogError("minSdkVersion is higher than Android version installed on device, possibly due to NDK API Level");
|
|
}
|
|
throw new AutomationException(ExitCode.Error_AppInstallFailed, ErrorMessage);
|
|
}
|
|
else
|
|
{
|
|
// giving EXTERNAL_STORAGE_WRITE permission to the apk for API23+
|
|
// without this permission apk can't access to the assets put into the device
|
|
string ReadPermissionCommandLine = "shell pm grant " + PackageName + " android.permission.READ_EXTERNAL_STORAGE";
|
|
string WritePermissionCommandLine = "shell pm grant " + PackageName + " android.permission.WRITE_EXTERNAL_STORAGE";
|
|
RunAndLogAdbCommand(Params, DeviceName, ReadPermissionCommandLine, out SuccessCode);
|
|
RunAndLogAdbCommand(Params, DeviceName, WritePermissionCommandLine, out SuccessCode);
|
|
}
|
|
}
|
|
|
|
// update the ue4commandline.txt
|
|
// update and deploy ue4commandline.txt
|
|
// always delete the existing commandline text file, so it doesn't reuse an old one
|
|
string IntermediateCmdLineFile = CombinePaths(SC.StageDirectory, "UE4CommandLine.txt");
|
|
Project.WriteStageCommandline(IntermediateCmdLineFile, Params, SC);
|
|
|
|
// copy files to device if we were staging
|
|
if (SC.Stage)
|
|
{
|
|
// cache some strings
|
|
string BaseCommandline = "push";
|
|
|
|
HashSet<string> EntriesToDeploy = new HashSet<string>();
|
|
|
|
if (Params.IterativeDeploy)
|
|
{
|
|
// always send UE4CommandLine.txt (it was written above after delta checks applied)
|
|
EntriesToDeploy.Add(IntermediateCmdLineFile);
|
|
|
|
// Add non UFS files if any to deploy
|
|
String NonUFSManifestPath = SC.GetNonUFSDeploymentDeltaPath(DeviceName);
|
|
if (File.Exists(NonUFSManifestPath))
|
|
{
|
|
string NonUFSFiles = File.ReadAllText(NonUFSManifestPath);
|
|
foreach (string Filename in NonUFSFiles.Split('\n'))
|
|
{
|
|
if (!string.IsNullOrEmpty(Filename) && !string.IsNullOrWhiteSpace(Filename))
|
|
{
|
|
EntriesToDeploy.Add(CombinePaths(SC.StageDirectory, Filename.Trim()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add UFS files if any to deploy
|
|
String UFSManifestPath = SC.GetUFSDeploymentDeltaPath(DeviceName);
|
|
if (File.Exists(UFSManifestPath))
|
|
{
|
|
string UFSFiles = File.ReadAllText(UFSManifestPath);
|
|
foreach (string Filename in UFSFiles.Split('\n'))
|
|
{
|
|
if (!string.IsNullOrEmpty(Filename) && !string.IsNullOrWhiteSpace(Filename))
|
|
{
|
|
EntriesToDeploy.Add(CombinePaths(SC.StageDirectory, Filename.Trim()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// For now, if too many files may be better to just push them all
|
|
if (EntriesToDeploy.Count > 500)
|
|
{
|
|
// make sure device is at a clean state
|
|
RunAdbCommand(Params, DeviceName, "shell rm -r " + RemoteDir);
|
|
|
|
EntriesToDeploy.Clear();
|
|
EntriesToDeploy.TrimExcess();
|
|
EntriesToDeploy.Add(SC.StageDirectory);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// make sure device is at a clean state
|
|
RunAdbCommand(Params, DeviceName, "shell rm -r " + RemoteDir);
|
|
|
|
// Copy UFS files..
|
|
string[] Files = Directory.GetFiles(SC.StageDirectory, "*", SearchOption.AllDirectories);
|
|
System.Array.Sort(Files);
|
|
|
|
// Find all the files we exclude from copying. And include
|
|
// the directories we need to individually copy.
|
|
HashSet<string> ExcludedFiles = new HashSet<string>();
|
|
SortedSet<string> IndividualCopyDirectories
|
|
= new SortedSet<string>((IComparer<string>)new LongestFirst());
|
|
foreach (string Filename in Files)
|
|
{
|
|
bool Exclude = false;
|
|
// Don't push the apk, we install it
|
|
Exclude |= Path.GetExtension(Filename).Equals(".apk", StringComparison.InvariantCultureIgnoreCase);
|
|
// For excluded files we add the parent dirs to our
|
|
// tracking of stuff to individually copy.
|
|
if (Exclude)
|
|
{
|
|
ExcludedFiles.Add(Filename);
|
|
// We include all directories up to the stage root in having
|
|
// to individually copy the files.
|
|
for (string FileDirectory = Path.GetDirectoryName(Filename);
|
|
!FileDirectory.Equals(SC.StageDirectory);
|
|
FileDirectory = Path.GetDirectoryName(FileDirectory))
|
|
{
|
|
if (!IndividualCopyDirectories.Contains(FileDirectory))
|
|
{
|
|
IndividualCopyDirectories.Add(FileDirectory);
|
|
}
|
|
}
|
|
if (!IndividualCopyDirectories.Contains(SC.StageDirectory))
|
|
{
|
|
IndividualCopyDirectories.Add(SC.StageDirectory);
|
|
}
|
|
}
|
|
}
|
|
|
|
// The directories are sorted above in "deepest" first. We can
|
|
// therefore start copying those individual dirs which will
|
|
// recreate the tree. As the subtrees will get copied at each
|
|
// possible individual level.
|
|
foreach (string DirectoryName in IndividualCopyDirectories)
|
|
{
|
|
string[] Entries
|
|
= Directory.GetFileSystemEntries(DirectoryName, "*", SearchOption.TopDirectoryOnly);
|
|
foreach (string Entry in Entries)
|
|
{
|
|
// We avoid excluded files and the individual copy dirs
|
|
// (the individual copy dirs will get handled as we iterate).
|
|
if (ExcludedFiles.Contains(Entry) || IndividualCopyDirectories.Contains(Entry))
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
EntriesToDeploy.Add(Entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (EntriesToDeploy.Count == 0)
|
|
{
|
|
EntriesToDeploy.Add(SC.StageDirectory);
|
|
}
|
|
}
|
|
|
|
// We now have a minimal set of file & dir entries we need
|
|
// to deploy. Files we deploy will get individually copied
|
|
// and dirs will get the tree copies by default (that's
|
|
// what ADB does).
|
|
HashSet<IProcessResult> DeployCommands = new HashSet<IProcessResult>();
|
|
foreach (string Entry in EntriesToDeploy)
|
|
{
|
|
string FinalRemoteDir = RemoteDir;
|
|
string RemotePath = Entry.Replace(SC.StageDirectory, FinalRemoteDir).Replace("\\", "/");
|
|
string Commandline = string.Format("{0} \"{1}\" \"{2}\"", BaseCommandline, Entry, RemotePath);
|
|
// We run deploy commands in parallel to maximize the connection
|
|
// throughput.
|
|
DeployCommands.Add(
|
|
RunAdbCommand(Params, DeviceName, Commandline, null,
|
|
ERunOptions.Default | ERunOptions.NoWaitForExit));
|
|
// But we limit the parallel commands to avoid overwhelming
|
|
// memory resources.
|
|
if (DeployCommands.Count == DeployMaxParallelCommands)
|
|
{
|
|
while (DeployCommands.Count > DeployMaxParallelCommands / 2)
|
|
{
|
|
Thread.Sleep(1);
|
|
DeployCommands.RemoveWhere(
|
|
delegate (IProcessResult r)
|
|
{
|
|
return r.HasExited;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
foreach (IProcessResult deploy_result in DeployCommands)
|
|
{
|
|
deploy_result.WaitForExit();
|
|
}
|
|
|
|
// delete the .obb file, since it will cause nothing we just deployed to be used
|
|
RunAdbCommand(Params, DeviceName, "shell rm " + DeviceObbName);
|
|
}
|
|
else if (SC.Archive)
|
|
{
|
|
// deploy the obb if there is one
|
|
string ObbPath = Path.Combine(SC.StageDirectory, GetFinalObbName(ApkName));
|
|
if (File.Exists(ObbPath))
|
|
{
|
|
// cache some strings
|
|
string BaseCommandline = "push";
|
|
|
|
string Commandline = string.Format("{0} \"{1}\" \"{2}\"", BaseCommandline, ObbPath, DeviceObbName);
|
|
RunAdbCommand(Params, DeviceName, Commandline);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// cache some strings
|
|
string BaseCommandline = "push";
|
|
|
|
string FinalRemoteDir = RemoteDir;
|
|
/*
|
|
// handle the special case of the UE4Commandline.txt when using content only game (UE4Game)
|
|
if (!Params.IsCodeBasedProject)
|
|
{
|
|
FinalRemoteDir = "/mnt/sdcard/UE4Game";
|
|
}
|
|
*/
|
|
|
|
string RemoteFilename = IntermediateCmdLineFile.Replace(SC.StageDirectory, FinalRemoteDir).Replace("\\", "/");
|
|
string Commandline = string.Format("{0} \"{1}\" \"{2}\"", BaseCommandline, IntermediateCmdLineFile, RemoteFilename);
|
|
RunAdbCommand(Params, DeviceName, Commandline);
|
|
}
|
|
|
|
// write new timestamp for APK (do it here since RemoteDir will now exist)
|
|
if (bNeedAPKInstall)
|
|
{
|
|
int SuccessCode = 0;
|
|
RunAndLogAdbCommand(Params, DeviceName, "shell \"echo 'APK: " + APKLastUpdateTime + "' > " + RemoteDir + "/APKFileStamp.txt\"", out SuccessCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Internal usage for GetPackageName */
|
|
private static string PackageLine = null;
|
|
private static Mutex PackageInfoMutex = new Mutex();
|
|
private static string LaunchableActivityLine = null;
|
|
|
|
/** Run an external exe (and capture the output), given the exe path and the commandline. */
|
|
private static string GetPackageInfo(string ApkName, bool bRetrieveVersionCode)
|
|
{
|
|
// we expect there to be one, so use the first one
|
|
string AaptPath = GetAaptPath();
|
|
|
|
PackageInfoMutex.WaitOne();
|
|
|
|
var ExeInfo = new ProcessStartInfo(AaptPath, "dump badging \"" + ApkName + "\"");
|
|
ExeInfo.UseShellExecute = false;
|
|
ExeInfo.RedirectStandardOutput = true;
|
|
using (var GameProcess = Process.Start(ExeInfo))
|
|
{
|
|
PackageLine = null;
|
|
LaunchableActivityLine = null;
|
|
GameProcess.BeginOutputReadLine();
|
|
GameProcess.OutputDataReceived += ParsePackageName;
|
|
GameProcess.WaitForExit();
|
|
}
|
|
|
|
PackageInfoMutex.ReleaseMutex();
|
|
|
|
string ReturnValue = null;
|
|
if (PackageLine != null)
|
|
{
|
|
// the line should look like: package: name='com.epicgames.qagame' versionCode='1' versionName='1.0'
|
|
string[] Tokens = PackageLine.Split("'".ToCharArray());
|
|
int TokenIndex = bRetrieveVersionCode ? 3 : 1;
|
|
if (Tokens.Length >= TokenIndex + 1)
|
|
{
|
|
ReturnValue = Tokens[TokenIndex];
|
|
}
|
|
}
|
|
return ReturnValue;
|
|
}
|
|
|
|
/** Returns the launch activity name to launch (must call GetPackageInfo first), returns "com.epicgames.ue4.SplashActivity" default if not found */
|
|
private static string GetLaunchableActivityName()
|
|
{
|
|
string ReturnValue = "com.epicgames.ue4.SplashActivity";
|
|
if (LaunchableActivityLine != null)
|
|
{
|
|
// the line should look like: launchable-activity: name='com.epicgames.ue4.SplashActivity' label='TappyChicken' icon=''
|
|
string[] Tokens = LaunchableActivityLine.Split("'".ToCharArray());
|
|
if (Tokens.Length >= 2)
|
|
{
|
|
ReturnValue = Tokens[1];
|
|
}
|
|
}
|
|
return ReturnValue;
|
|
}
|
|
|
|
/** Simple function to pipe output asynchronously */
|
|
private static void ParsePackageName(object Sender, DataReceivedEventArgs Event)
|
|
{
|
|
// DataReceivedEventHandler is fired with a null string when the output stream is closed. We don't want to
|
|
// print anything for that event.
|
|
if (!String.IsNullOrEmpty(Event.Data))
|
|
{
|
|
if (PackageLine == null)
|
|
{
|
|
string Line = Event.Data;
|
|
if (Line.StartsWith("package:"))
|
|
{
|
|
PackageLine = Line;
|
|
}
|
|
}
|
|
if (LaunchableActivityLine == null)
|
|
{
|
|
string Line = Event.Data;
|
|
if (Line.StartsWith("launchable-activity:"))
|
|
{
|
|
LaunchableActivityLine = Line;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static private string CachedAaptPath = null;
|
|
static private string LastAndroidHomePath = null;
|
|
|
|
private static uint GetRevisionValue(string VersionString)
|
|
{
|
|
// read up to 4 sections (ie. 20.0.3.5), first section most significant
|
|
// each section assumed to be 0 to 255 range
|
|
uint Value = 0;
|
|
try
|
|
{
|
|
string[] Sections= VersionString.Split(".".ToCharArray());
|
|
Value |= (Sections.Length > 0) ? (uint.Parse(Sections[0]) << 24) : 0;
|
|
Value |= (Sections.Length > 1) ? (uint.Parse(Sections[1]) << 16) : 0;
|
|
Value |= (Sections.Length > 2) ? (uint.Parse(Sections[2]) << 8) : 0;
|
|
Value |= (Sections.Length > 3) ? uint.Parse(Sections[3]) : 0;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignore poorly formed version
|
|
}
|
|
return Value;
|
|
}
|
|
|
|
private static string GetAaptPath()
|
|
{
|
|
// return cached path if ANDROID_HOME has not changed
|
|
string HomePath = Environment.ExpandEnvironmentVariables("%ANDROID_HOME%");
|
|
if (CachedAaptPath != null && LastAndroidHomePath == HomePath)
|
|
{
|
|
return CachedAaptPath;
|
|
}
|
|
|
|
// get a list of the directories in build-tools.. may be more than one set installed (or none which is bad)
|
|
string[] Subdirs = Directory.GetDirectories(Path.Combine(HomePath, "build-tools"));
|
|
if (Subdirs.Length == 0)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_AndroidBuildToolsPathNotFound, "Failed to find %ANDROID_HOME%/build-tools subdirectory. Run SDK manager and install build-tools.");
|
|
}
|
|
|
|
// valid directories will have a source.properties with the Pkg.Revision (there is no guarantee we can use the directory name as revision)
|
|
string BestToolPath = null;
|
|
uint BestVersion = 0;
|
|
foreach (string CandidateDir in Subdirs)
|
|
{
|
|
string AaptFilename = Path.Combine(CandidateDir, Utils.IsRunningOnMono ? "aapt" : "aapt.exe");
|
|
uint RevisionValue = 0;
|
|
|
|
if (File.Exists(AaptFilename))
|
|
{
|
|
string SourcePropFilename = Path.Combine(CandidateDir, "source.properties");
|
|
if (File.Exists(SourcePropFilename))
|
|
{
|
|
string[] PropertyContents = File.ReadAllLines(SourcePropFilename);
|
|
foreach (string PropertyLine in PropertyContents)
|
|
{
|
|
if (PropertyLine.StartsWith("Pkg.Revision="))
|
|
{
|
|
RevisionValue = GetRevisionValue(PropertyLine.Substring(13));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// remember it if newer version or haven't found one yet
|
|
if (RevisionValue > BestVersion || BestToolPath == null)
|
|
{
|
|
BestVersion = RevisionValue;
|
|
BestToolPath = AaptFilename;
|
|
}
|
|
}
|
|
|
|
if (BestToolPath == null)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_AndroidBuildToolsPathNotFound, "Failed to find %ANDROID_HOME%/build-tools subdirectory with aapt. Run SDK manager and install build-tools.");
|
|
}
|
|
|
|
CachedAaptPath = BestToolPath;
|
|
LastAndroidHomePath = HomePath;
|
|
|
|
Log("Using this aapt: {0}", CachedAaptPath);
|
|
|
|
return CachedAaptPath;
|
|
}
|
|
|
|
private string GetBestDeviceArchitecture(ProjectParams Params, string DeviceName)
|
|
{
|
|
bool bMakeSeparateApks = UnrealBuildTool.AndroidExports.ShouldMakeSeparateApks();
|
|
// if we are joining all .so's into a single .apk, there's no need to find the best one - there is no other one
|
|
if (!bMakeSeparateApks)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
var AppArchitectures = AndroidExports.CreateToolChain(Params.RawProjectPath).GetAllArchitectures();
|
|
|
|
// ask the device
|
|
IProcessResult ABIResult = RunAdbCommand(Params, DeviceName, " shell getprop ro.product.cpu.abi", null, ERunOptions.AppMustExist);
|
|
|
|
// the output is just the architecture
|
|
string DeviceArch = UnrealBuildTool.AndroidExports.GetUE4Arch(ABIResult.Output.Trim());
|
|
|
|
// if the architecture wasn't built, look for a backup
|
|
if (!AppArchitectures.Contains(DeviceArch))
|
|
{
|
|
// go from 64 to 32-bit
|
|
if (DeviceArch == "-arm64")
|
|
{
|
|
DeviceArch = "-armv7";
|
|
}
|
|
// go from 64 to 32-bit
|
|
else if (DeviceArch == "-x64")
|
|
{
|
|
if (!AppArchitectures.Contains("-x86"))
|
|
{
|
|
DeviceArch = "-x86";
|
|
}
|
|
// if it didn't have 32-bit x86, look for 64-bit arm for emulation
|
|
// @todo android 64-bit: x86_64 most likely can't emulate arm64 at this ponit
|
|
// else if (Array.IndexOf(AppArchitectures, "-arm64") == -1)
|
|
// {
|
|
// DeviceArch = "-arm64";
|
|
// }
|
|
// finally try for 32-bit arm emulation (Houdini)
|
|
else
|
|
{
|
|
DeviceArch = "-armv7";
|
|
}
|
|
}
|
|
// use armv7 (with Houdini emulation)
|
|
else if (DeviceArch == "-x86")
|
|
{
|
|
DeviceArch = "-armv7";
|
|
}
|
|
else
|
|
{
|
|
// future-proof by dropping back to armv7 for unknown
|
|
DeviceArch = "-armv7";
|
|
}
|
|
}
|
|
|
|
// if after the fallbacks, we still don't have it, we can't continue
|
|
if (!AppArchitectures.Contains(DeviceArch))
|
|
{
|
|
throw new AutomationException(ExitCode.Error_NoApkSuitableForArchitecture, "Unable to run because you don't have an apk that is usable on {0}. Looked for {1}", DeviceName, DeviceArch);
|
|
}
|
|
|
|
return DeviceArch;
|
|
}
|
|
|
|
private string GetBestGPUArchitecture(ProjectParams Params, string DeviceName)
|
|
{
|
|
bool bMakeSeparateApks = UnrealBuildTool.AndroidExports.ShouldMakeSeparateApks();
|
|
// if we are joining all .so's into a single .apk, there's no need to find the best one - there is no other one
|
|
if (!bMakeSeparateApks)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
var AppGPUArchitectures = AndroidExports.CreateToolChain(Params.RawProjectPath).GetAllGPUArchitectures();
|
|
|
|
// get the device extensions
|
|
IProcessResult ExtensionsResult = RunAdbCommand(Params, DeviceName, "shell dumpsys SurfaceFlinger", null, ERunOptions.AppMustExist);
|
|
string Extensions = ExtensionsResult.Output.Trim();
|
|
|
|
// look for AEP support (on device and in project)
|
|
if (Extensions.Contains("GL_ANDROID_extension_pack_es31a") && Extensions.Contains("GL_EXT_color_buffer_half_float"))
|
|
{
|
|
if (AppGPUArchitectures.Contains("-esdeferred"))
|
|
{
|
|
return "-esdeferred";
|
|
}
|
|
}
|
|
|
|
return "-es2";
|
|
}
|
|
|
|
public override IProcessResult RunClient(ERunOptions ClientRunFlags, string ClientApp, string ClientCmdLine, ProjectParams Params)
|
|
{
|
|
//make a copy of the device names, we'll be working through them
|
|
List<string> DeviceNames = new List<string>();
|
|
//same with the package names
|
|
List<string> PackageNames = new List<string>();
|
|
|
|
foreach (string DeviceName in Params.DeviceNames)
|
|
{
|
|
//save the device name
|
|
DeviceNames.Add(DeviceName);
|
|
|
|
//get the package name and save that
|
|
string DeviceArchitecture = GetBestDeviceArchitecture(Params, DeviceName);
|
|
string GPUArchitecture = GetBestGPUArchitecture(Params, DeviceName);
|
|
string ApkName = ClientApp + DeviceArchitecture + ".apk";
|
|
if (!File.Exists(ApkName))
|
|
{
|
|
ApkName = GetFinalApkName(Params, Path.GetFileNameWithoutExtension(ClientApp), true, DeviceArchitecture, GPUArchitecture);
|
|
}
|
|
Console.WriteLine("Apk='{0}', ClientApp='{1}', ExeName='{2}'", ApkName, ClientApp, Params.ProjectGameExeFilename);
|
|
|
|
// run aapt to get the name of the intent
|
|
string PackageName = GetPackageInfo(ApkName, false);
|
|
if (PackageName == null)
|
|
{
|
|
throw new AutomationException(ExitCode.Error_FailureGettingPackageInfo, "Failed to get package name from " + ClientApp);
|
|
}
|
|
|
|
PackageNames.Add(PackageName);
|
|
|
|
// clear the log for the device
|
|
RunAdbCommand(Params, DeviceName, "logcat -c");
|
|
|
|
// start the app on device!
|
|
string CommandLine = "shell am start -n " + PackageName + "/" + GetLaunchableActivityName();
|
|
RunAdbCommand(Params, DeviceName, CommandLine, null, ClientRunFlags);
|
|
|
|
// save the output to the staging directory
|
|
string LogPath = Path.Combine(Params.BaseStageDirectory, "Android\\logs");
|
|
Directory.CreateDirectory(LogPath);
|
|
}
|
|
|
|
//now check if each device still has the game running, and time out if it's taking too long
|
|
DateTime StartTime = DateTime.Now;
|
|
int TimeOutSeconds = Params.RunTimeoutSeconds;
|
|
|
|
while (DeviceNames.Count > 0)
|
|
{
|
|
for(int DeviceIndex = 0; DeviceIndex < DeviceNames.Count; DeviceIndex++)
|
|
{
|
|
string DeviceName = DeviceNames[DeviceIndex];
|
|
|
|
//replace the port name in the case of deploy while adb is using wifi
|
|
string SanitizedDeviceName = DeviceName.Replace(":", "_");
|
|
|
|
bool FinishedRunning = false;
|
|
IProcessResult ProcessesResult = RunAdbCommand(Params, DeviceName, "shell ps", null, ERunOptions.SpewIsVerbose);
|
|
|
|
string RunningProcessList = ProcessesResult.Output;
|
|
if (!RunningProcessList.Contains(PackageNames[DeviceIndex]))
|
|
{
|
|
FinishedRunning = true;
|
|
}
|
|
|
|
Thread.Sleep(10);
|
|
|
|
if(!FinishedRunning)
|
|
{
|
|
TimeSpan DeltaRunTime = DateTime.Now - StartTime;
|
|
if ((DeltaRunTime.TotalSeconds > TimeOutSeconds) && (TimeOutSeconds != 0))
|
|
{
|
|
Log("Device: " + DeviceName + " timed out while waiting for run to finish");
|
|
FinishedRunning = true;
|
|
}
|
|
}
|
|
|
|
//log the results, then clear out the device from our list
|
|
if(FinishedRunning)
|
|
{
|
|
// this is just to get the ue4 log to go to the output
|
|
RunAdbCommand(Params, DeviceName, "logcat -d -s UE4 -s Debug");
|
|
|
|
// get the log we actually want to save
|
|
IProcessResult LogFileProcess = RunAdbCommand(Params, DeviceName, "logcat -d", null, ERunOptions.AppMustExist);
|
|
|
|
string LogPath = Path.Combine(Params.BaseStageDirectory, "Android\\logs");
|
|
string LogFilename = Path.Combine(LogPath, "devicelog" + SanitizedDeviceName + ".log");
|
|
string ServerLogFilename = Path.Combine(CmdEnv.LogFolder, "devicelog" + SanitizedDeviceName + ".log");
|
|
|
|
File.WriteAllText(LogFilename, LogFileProcess.Output);
|
|
File.WriteAllText(ServerLogFilename, LogFileProcess.Output);
|
|
|
|
DeviceNames.RemoveAt(DeviceIndex);
|
|
PackageNames.RemoveAt(DeviceIndex);
|
|
|
|
--DeviceIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override void GetFilesToDeployOrStage(ProjectParams Params, DeploymentContext SC)
|
|
{
|
|
// Add any Android shader cache files
|
|
string ProjectShaderDir = Path.Combine(Path.GetDirectoryName(Path.GetFullPath(Params.RawProjectPath.ToString())), "Build/ShaderCaches/Android");
|
|
SC.StageFiles(StagedFileType.UFS, ProjectShaderDir, "*.*", true, null, null, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets cook platform name for this platform.
|
|
/// </summary>
|
|
/// <returns>Cook platform string.</returns>
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android";
|
|
}
|
|
|
|
public override bool DeployPakInternalLowerCaseFilenames()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override bool DeployLowerCaseFilenames(bool bUFSFile)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override string LocalPathToTargetPath(string LocalPath, string LocalRoot)
|
|
{
|
|
return LocalPath.Replace("\\", "/").Replace(LocalRoot, "../../..");
|
|
}
|
|
|
|
public override bool IsSupported { get { return true; } }
|
|
|
|
public override string Remap(string Dest)
|
|
{
|
|
return Dest;
|
|
}
|
|
|
|
public override PakType RequiresPak(ProjectParams Params)
|
|
{
|
|
// if packaging is enabled, always create a pak, otherwise use the Params.Pak value
|
|
return Params.Package ? PakType.Always : PakType.DontCare;
|
|
}
|
|
public override bool SupportsMultiDeviceDeploy
|
|
{
|
|
get
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
public override bool RequiresPackageToDeploy
|
|
{
|
|
get { return true; }
|
|
}
|
|
*/
|
|
|
|
public override List<string> GetDebugFileExtentions()
|
|
{
|
|
return new List<string> { };
|
|
}
|
|
|
|
public override void StripSymbols(string SourceFileName, string TargetFileName)
|
|
{
|
|
AndroidExports.StripSymbols(SourceFileName, TargetFileName);
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformMulti : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_Multi";
|
|
}
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "Multi");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformATC : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_ATC";
|
|
}
|
|
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "ATC");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformDXT : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_DXT";
|
|
}
|
|
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "DXT");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformETC1 : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_ETC1";
|
|
}
|
|
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "ETC1");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformETC2 : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_ETC2";
|
|
}
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "ETC2");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformPVRTC : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_PVRTC";
|
|
}
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "PVRTC");
|
|
}
|
|
}
|
|
|
|
public class AndroidPlatformASTC : AndroidPlatform
|
|
{
|
|
public override string GetCookPlatform(bool bDedicatedServer, bool bIsClientOnly)
|
|
{
|
|
return "Android_ASTC";
|
|
}
|
|
public override TargetPlatformDescriptor GetTargetPlatformDescriptor()
|
|
{
|
|
return new TargetPlatformDescriptor(TargetPlatformType, "ASTC");
|
|
}
|
|
}
|