diff --git a/Assets/Photon.meta b/Assets/Photon.meta new file mode 100644 index 0000000..7b4ed7f --- /dev/null +++ b/Assets/Photon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cb56c3824ef5c8e40acfc607e00aded4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion.meta b/Assets/Photon/Fusion.meta new file mode 100644 index 0000000..fb14d0e --- /dev/null +++ b/Assets/Photon/Fusion.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 985f71308df30494eab1c01a553f6450 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies.meta b/Assets/Photon/Fusion/Assemblies.meta new file mode 100644 index 0000000..92cb20e --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6b498a0317a268b4f95f38e8863f3e53 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll new file mode 100644 index 0000000..f819111 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug new file mode 100644 index 0000000..49dca4a Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug.meta new file mode 100644 index 0000000..c476b28 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: dfe46f6ff6fe58c47aa967992595b12e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.meta new file mode 100644 index 0000000..e02e4f2 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Common.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: a4d1b22b416816f4a830209a6058fe35 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug new file mode 100644 index 0000000..da491e6 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug.meta new file mode 100644 index 0000000..80c4c01 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Common.pdb.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 64771868c228aac4caec4e19fea3aa53 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml b/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml new file mode 100644 index 0000000..7b71eb9 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml @@ -0,0 +1,156 @@ + + + + Fusion.Common + + + + + Task Factory is used to create new Tasks and Schedule long running Tasks + + + + + Stores a Task Factory ready made to be used with Unity + + + + + Task Scheduler based on the TaskFactory currently set + + + + + Setup a new TaskFactory tailored to work with Unity + + + + + Create a new TaskFactory reference with settings to work wiht Unity + + TaskFactory ref with the right arguments set + + + + Queue that usese Sync locks for each operations + + Type used by the internal Queue + + + + Compress the byte array uisng GZip + + Original byte array + Compressed byte array + + + + Decompress the byte array using GZip + + Compressed byte array + Decompressed byte array + + + + Represents the current ID of the communicator. + + + + + Sends a package data using the communication system + + Event Code used to send the Package + Target Actor of the Package + Flag if this Package should be sent reliably + Data Buffer + Buffer Length + + + + Retrieve a Data Package + + Data Package Sender + Buffer to be filled with the Data + Buffer length + Total number of bytes written to buffer + + + + Check if there are data package to be retrieved + + True if the internal buffer has pendind data + + + + Push a new Package into the communicator queues + + Data Sender Actor + Event Code of the Package + Package + + + + Register a callback for a specific Message Type + + + + + + + Send a Protocol Message using the communicator system + + Target Actor of the Protocol Message + Protocol Message to be sent + + + + Step the Communicator internals + + + + + Represents a Protocol Message + + Used to tag the Messages in . + + + + + Collection of simple JSON Utility methods + + + + + Removes from a JSON serialized by Unity Serializer the "referenes" field. + This aims to reduce the JSON size when sending accross the network + + JSON output of "JsonUtility.ToJson" call + Same JSON but without the "referenes" object + + + + Instructs all sub-Samplers to register a frame change. The next Add will go to the next frame section. + + + + + Get Sampler from the connection specific collection. + + + + + Get Sampler from the common samplers (non-connection specific) collection. + + + + + Sort 4 byte integers + + array to sort + temp array that is >= aLength + length of array + integer array with size (1 << RADIX) + integer array with size (1 << RADIX) * 4 + + + diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml.meta new file mode 100644 index 0000000..8bea615 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Common.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d1aa52c25b7410d409a610b7ae0e2be5 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll new file mode 100644 index 0000000..6ac7c98 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug new file mode 100644 index 0000000..79e2df1 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug.meta new file mode 100644 index 0000000..565c665 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3b0bcfb09765a4a4ebf152ab4db50ef4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.meta new file mode 100644 index 0000000..ac114ab --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 7de3b8b9e1263ad479e2d0c4261b7646 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug new file mode 100644 index 0000000..7b6c6f5 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug.meta new file mode 100644 index 0000000..859ef0d --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.pdb.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2522e117b83964644bbf25885af7bea2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml new file mode 100644 index 0000000..b0e680e --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml @@ -0,0 +1,4507 @@ + + + + Fusion.Realtime + + + + + Responsible for running a thread that will keep the LoadBalancingClient connected to the Cloud + when the application is in the background + + + Fusion Realtime Client + + This will deal with all communication done with the Photon Cloud + + + Fusion Realtime Client + + This will deal with all communication done with the Photon Cloud + + + + Defines for how long the Fallback Thread should keep the connection, before it may time out as usual. + We want to the Client to keep it's connection when an app is in the background (and doesn't call Update / Service Clients should not keep their connection indefinitely in the background, so after some milliseconds, the Fallback Thread should stop keeping it up. + + + + Delay between each ACK/Ping to Photon Cloud + + + + + Signal if ACK was Sent + + + + + Thread ID + + + + + Check if Thread is running by checking the Thread ID + + + + + Starts a new rhread to send ACK to Cloud + + + + + Stop the currently running background thread + + + + + ACK Thread Action. + + This will check the last time the LBC sent anything to the Cloud, if it's more than , + an ACK Msg will be sent, so the Cloud keeps the connection active. + + True if the Thread should keep running, false otherwise. + + + + Handles all received events from the Photon Cloud + + Event Data + + + + Send data to another Actor on the Room + + Target Actor of the Event + Event Code + Data to be sent + Buffer Length + Flag to set reliability on the Event + True if the event was sent, false otherwise + + + + Utility method used to extract the content of a from a object holder. + This is necessary so there are no references of Photon Lib DLLs other than on the Fusion.Realtime project + + Data Object Holder, this must be a reference of a + Buffer to write the content of the array slice + Output size of the written buffer + True if the extraction was done correctly, false otherwise + + + + Fusion Plugin Name for request + + + + + Alternative Name Server for CN Region + + + + + Flag to signal if the Client is Ready to perform cloud action and it is in a Room + + + + + Signal if some room property has changed + + + + + Change the Custom Properties of the current Room + + New set of Custom Properties + True if the change was made, false otherwise + + + + Change the IsVisible Property of the current Room + + New value of IsVisible + True if the change was made, false otherwise + + + + Change the IsOpen Property of the current Room + + New value of IsOpen + True if the change was made, false otherwise + + + + Get Current Custom Properties of Room + + Custom Properties Dictionary + + + + Used to keep the client communication + + + + + Build a new EnterRoomParams ref using the default configs and optional Room Name + + Which lobby the Room should exits + Room Name, if not set, a Random Name will be used + Optional Room Custom Properties + EnterRoomParams reference. + + + + Build a new OpJoinRandomRoomParams that will be used to setup which Room the local peer wants to join + + Type of Lobby to search rooms + Optional list of filter parameters + OpJoinRandomRoomParams reference + + + + Convert a into a pair of and respectively + representing the custom properties of a session and the property names that will be published on the Lobby + + Dictionary to be converted + Hashtable with all allowed Custom Properties + String array with all public key names + + + + Connect to master server. + + Client + App settings + Runs client.Service() during the operation + When connected to master server callback was called. + Is thrown when the connection terminated + Is thrown when the authentication failed + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Runs reconnect and rejoin. + + Client object + Runs client.Service() during the operation + Returns when inside the room + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Disconnects the client. + + Client. + Runs client.Service() during the operation + Returns when the client has successfully disconnected + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Create and join a room. + + Client object + Enter room params + Set ErrorCode as result on RoomCreateFailed or RoomJoinFailed + Runs client.Service() during the operation + When the room has been entered + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Create or Join a Room. + + Client object + Enter room params + Set ErrorCode as result on RoomCreateFailed or RoomJoinFailed + Runs client.Service() during the operation + When the room has been entered + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Join room. + + Client object + Enter room params + Set ErrorCode as result when JoinRoomFailed + Runs client.Service() during the operation + When room has been entered + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Join random or Create room + + Client object + Join random room params + Enter room params + Set ErrorCode as result when operation fails with ErrorCode + Runs client.Service() during the operation + When inside a room + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Join a Random Room + + Client object + Join random room params + Set ErrorCode as result when operation fails with ErrorCode + Runs client.Service() during the operation + When inside a room + Is thrown when the connection terminated + Is thrown when the operation could not be started + Is thrown when the operation completed unsuccessfully + Is thrown when the operation timed out + + + + Create a instance, sets up the Photon callbacks, schedules removing them, create a connection service task. + The handler will monitor the Photon callbacks and complete, fault accordingly. + Use the callbacks to change the default handling. + can complete with ErrorCode.Ok, exception on errors and a timeout . + + Client + The default implementation will throw an exception on every unexpected result, set this to false to return a result instead + Runs client.Service() during the operation + Photon Connection Handler object + + + + Starts a task that calls every updateIntervalMs milliseconds. + The task is stopped by the cancellation token from . + It will set an exception on the TaskCompletionSource if after the timeout it is still not completed. + + Client + Cancellation token to stop the update loop + Completion source is notified on an exception in Service() + + + + Convert a into a + + + + + Convert a into a + + + + + Calculate the total size a would take when serialized + + Hashtable to check the size + Total size of the Hashtable in serialized format + + + + Convert the Room Custom Properties into a + + RoomInfo to extract the custom properties + with the data + + + + Calculate the total size a would take when serialized + + Dictionary to check the size + Total size of the Dictionary in serialized format + + + + Settings for Photon application(s) and the server to connect to. + + + This is Serializable for Unity, so it can be included in ScriptableObject instances. + + + + AppId for Photon Fusion. + + + AppId for Photon Chat. + + + AppId for Photon Voice. + + + The AppVersion can be used to identify builds and will split the AppId distinct "Virtual AppIds" (important for matchmaking). + + + If false, the app will attempt to connect to a Master Server (which is obsolete but sometimes still necessary). + if true, Server points to a NameServer (or is null, using the default), else it points to a MasterServer. + + + Can be set to any of the Photon Cloud's region names to directly connect to that region. + if this IsNullOrEmpty() AND UseNameServer == true, use BestRegion. else, use a server + + + + The address (hostname or IP) of the server to connect to. + + + If not null, this sets the port of the first Photon server to connect to (that will "forward" the client as needed). + + + The address (hostname or IP and port) of the proxy server. + + + Enables a fallback to another protocol in case a connect to the Name Server fails. + See: LoadBalancingClient.EnableProtocolFallback. + + + Defines how authentication is done. On each system, once or once via a WSS connection (safe). + + + If true, the client will request the list of currently available lobbies. + + + Log level for the network lib. + + + If true, the Server field contains a Master Server address (if any address at all). + + + If true, the client should fetch the region list from the Name Server and find the one with best ping. + See "Best Region" in the online docs. + + + If true, the default nameserver address for the Photon Cloud should be used. + + + If true, the default ports for a protocol will be used. + + + ToString but with more details. + + + Checks if a string is a Guid by attempting to create one. + The potential guid to check. + True if new Guid(val) did not fail. + + + + Get a Copy from the into a new instance. + + Copy of + + + + Photon client to log information and statistics from. + + + + Option to let the fallback thread call Disconnect after the KeepAliveInBackground time. Default: false. + + If set to true, the thread will disconnect the client regularly, should the client not call SendOutgoingCommands / Service. + This may happen due to an app being in background (and not getting a lot of CPU time) or when loading assets. + + If false, a regular timeout time will have to pass (on top) to time out the client. + + + + Defines for how long the Fallback Thread should keep the connection, before it may time out as usual. + We want to the Client to keep it's connection when an app is in the background (and doesn't call Update / Service Clients should not keep their connection indefinitely in the background, so after some milliseconds, the Fallback Thread should stop keeping it up. + + + Counts how often the Fallback Thread called SendAcksOnly, which is purely of interest to monitor if the game logic called SendOutgoingCommands as intended. + + + True if a fallback thread is running. Will call the client's SendAcksOnly() method to keep the connection up. + + + Keeps the ConnectionHandler, even if a new scene gets loaded. + + + Indicates that the app is closing. Set in OnApplicationQuit(). + + + Called by Unity when the application gets closed. The UnityEngine will also call OnDisable, which disconnects. + + + + + + Called by Unity when the application gets closed. Disconnects if OnApplicationQuit() was called before. + + + A thread which runs independent from the Update() calls. Keeps connections online while loading or in background. See . + + + + Internally used class, containing de/serialization methods for various Unity-specific classes. + Adding those to the Photon serialization protocol allows you to send them in events, etc. + + + + Register de/serializer methods for Unity specific types. Makes the types usable in RaiseEvent and PUN. + + + + This static class defines some useful extension methods for several existing classes (e.g. Vector3, float and others). + + + + + Merges all keys from addHash into the target. Adds new keys and updates the values of existing keys in target. + + The IDictionary to update. + The IDictionary containing data to merge into target. + + + + Merges keys of type string to target Hashtable. + + + Does not remove keys from target (so non-string keys CAN be in target if they were before). + + The target IDictionary passed in plus all string-typed keys from the addHash. + A IDictionary that should be merged partly into target to update it. + + + Helper method for debugging of IDictionary content, including type-information. Using this is not performant. + Should only be used for debugging as necessary. + Some Dictionary or Hashtable. + String of the content of the IDictionary. + + + + Helper method for debugging of object[] content. Using this is not performant. + Should only be used for debugging as necessary. + Any object[]. + A comma-separated string containing each value's ToString(). + + + + This method copies all string-typed keys of the original into a new Hashtable. + + + Does not recurse (!) into hashes that might be values in the root-hash. + This does not modify the original. + + The original IDictonary to get string-typed keys from. + New Hashtable containing only string-typed keys of the original. + + + + This method copies all string-typed keys of the original into a new Hashtable. + + + Does not recurse (!) into hashes that might be values in the root-hash. + This does not modify the original. + + The original IDictonary to get string-typed keys from. + New Hashtable containing only string-typed keys of the original. + + + Used by StripKeysWithNullValues. + + By making keysWithNullValue a static variable to clear before using, allocations only happen during the warm-up phase + as the list needs to grow. Once it hit the high water mark for keys you need to remove. + + + + Removes all keys with null values. + + Photon properties are removed by setting their value to null. Changes the original IDictionary! + Uses lock(keysWithNullValue), which should be no problem in expected use cases. + + The IDictionary to strip of keys with null value. + + + Removes all keys with null values. + + Photon properties are removed by setting their value to null. Changes the original IDictionary! + Uses lock(keysWithNullValue), which should be no problem in expected use cases. + + The IDictionary to strip of keys with null value. + + + + Checks if a particular integer value is in an int-array. + + This might be useful to look up if a particular actorNumber is in the list of players of a room. + The array of ints to check. + The number to lookup in target. + True if nr was found in target. + + + + Used to store info about a friend's online state and in which room he/she is. + + + + + State values for a client, which handles switching Photon server types, some operations, etc. + + \ingroup publicApi + + + Peer is created but not used yet. + + + Transition state while connecting to a server. On the Photon Cloud this sends the AppId and AuthenticationValues (UserID). + + + Not Used. + + + The client sent an OpJoinLobby and if this was done on the Master Server, it will result in. Depending on the lobby, it gets room listings. + + + The client is in a lobby, connected to the MasterServer. Depending on the lobby, it gets room listings. + + + Transition from MasterServer to GameServer. + + + Transition to GameServer (client authenticates and joins/creates a room). + + + Connected to GameServer (going to auth and join game). + + + Transition state while joining or creating a room on GameServer. + + + The client entered a room. The CurrentRoom and Players are known and you can now raise events. + + + Transition state when leaving a room. + + + Transition from GameServer to MasterServer (after leaving a room/game). + + + Connecting to MasterServer (includes sending authentication values). + + + The client disconnects (from any server). This leads to state Disconnected. + + + The client is no longer connected (to any server). Connect to MasterServer to go on. + + + Connected to MasterServer. You might use matchmaking or join a lobby now. + + + Client connects to the NameServer. This process includes low level connecting and setting up encryption. When done, state becomes ConnectedToNameServer. + + + Client is connected to the NameServer and established encryption already. You should call OpGetRegions or ConnectToRegionMaster. + + + Clients disconnects (specifically) from the NameServer (usually to connect to the MasterServer). + + + Client was unable to connect to Name Server and will attempt to connect with an alternative network protocol (TCP). + + + + Internal state, how this peer gets into a particular room (joining it or creating it). + + + + This client creates a room, gets into it (no need to join) and can set room properties. + + + The room existed already and we join into it (not setting room properties). + + + Done on Master Server and (if successful) followed by a Join on Game Server. + + + Done on Master Server and (if successful) followed by a Join or Create on Game Server. + + + Client is either joining or creating a room. On Master- and Game-Server. + + + Enumeration of causes for Disconnects (used in LoadBalancingClient.DisconnectedCause). + Read the individual descriptions to find out what to do about this type of disconnect. + + + No error was tracked. + + + OnStatusChanged: The server is not available or the address is wrong. Make sure the port is provided and the server is up. + + + OnStatusChanged: Dns resolution for a hostname failed. The exception for this is being catched and logged with error level. + + + OnStatusChanged: The server address was parsed as IPv4 illegally. An illegal address would be e.g. 192.168.1.300. IPAddress.TryParse() will let this pass but our check won't. + + + OnStatusChanged: Some internal exception caused the socket code to fail. This may happen if you attempt to connect locally but the server is not available. In doubt: Contact Exit Games. + + + OnStatusChanged: The server disconnected this client due to timing out (missing acknowledgement from the client). + + + OnStatusChanged: This client detected that the server's responses are not received in due time. + + + OnStatusChanged: The server disconnected this client from within the room's logic (the C# code). + + + OnStatusChanged: The server disconnected this client for unknown reasons. + + + OnOperationResponse: Authenticate in the Photon Cloud with invalid AppId. Update your subscription or contact Exit Games. + + + OnOperationResponse: Authenticate in the Photon Cloud with invalid client values or custom authentication setup in Cloud Dashboard. + + + The authentication ticket should provide access to any Photon Cloud server without doing another authentication-service call. However, the ticket expired. + + + OnOperationResponse: Authenticate (temporarily) failed when using a Photon Cloud subscription without CCU Burst. Update your subscription. + + + OnOperationResponse: Authenticate when the app's Photon Cloud subscription is locked to some (other) region(s). Update your subscription or master server address. + + + OnOperationResponse: Operation that's (currently) not available for this client (not authorized usually). Only tracked for op Authenticate. + + + OnStatusChanged: The client disconnected from within the logic (the C# code). + + + The client called an operation too frequently and got disconnected due to hitting the OperationLimit. This triggers a client-side disconnect, too. + To protect the server, some operations have a limit. When an OperationResponse fails with ErrorCode.OperationLimitReached, the client disconnects. + + + The client received a "Disconnect Message" from the server. Check the debug logs for details. + + + Available server (types) for internally used field: server. + Photon uses 3 different roles of servers: Name Server, Master Server and Game Server. + + + This server is where matchmaking gets done and where clients can get lists of rooms in lobbies. + + + This server handles a number of rooms to execute and relay the messages between players (in a room). + + + This server is used initially to get the address (IP) of a Master Server for a specific region. Not used for Photon OnPremise (self hosted). + + + Defines which sort of app the LoadBalancingClient is used for: Realtime or Voice. + + + Realtime apps are for gaming / interaction. Also used by PUN 2. + + + Voice apps stream audio. + + + Fusion clients are for matchmaking and relay in Photon Fusion. + + + + Defines how the communication gets encrypted. + + + + + This is the default encryption mode: Messages get encrypted only on demand (when you send operations with the "encrypt" parameter set to true). + + + + + With this encryption mode for UDP, the connection gets setup and all further datagrams get encrypted almost entirely. On-demand message encryption (like in PayloadEncryption) is unavailable. + + + + + With this encryption mode for UDP, the connection gets setup with random sequence numbers and all further datagrams get encrypted almost entirely. On-demand message encryption (like in PayloadEncryption) is unavailable. + + + + + Datagram Encryption with GCM. + + + + Container for port definitions. + + + Typical ports: UDP: 5058 or 27000, TCP: 4533, WSS: 19093 or 443. + + + Typical ports: UDP: 5056 or 27002, TCP: 4530, WSS: 19090 or 443. + + + Typical ports: UDP: 5055 or 27001, TCP: 4531, WSS: 19091 or 443. + + + + This class implements the Photon LoadBalancing workflow by using a LoadBalancingPeer. + It keeps a state and will automatically execute transitions between the Master and Game Servers. + + + This class (and the Player class) should be extended to implement your own game logic. + You can override CreatePlayer as "factory" method for Players and return your own Player instances. + The State of this class is essential to know when a client is in a lobby (or just on the master) + and when in a game where the actual gameplay should take place. + Extension notes: + An extension of this class should override the methods of the IPhotonPeerListener, as they + are called when the state changes. Call base.method first, then pick the operation or state you + want to react to and put it in a switch-case. + We try to provide demo to each platform where this api can be used, so lookout for those. + + + + + The client uses a LoadBalancingPeer as API to communicate with the server. + This is public for ease-of-use: Some methods like OpRaiseEvent are not relevant for the connection state and don't need a override. + + + + + Gets or sets the binary protocol version used by this client + + + Use this always instead of setting it via + () directly, especially when WSS protocol is used. + + + + The version of your client. A new version also creates a new "virtual app" to separate players from older client versions. + + + The AppID as assigned from the Photon Cloud. If you host yourself, this is the "regular" Photon Server Application Name (most likely: "LoadBalancing"). + + + The ClientAppType defines which sort of AppId should be expected. The LoadBalancingClient supports Realtime and Voice app types. Default: Realtime. + + + User authentication values to be sent to the Photon server right after connecting. + Set this property or pass AuthenticationValues by Connect(..., authValues). + + + Enables the new Authentication workflow. + + + Defines how the communication gets encrypted. + + + Optionally contains a protocol which will be used on Master- and GameServer. + + When using AuthMode = AuthModeOption.AuthOnceWss, the client uses a wss-connection on the NameServer but another protocol on the other servers. + As the NameServer sends an address, which is different per protocol, it needs to know the expected protocol. + + This is nullable by design. In many cases, the protocol on the NameServer is not different from the other servers. + If set, the operation AuthOnce will contain this value and the OpAuth response on the NameServer will execute a protocol switch. + + + + Simplifies getting the token for connect/init requests, if this feature is enabled. + + + Internally used cache for the server's token. Identifies a user/session and can be used to rejoin. + + + True if this client uses a NameServer to get the Master Server address. + This value is public, despite being an internal value, which should only be set by this client. + + + Name Server Host Name for Photon Cloud. Without port and without any prefix. + + + Name Server Address for Photon Cloud (based on current protocol). You can use the default values and usually won't have to set this value. + + + Name Server port per protocol (the UDP port is different than TCP, etc). + + + Replaced by ServerPortOverrides. + + + Defines overrides for server ports. Used per server-type if > 0. Important: You must change these when the protocol changes! + + Typical ports are listed in PhotonPortDefinition. + + Instead of using the port provided from the servers, the specified port is used (independent of the protocol). + If a value is 0 (default), the port is not being replaced. + + Different protocols have different typical ports per server-type. + https://doc.photonengine.com/en-us/pun/current/reference/tcp-and-udp-port-numbers + + In case of using the AuthMode AutOnceWss, the name server's protocol is wss, while udp or tcp will be used on the master server and game server. + Set the ports accordingly per protocol and server. + + + + Enables a fallback to another protocol in case a connect to the Name Server fails. + + When connecting to the Name Server fails for a first time, the client will select an alternative + network protocol and re-try to connect. + + The fallback will use the default Name Server port as defined by ProtocolToNameServerPort. + + The fallback for TCP is UDP. All other protocols fallback to TCP. + + + + The currently used server address (if any). The type of server is define by Server property. + + + Your Master Server address. In PhotonCloud, call ConnectToRegionMaster() to find your Master Server. + + In the Photon Cloud, explicit definition of a Master Server Address is not best practice. + The Photon Cloud has a "Name Server" which redirects clients to a specific Master Server (per Region and AppId). + + + + The game server's address for a particular room. In use temporarily, as assigned by master. + + + The server this client is currently connected or connecting to. + + Each server (NameServer, MasterServer, GameServer) allow some operations and reject others. + + + + + Defines a proxy URL for WebSocket connections. Can be the proxy or point to a .pac file. + + + This URL supports various definitions: + + "user:pass@proxyaddress:port"
+ "proxyaddress:port"
+ "system:"
+ "pac:"
+ "pac:http://host/path/pacfile.pac"
+ + Important: Don't define a protocol, except to point to a pac file. the proxy address should not begin with http:// or https://. +
+
+ + Backing field for property. + + + Current state this client is in. Careful: several states are "transitions" that lead to other states. + + + Returns if this client is currently connected or connecting to some type of server. + This is even true while switching servers. Use IsConnectedAndReady to check only for those states that enable you to send Operations. + + + + A refined version of IsConnected which is true only if your connection is ready to send operations. + + + Not all operations can be called on all types of servers. If an operation is unavailable on the currently connected server, + this will result in a OperationResponse with ErrorCode != 0. + + Examples: The NameServer allows OpGetRegions which is not available anywhere else. + The MasterServer does not allow you to send events (OpRaiseEvent) and on the GameServer you are unable to join a lobby (OpJoinLobby). + + To check which server you are on, use: . + + + + Register a method to be called when this client's ClientState gets set. + This can be useful to react to being connected, joined into a room, etc. + + + Register a method to be called when an event got dispatched. Gets called after the LoadBalancingClient handled the internal events first. + + This is an alternative to extending LoadBalancingClient to override OnEvent(). + + Note that OnEvent is calling EventReceived after it handled internal events first. + That means for example: Joining players will already be in the player list but leaving + players will already be removed from the room. + + + + Register a method to be called when an operation response is received. + + This is an alternative to extending LoadBalancingClient to override OnOperationResponse(). + + Note that OnOperationResponse gets executed before your Action is called. + That means for example: The OpJoinLobby response already set the state to "JoinedLobby" + and the response to OpLeave already triggered the Disconnect before this is called. + + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Wraps up the target objects for a group of callbacks, so they can be called conveniently. + By using Add or Remove, objects can "subscribe" or "unsubscribe" for this group of callbacks. + + + Summarizes (aggregates) the different causes for disconnects of a client. + + A disconnect can be caused by: errors in the network connection or some vital operation failing + (which is considered "high level"). While operations always trigger a call to OnOperationResponse, + connection related changes are treated in OnStatusChanged. + The DisconnectCause is set in either case and summarizes the causes for any disconnect in a single + state value which can be used to display (or debug) the cause for disconnection. + + + + Internal value if the client is in a lobby. + This is used to re-set this.State, when joining/creating a room fails. + + + The lobby this client currently uses. Defined when joining a lobby or creating rooms + + + + If enabled, the client will get a list of available lobbies from the Master Server. + + + Set this value before the client connects to the Master Server. While connected to the Master + Server, a change has no effect. + + Implement OptionalInfoCallbacks.OnLobbyStatisticsUpdate, to get the list of used lobbies. + + The lobby statistics can be useful if your title dynamically uses lobbies, depending (e.g.) + on current player activity or such. + In this case, getting a list of available lobbies, their room-count and player-count can + be useful info. + + ConnectUsingSettings sets this to the PhotonServerSettings value. + + + + Internal lobby stats cache, used by LobbyStatistics. + + + The local player is never null but not valid unless the client is in a room, too. The ID will be -1 outside of rooms. + + + + The nickname of the player (synced with others). Same as client.LocalPlayer.NickName. + + + + An ID for this user. Sent in OpAuthenticate when you connect. If not set, the PlayerName is applied during connect. + + On connect, if the UserId is null or empty, the client will copy the PlayName to UserId. If PlayerName is not set either + (before connect), the server applies a temporary ID which stays unknown to this client and other clients. + + The UserId is what's used in FindFriends and for fetching data for your account (with WebHooks e.g.). + + By convention, set this ID before you connect, not while being connected. + There is no error but the ID won't change while being connected. + + + + The current room this client is connected to (null if none available). + + + Is true while being in a room (this.state == ClientState.Joined). + + Aside from polling this value, game logic should implement IMatchmakingCallbacks in some class + and react when that gets called.
+ OpRaiseEvent, OpLeave and some other operations can only be used (successfully) when the client is in a room.. +
+
+ + Statistic value available on master server: Players on master (looking for games). + + + Statistic value available on master server: Players in rooms (playing). + + + Statistic value available on master server: Rooms currently created. + + + Internally used to decide if a room must be created or joined on game server. + + + Used when the client arrives on the GS, to join the room with the correct values. + + + Used to cache a failed "enter room" operation on the Game Server, to return to the Master Server before calling a fail-callback. + + + Maximum of userIDs that can be sent in one friend list request. + + + Contains the list of names of friends to look up their state on the server. + + + Internal flag to know if the client currently fetches a friend list. + + + The cloud region this client connects to. Set by ConnectToRegionMaster(). Not set if you don't use a NameServer! + + + The cluster name provided by the Name Server. + + The value is provided by the OpResponse for OpAuthenticate/OpAuthenticateOnce. + Default: null. This value only ever updates from the Name Server authenticate response. + + + + Contains the list if enabled regions this client may use. Null, unless the client got a response to OpGetRegions. + + + Stores the best region summary of a previous session to speed up connecting. + + + Set when the best region pinging is done. + + + Internal connection setting/flag. If the client should connect to the best region or not. + + It's set in the Connect...() methods. Only ConnectUsingSettings() sets it to true. + If true, client will ping available regions and select the best. + A bestRegionSummaryFromStorage can be used to cut the ping time short. + + + + Definition of parameters for encryption data (included in Authenticate operation response). + + + + Key for encryption mode + + + + + Key for first secret + + + + + Key for second secret + + + + Add if true, remove if false. + + + Creates a LoadBalancingClient with UDP protocol or the one specified. + Specifies the network protocol to use for connections. + + + Creates a LoadBalancingClient, setting various values needed before connecting. + The Master Server's address to connect to. Used in Connect. + The AppId of this title. Needed for the Photon Cloud. Find it in the Dashboard. + A version for this client/build. In the Photon Cloud, players are separated by AppId, GameVersion and Region. + Specifies the network protocol to use for connections. + + + + Gets the NameServer Address (with prefix and port), based on the set protocol (this.LoadBalancingPeer.UsedProtocol). + + NameServer Address (with prefix and port). + + + + Starts the "process" to connect to a Master Server, using MasterServerAddress and AppId properties. + + + To connect to the Photon Cloud, use ConnectUsingSettings() or ConnectToRegionMaster(). + + The process to connect includes several steps: the actual connecting, establishing encryption, authentification + (of app and optionally the user) and connecting to the MasterServer + + Users can connect either anonymously or use "Custom Authentication" to verify each individual player's login. + Custom Authentication in Photon uses external services and communities to verify users. While the client provides a user's info, + the service setup is done in the Photon Cloud Dashboard. + The parameter authValues will set this.AuthValues and use them in the connect process. + + Connecting to the Photon Cloud might fail due to: + - Network issues (OnStatusChanged() StatusCode.ExceptionOnConnect) + - Region not available (OnOperationResponse() for OpAuthenticate with ReturnCode == ErrorCode.InvalidRegion) + - Subscription CCU limit reached (OnOperationResponse() for OpAuthenticate with ReturnCode == ErrorCode.MaxCcuReached) + + + + + Connects to the NameServer for Photon Cloud, where a region and server list can be obtained. + + + If the workflow was started or failed right away. + + + + Connects you to a specific region's Master Server, using the Name Server to find the IP. + + + If the region is null or empty, no connection will be made. + If the region (code) provided is not available, the connection process will fail on the Name Server. + This method connects only to the region defined. No "Best Region" pinging will be done. + + If the region string does not contain a "/", this means no specific cluster is requested. + To support "Sharding", the region gets a "/*" postfix in this case, to select a random cluster. + + If the operation could be sent. If false, no operation was sent. + + + + Privately used only for reconnecting. + + + + Can be used to reconnect to the master server after a disconnect. + Common use case: Press the Lock Button on a iOS device and you get disconnected immediately. + + + + Can be used to return to a room quickly by directly reconnecting to a game server to rejoin a room. + + + Rejoining room will not send any player properties. Instead client will receive up-to-date ones from server. + If you want to set new player properties, do it once rejoined. + + False, if the conditions are not met. Then, this client does not attempt the ReconnectAndRejoin. + + + Disconnects the peer from a server or stays disconnected. If the client / peer was connected, a callback will be triggered. + + Disconnect will attempt to notify the server of the client closing the connection. + + Clients that are in a room, will leave the room. If the room's playerTTL > 0, the player will just become inactive (and may rejoin). + + This method will not change the current State, if this client State is PeerCreated, Disconnecting or Disconnected. + In those cases, there is also no callback for the disconnect. The DisconnectedCause will only change if the client was connected. + + + + + Private Disconnect variant that sets the state, too. + + + + + Useful to test loss of connection which will end in a client timeout. This modifies LoadBalancingPeer.NetworkSimulationSettings. Read remarks. + + + Use with care as this sets LoadBalancingPeer.IsSimulationEnabled.
+ Read LoadBalancingPeer.IsSimulationEnabled to check if this is on or off, if needed.
+ + If simulateTimeout is true, LoadBalancingPeer.NetworkSimulationSettings.IncomingLossPercentage and + LoadBalancingPeer.NetworkSimulationSettings.OutgoingLossPercentage will be set to 100.
+ Obviously, this overrides any network simulation settings done before.
+ + If you want fine-grained network simulation control, use the NetworkSimulationSettings.
+ + The timeout will lead to a call to , as usual in a client timeout. + + You could modify this method (or use NetworkSimulationSettings) to deliberately run into a server timeout by + just setting the OutgoingLossPercentage = 100 and the IncomingLossPercentage = 0. +
+ If true, a connection loss is simulated. If false, the simulation ends. +
+ + + This method dispatches all available incoming commands and then sends this client's outgoing commands. + It uses DispatchIncomingCommands and SendOutgoingCommands to do that. + + + The Photon client libraries are designed to fit easily into a game or application. The application + is in control of the context (thread) in which incoming events and responses are executed and has + full control of the creation of UDP/TCP packages. + + Sending packages and dispatching received messages are two separate tasks. Service combines them + into one method at the cost of control. It calls DispatchIncomingCommands and SendOutgoingCommands. + + Call this method regularly (10..50 times a second). + + This will Dispatch ANY received commands (unless a reliable command in-order is still missing) and + events AND will send queued outgoing commands. Fewer calls might be more effective if a device + cannot send many packets per second, as multiple operations might be combined into one package. + + + You could replace Service by: + + while (DispatchIncomingCommands()); //Dispatch until everything is Dispatched... + SendOutgoingCommands(); //Send a UDP/TCP package with outgoing messages + + + + + + + While on the NameServer, this gets you the list of regional servers (short names and their IPs to ping them). + + If the operation could be sent. If false, no operation was sent (e.g. while not connected to the NameServer). + + + + Request the rooms and online status for a list of friends. All clients should set a unique UserId before connecting. The result is available in this.FriendList. + + + Used on Master Server to find the rooms played by a selected list of users. + The result will be stored in LoadBalancingClient.FriendList, which is null before the first server response. + + Users identify themselves by setting a UserId in the LoadBalancingClient instance. + This will send the ID in OpAuthenticate during connect (to master and game servers). + Note: Changing a player's name doesn't make sense when using a friend list. + + The list of usernames must be fetched from some other source (not provided by Photon). + + + Internal:
+ The server response includes 2 arrays of info (each index matching a friend from the request):
+ ParameterCode.FindFriendsResponseOnlineList = bool[] of online states
+ ParameterCode.FindFriendsResponseRoomIdList = string[] of room names (empty string if not in a room)
+
+ The options may be used to define which state a room must match to be returned. +
+ Array of friend's names (make sure they are unique). + Options that affect the result of the FindFriends operation. + If the operation could be sent (requires connection). +
+ + If already connected to a Master Server, this joins the specified lobby. This request triggers an OnOperationResponse() call and the callback OnJoinedLobby(). + The lobby to join. Use null for default lobby. + If the operation could be sent. False, if the client is not IsConnectedAndReady or when it's not connected to a Master Server. + + + Opposite of joining a lobby. You don't have to explicitly leave a lobby to join another (client can be in one max, at any time). + If the operation could be sent (has to be connected). + + + + Joins a random room that matches the filter. Will callback: OnJoinedRoom or OnJoinRandomFailed. + + + Used for random matchmaking. You can join any room or one with specific properties defined in opJoinRandomRoomParams. + + You can use expectedCustomRoomProperties and expectedMaxPlayers as filters for accepting rooms. + If you set expectedCustomRoomProperties, a room must have the exact same key values set at Custom Properties. + You need to define which Custom Room Properties will be available for matchmaking when you create a room. + See: OpCreateRoom(string roomName, RoomOptions roomOptions, TypedLobby lobby) + + This operation fails if no rooms are fitting or available (all full, closed or not visible). + It may also fail when actually joining the room which was found. Rooms may close, become full or empty anytime. + + This method can only be called while the client is connected to a Master Server so you should + implement the callback OnConnectedToMaster. + Check the return value to make sure the operation will be called on the server. + Note: There will be no callbacks if this method returned false. + + + This client's State is set to ClientState.Joining immediately, when the operation could + be called. In the background, the client will switch servers and call various related operations. + + When you're in the room, this client's State will become ClientState.Joined. + + + When entering a room, this client's Player Custom Properties will be sent to the room. + Use LocalPlayer.SetCustomProperties to set them, even while not yet in the room. + Note that the player properties will be cached locally and are not wiped when leaving a room. + + More about matchmaking: + https://doc.photonengine.com/en-us/realtime/current/reference/matchmaking-and-lobby + + You can define an array of expectedUsers, to block player slots in the room for these users. + The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages. + + Optional definition of properties to filter rooms in random matchmaking. + If the operation could be sent currently (requires connection to Master Server). + + + + Attempts to join a room that matches the specified filter and creates a room if none found. + + + This operation is a combination of filter-based random matchmaking with the option to create a new room, + if no fitting room exists. + The benefit of that is that the room creation is done by the same operation and the room can be found + by the very next client, looking for similar rooms. + + There are separate parameters for joining and creating a room. + + This method can only be called while connected to a Master Server. + This client's State is set to ClientState.Joining immediately. + + Either IMatchmakingCallbacks.OnJoinedRoom or IMatchmakingCallbacks.OnCreatedRoom get called. + + More about matchmaking: + https://doc.photonengine.com/en-us/realtime/current/reference/matchmaking-and-lobby + + Check the return value to make sure the operation will be called on the server. + Note: There will be no callbacks if this method returned false. + + If the operation will be sent (requires connection to Master Server). + + + + Creates a new room. Will callback: OnCreatedRoom and OnJoinedRoom or OnCreateRoomFailed. + + + When successful, the client will enter the specified room and callback both OnCreatedRoom and OnJoinedRoom. + In all error cases, OnCreateRoomFailed gets called. + + Creating a room will fail if the room name is already in use or when the RoomOptions clashing + with one another. Check the EnterRoomParams reference for the various room creation options. + + + This method can only be called while the client is connected to a Master Server so you should + implement the callback OnConnectedToMaster. + Check the return value to make sure the operation will be called on the server. + Note: There will be no callbacks if this method returned false. + + + When you're in the room, this client's State will become ClientState.Joined. + + + When entering a room, this client's Player Custom Properties will be sent to the room. + Use LocalPlayer.SetCustomProperties to set them, even while not yet in the room. + Note that the player properties will be cached locally and are not wiped when leaving a room. + + You can define an array of expectedUsers, to block player slots in the room for these users. + The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages. + + Definition of properties for the room to create. + If the operation could be sent currently (requires connection to Master Server). + + + + Joins a specific room by name and creates it on demand. Will callback: OnJoinedRoom or OnJoinRoomFailed. + + + Useful when players make up a room name to meet in: + All involved clients call the same method and whoever is first, also creates the room. + + When successful, the client will enter the specified room. + The client which creates the room, will callback both OnCreatedRoom and OnJoinedRoom. + Clients that join an existing room will only callback OnJoinedRoom. + In all error cases, OnJoinRoomFailed gets called. + + Joining a room will fail, if the room is full, closed or when the user + already is present in the room (checked by userId). + + To return to a room, use OpRejoinRoom. + + This method can only be called while the client is connected to a Master Server so you should + implement the callback OnConnectedToMaster. + Check the return value to make sure the operation will be called on the server. + Note: There will be no callbacks if this method returned false. + + This client's State is set to ClientState.Joining immediately, when the operation could + be called. In the background, the client will switch servers and call various related operations. + + When you're in the room, this client's State will become ClientState.Joined. + + + If you set room properties in roomOptions, they get ignored when the room is existing already. + This avoids changing the room properties by late joining players. + + When entering a room, this client's Player Custom Properties will be sent to the room. + Use LocalPlayer.SetCustomProperties to set them, even while not yet in the room. + Note that the player properties will be cached locally and are not wiped when leaving a room. + + You can define an array of expectedUsers, to block player slots in the room for these users. + The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages. + + Definition of properties for the room to create or join. + If the operation could be sent currently (requires connection to Master Server). + + + + Joins a room by name. Will callback: OnJoinedRoom or OnJoinRoomFailed. + + + Useful when using lobbies or when players follow friends or invite each other. + + When successful, the client will enter the specified room and callback via OnJoinedRoom. + In all error cases, OnJoinRoomFailed gets called. + + Joining a room will fail if the room is full, closed, not existing or when the user + already is present in the room (checked by userId). + + To return to a room, use OpRejoinRoom. + When players invite each other and it's unclear who's first to respond, use OpJoinOrCreateRoom instead. + + This method can only be called while the client is connected to a Master Server so you should + implement the callback OnConnectedToMaster. + Check the return value to make sure the operation will be called on the server. + Note: There will be no callbacks if this method returned false. + + A room's name has to be unique (per region, appid and gameversion). + When your title uses a global matchmaking or invitations (e.g. an external solution), + keep regions and the game versions in mind to join a room. + + + This client's State is set to ClientState.Joining immediately, when the operation could + be called. In the background, the client will switch servers and call various related operations. + + When you're in the room, this client's State will become ClientState.Joined. + + + When entering a room, this client's Player Custom Properties will be sent to the room. + Use LocalPlayer.SetCustomProperties to set them, even while not yet in the room. + Note that the player properties will be cached locally and are not wiped when leaving a room. + + You can define an array of expectedUsers, to reserve player slots in the room for friends or party members. + The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages. + + Definition of properties for the room to join. + If the operation could be sent currently (requires connection to Master Server). + + + + Rejoins a room by roomName (using the userID internally to return). Will callback: OnJoinedRoom or OnJoinRoomFailed. + + + Used to return to a room, before this user was removed from the players list. + Internally, the userID will be checked by the server, to make sure this user is in the room (active or inactice). + + In contrast to join, this operation never adds a players to a room. It will attempt to retake an existing + spot in the playerlist or fail. This makes sure the client doean't accidentally join a room when the + game logic meant to re-activate an existing actor in an existing room. + + This method will fail on the server, when the room does not exist, can't be loaded (persistent rooms) or + when the userId is not in the player list of this room. This will lead to a callback OnJoinRoomFailed. + + Rejoining room will not send any player properties. Instead client will receive up-to-date ones from server. + If you want to set new player properties, do it once rejoined. + + + + + Leaves the current room, optionally telling the server that the user is just becoming inactive. Will callback: OnLeftRoom. + + + + OpLeaveRoom skips execution when the room is null or the server is not GameServer or the client is disconnecting from GS already. + OpLeaveRoom returns false in those cases and won't change the state, so check return of this method. + + In some cases, this method will skip the OpLeave call and just call Disconnect(), + which not only leaves the room but also the server. Disconnect also triggers a leave and so that workflow is is quicker. + + If true, this player becomes inactive in the game and can return later (if PlayerTTL of the room is != 0). + WebFlag: Securely transmit the encrypted object AuthCookie to the web service in PathLeave webhook when available + If the current room could be left (impossible while not in a room). + + + Gets a list of rooms matching the (non empty) SQL filter for the given SQL-typed lobby. + + Operation is only available for lobbies of type SqlLobby and the filter can not be empty. + It will check those conditions and fail locally, returning false. + + This is an async request which triggers a OnOperationResponse() call. + + + The lobby to query. Has to be of type SqlLobby. + The sql query statement. + If the operation could be sent (has to be connected). + + + + Updates and synchronizes a Player's Custom Properties. Optionally, expectedProperties can be provided as condition. + + + Custom Properties are a set of string keys and arbitrary values which is synchronized + for the players in a Room. They are available when the client enters the room, as + they are in the response of OpJoin and OpCreate. + + Custom Properties either relate to the (current) Room or a Player (in that Room). + + Both classes locally cache the current key/values and make them available as + property: CustomProperties. This is provided only to read them. + You must use the method SetCustomProperties to set/modify them. + + Any client can set any Custom Properties anytime (when in a room). + It's up to the game logic to organize how they are best used. + + You should call SetCustomProperties only with key/values that are new or changed. This reduces + traffic and performance. + + Unless you define some expectedProperties, setting key/values is always permitted. + In this case, the property-setting client will not receive the new values from the server but + instead update its local cache in SetCustomProperties. + + If you define expectedProperties, the server will skip updates if the server property-cache + does not contain all expectedProperties with the same values. + In this case, the property-setting client will get an update from the server and update it's + cached key/values at about the same time as everyone else. + + The benefit of using expectedProperties can be only one client successfully sets a key from + one known value to another. + As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally. + When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their + actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to + take the item will have it (and the others fail to set the ownership). + + Properties get saved with the game state for Turnbased games (which use IsPersistent = true). + + Defines which player the Custom Properties belong to. ActorID of a player. + Hashtable of Custom Properties that changes. + Provide some keys/values to use as condition for setting the new values. Client must be in room. + Defines if the set properties should be forwarded to a WebHook. Client must be in room. + + False if propertiesToSet is null or empty or have zero string keys. + If not in a room, returns true if local player and expectedProperties and webFlags are null. + False if actorNr is lower than or equal to zero. + Otherwise, returns if the operation could be sent to the server. + + + + Internally used to cache and set properties (including well known properties). + Requires being in a room (because this attempts to send an operation which will fail otherwise). + + + + Updates and synchronizes this Room's Custom Properties. Optionally, expectedProperties can be provided as condition. + + + Custom Properties are a set of string keys and arbitrary values which is synchronized + for the players in a Room. They are available when the client enters the room, as + they are in the response of OpJoin and OpCreate. + + Custom Properties either relate to the (current) Room or a Player (in that Room). + + Both classes locally cache the current key/values and make them available as + property: CustomProperties. This is provided only to read them. + You must use the method SetCustomProperties to set/modify them. + + Any client can set any Custom Properties anytime (when in a room). + It's up to the game logic to organize how they are best used. + + You should call SetCustomProperties only with key/values that are new or changed. This reduces + traffic and performance. + + Unless you define some expectedProperties, setting key/values is always permitted. + In this case, the property-setting client will not receive the new values from the server but + instead update its local cache in SetCustomProperties. + + If you define expectedProperties, the server will skip updates if the server property-cache + does not contain all expectedProperties with the same values. + In this case, the property-setting client will get an update from the server and update it's + cached key/values at about the same time as everyone else. + + The benefit of using expectedProperties can be only one client successfully sets a key from + one known value to another. + As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally. + When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their + actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to + take the item will have it (and the others fail to set the ownership). + + Properties get saved with the game state for Turnbased games (which use IsPersistent = true). + + Hashtable of Custom Properties that changes. + Provide some keys/values to use as condition for setting the new values. + Defines web flags for an optional PathProperties webhook. + + False if propertiesToSet is null or empty or have zero string keys. + Otherwise, returns if the operation could be sent to the server. + + + + Internally used to cache and set properties (including well known properties). + Requires being in a room (because this attempts to send an operation which will fail otherwise). + + + + Send an event with custom code/type and any content to the other players in the same room. + + Identifies this type of event (and the content). Your game's event codes can start with 0. + Any serializable datatype (including Hashtable like the other OpRaiseEvent overloads). + Contains used send options. If you pass null, the default options will be used. + Send options for reliable, encryption etc + If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands. + + + + Operation to handle this client's interest groups (for events in room). + + + Note the difference between passing null and byte[0]: + null won't add/remove any groups. + byte[0] will add/remove all (existing) groups. + First, removing groups is executed. This way, you could leave all groups and join only the ones provided. + + Changes become active not immediately but when the server executes this operation (approximately RTT/2). + + Groups to remove from interest. Null will not remove any. A byte[0] will remove all. + Groups to add to interest. Null will not add any. A byte[0] will add all current. + If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands. + + + + Privately used to read-out properties coming from the server in events and operation responses (which might be a bit tricky). + + + + + Privately used only to read properties for a distinct actor (which might be the hashtable OR a key-pair value IN the actorProperties). + + + + + Internally used to set the LocalPlayer's ID (from -1 to the actual in-room ID). + + New actor ID (a.k.a actorNr) assigned when joining a room. + + + + Called internally, when a game was joined or created on the game server successfully. + + + This reads the response, finds out the local player's actorNumber (a.k.a. Player.ID) and applies properties of the room and players. + Errors for these operations are to be handled before this method is called. + + Contains the server's response for an operation called by this peer. + + + + Factory method to create a player instance - override to get your own player-type with custom features. + + The name of the player to be created. + The player ID (a.k.a. actorNumber) of the player to be created. + Sets the distinction if the player to be created is your player or if its assigned to someone else. + The custom properties for this new player + The newly created player + + + Internal "factory" method to create a room-instance. + + + Debug output of low level api (and this client). + This method is not responsible to keep up the state of a LoadBalancingClient. Calling base.DebugReturn on overrides is optional. + + + + Uses the OperationResponses provided by the server to advance the internal state and call ops as needed. + + + When this method finishes, it will call your OnOpResponseAction (if any). This way, you can get any + operation response without overriding this class. + + To implement a more complex game/app logic, you should implement your own class that inherits the + LoadBalancingClient. Override this method to use your own operation-responses easily. + + This method is essential to update the internal state of a LoadBalancingClient, so overriding methods + must call base.OnOperationResponse(). + + Contains the server's response for an operation called by this peer. + + + + Uses the connection's statusCodes to advance the internal state and call operations as needed. + + This method is essential to update the internal state of a LoadBalancingClient. Overriding methods must call base.OnStatusChanged. + + + + Uses the photonEvent's provided by the server to advance the internal state and call ops as needed. + + This method is essential to update the internal state of a LoadBalancingClient. Overriding methods must call base.OnEvent. + + + In Photon 4, "raw messages" will get their own callback method in the interface. Not used yet. + + + A callback of the RegionHandler, provided in OnRegionListReceived. + The regionHandler wraps up best region and other region relevant info. + + + + This operation makes Photon call your custom web-service by path/name with the given parameters (converted into Json). + Use as a callback. + + + A WebRPC calls a custom, http-based function on a server you provide. The uriPath is relative to a "base path" + which is configured server-side. The sent parameters get converted from C# types to Json. Vice versa, the response + of the web-service will be converted to C# types and sent back as normal operation response. + + To use this feature, you have to setup your server: + + For a Photon Cloud application, + visit the Dashboard and setup "WebHooks". The BaseUrl is used for WebRPCs as well. + + The class is a helper-class that extracts the most valuable content from the WebRPC + response. + + The url path to call, relative to the baseUrl configured on Photon's server-side. + The parameters to send to the web-service method. + Defines if the authentication cookie gets sent to a WebHook (if setup). + + + + Registers an object for callbacks for the implemented callback-interfaces. + + + Adding and removing callback targets is queued to not mess with callbacks in execution. + Internally, this means that the addition/removal is done before the LoadBalancingClient + calls the next callbacks. This detail should not affect a game's workflow. + + The covered callback interfaces are: IConnectionCallbacks, IMatchmakingCallbacks, + ILobbyCallbacks, IInRoomCallbacks, IOnEventCallback and IWebRpcCallback. + + See: + + The object that registers to get callbacks from this client. + + + + Unregisters an object from callbacks for the implemented callback-interfaces. + + + Adding and removing callback targets is queued to not mess with callbacks in execution. + Internally, this means that the addition/removal is done before the LoadBalancingClient + calls the next callbacks. This detail should not affect a game's workflow. + + The covered callback interfaces are: IConnectionCallbacks, IMatchmakingCallbacks, + ILobbyCallbacks, IInRoomCallbacks, IOnEventCallback and IWebRpcCallback. + + See: + + The object that unregisters from getting callbacks. + + + + Applies queued callback cahnges from a queue to the actual containers. Will cause exceptions if used while callbacks execute. + + + There is no explicit check that this is not called during callbacks, however the implemented, private logic takes care of this. + + + + Helper method to cast and apply a target per (interface) type. + Either of the interfaces for callbacks. + The queued change to apply (add or remove) some target. + The container that calls callbacks on it's list of targets. + + + + Collection of "organizational" callbacks for the Realtime Api to cover: Connection and Regions. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called to signal that the "low level connection" got established but before the client can call operation on the server. + + + After the (low level transport) connection is established, the client will automatically send + the Authentication operation, which needs to get a response before the client can call other operations. + + Your logic should wait for either: OnRegionListReceived or OnConnectedToMaster. + + This callback is useful to detect if the server can be reached at all (technically). + Most often, it's enough to implement OnDisconnected(DisconnectCause cause) and check for the cause. + + This is not called for transitions from the masterserver to game servers. + + + + + Called when the client is connected to the Master Server and ready for matchmaking and other tasks. + + + The list of available rooms won't become available unless you join a lobby via LoadBalancingClient.OpJoinLobby. + You can join rooms and create them even without being in a lobby. The default lobby is used in that case. + + + + + Called after disconnecting from the Photon server. It could be a failure or an explicit disconnect call + + + The reason for this disconnect is provided as DisconnectCause. + + + + + Called when the Name Server provided a list of regions for your title. + + + This callback is called as soon as the list is available. No pings were sent for Best Region selection yet. + If the client is set to connect to the Best Region (lowest ping), one or more regions get pinged. + Not all regions are pinged. As soon as the results are final, the client will connect to the best region, + so you can check the ping results when connected to the Master Server. + + Check the RegionHandler class description, to make use of the provided values. + + The currently used RegionHandler. + + + + Called when your Custom Authentication service responds with additional data. + + + Custom Authentication services can include some custom data in their response. + When present, that data is made available in this callback as Dictionary. + While the keys of your data have to be strings, the values can be either string or a number (in Json). + You need to make extra sure, that the value type is the one you expect. Numbers become (currently) int64. + + Example: void OnCustomAuthenticationResponse(Dictionary<string, object> data) { ... } + + + + + + Called when the custom authentication failed. Followed by disconnect! + + + Custom Authentication can fail due to user-input, bad tokens/secrets. + If authentication is successful, this method is not called. Implement OnJoinedLobby() or OnConnectedToMaster() (as usual). + + During development of a game, it might also fail due to wrong configuration on the server side. + In those cases, logging the debugMessage is very important. + + Unless you setup a custom authentication service for your app (in the [Dashboard](https://dashboard.photonengine.com)), + this won't be called! + + Contains a debug message why authentication failed. This has to be fixed during development. + + + + Collection of "organizational" callbacks for the Realtime Api to cover the Lobby. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called on entering a lobby on the Master Server. The actual room-list updates will call OnRoomListUpdate. + + + While in the lobby, the roomlist is automatically updated in fixed intervals (which you can't modify in the public cloud). + The room list gets available via OnRoomListUpdate. + + + + + Called after leaving a lobby. + + + When you leave a lobby, [OpCreateRoom](@ref OpCreateRoom) and [OpJoinRandomRoom](@ref OpJoinRandomRoom) + automatically refer to the default lobby. + + + + + Called for any update of the room-listing while in a lobby (InLobby) on the Master Server. + + + Each item is a RoomInfo which might include custom properties (provided you defined those as lobby-listed when creating a room). + Not all types of lobbies provide a listing of rooms to the client. Some are silent and specialized for server-side matchmaking. + + + + + Called when the Master Server sent an update for the Lobby Statistics. + + + This callback has two preconditions: + EnableLobbyStatistics must be set to true, before this client connects. + And the client has to be connected to the Master Server, which is providing the info about lobbies. + + + + + Collection of "organizational" callbacks for the Realtime Api to cover Matchmaking. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called when the server sent the response to a FindFriends request. + + + After calling OpFindFriends, the Master Server will cache the friend list and send updates to the friend + list. The friends includes the name, userId, online state and the room (if any) for each requested user/friend. + + Use the friendList to update your UI and store it, if the UI should highlight changes. + + + + + Called when this client created a room and entered it. OnJoinedRoom() will be called as well. + + + This callback is only called on the client which created a room (see OpCreateRoom). + + As any client might close (or drop connection) anytime, there is a chance that the + creator of a room does not execute OnCreatedRoom. + + If you need specific room properties or a "start signal", implement OnMasterClientSwitched() + and make each new MasterClient check the room's state. + + + + + Called when the server couldn't create a room (OpCreateRoom failed). + + + Creating a room may fail for various reasons. Most often, the room already exists (roomname in use) or + the RoomOptions clash and it's impossible to create the room. + + When creating a room fails on a Game Server: + The client will cache the failure internally and returns to the Master Server before it calls the fail-callback. + This way, the client is ready to find/create a room at the moment of the callback. + In this case, the client skips calling OnConnectedToMaster but returning to the Master Server will still call OnConnected. + Treat callbacks of OnConnected as pure information that the client could connect. + + Operation ReturnCode from the server. + Debug message for the error. + + + + Called when the LoadBalancingClient entered a room, no matter if this client created it or simply joined. + + + When this is called, you can access the existing players in Room.Players, their custom properties and Room.CustomProperties. + + In this callback, you could create player objects. For example in Unity, instantiate a prefab for the player. + + If you want a match to be started "actively", enable the user to signal "ready" (using OpRaiseEvent or a Custom Property). + + + + + Called when a previous OpJoinRoom call failed on the server. + + + Joining a room may fail for various reasons. Most often, the room is full or does not exist anymore + (due to someone else being faster or closing the room). + + When joining a room fails on a Game Server: + The client will cache the failure internally and returns to the Master Server before it calls the fail-callback. + This way, the client is ready to find/create a room at the moment of the callback. + In this case, the client skips calling OnConnectedToMaster but returning to the Master Server will still call OnConnected. + Treat callbacks of OnConnected as pure information that the client could connect. + + Operation ReturnCode from the server. + Debug message for the error. + + + + Called when a previous OpJoinRandom call failed on the server. + + + The most common causes are that a room is full or does not exist (due to someone else being faster or closing the room). + + This operation is only ever sent to the Master Server. Once a room is found by the Master Server, the client will + head off to the designated Game Server and use the operation Join on the Game Server. + + When using multiple lobbies (via OpJoinLobby or a TypedLobby parameter), another lobby might have more/fitting rooms.
+
+ Operation ReturnCode from the server. + Debug message for the error. +
+ + + Called when the local user/client left a room, so the game's logic can clean up it's internal state. + + + When leaving a room, the LoadBalancingClient will disconnect the Game Server and connect to the Master Server. + This wraps up multiple internal actions. + + Wait for the callback OnConnectedToMaster, before you use lobbies and join or create rooms. + + + + + Collection of "in room" callbacks for the Realtime Api to cover: Players entering or leaving, property updates and Master Client switching. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called when a remote player entered the room. This Player is already added to the playerlist. + + + If your game starts with a certain number of players, this callback can be useful to check the + Room.playerCount and find out if you can start. + + + + + Called when a remote player left the room or became inactive. Check otherPlayer.IsInactive. + + + If another player leaves the room or if the server detects a lost connection, this callback will + be used to notify your game logic. + + Depending on the room's setup, players may become inactive, which means they may return and retake + their spot in the room. In such cases, the Player stays in the Room.Players dictionary. + + If the player is not just inactive, it gets removed from the Room.Players dictionary, before + the callback is called. + + + + + Called when room properties changed. The propertiesThatChanged contain only the keys that changed. + + + In most cases, this method gets called due to some player settings Room Properties. + However, there are also "Well Known Properties", which use byte keys and this callback may include them. + Especially when entering a room, the server will also send the required Well Known Properties and those + are not filtered out before Realtime calls OnRoomPropertiesUpdate. + + You can safely ignore the byte typed keys in propertiesThatChanged. + + Changing properties is usually done by Room.SetCustomProperties. + + + + + + Called when custom player-properties are changed. Player and the changed properties are passed as object[]. + + + Changing properties must be done by Player.SetCustomProperties, which causes this callback locally, too. + + Contains Player that changed. + Contains the properties that changed. + + + + Called after switching to a new MasterClient when the current one leaves. + + + This is not called when this client enters a room. + The former MasterClient is still in the player list when this method get called. + + + + + Event callback for the Realtime Api. Covers events from the server and those sent by clients via OpRaiseEvent. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + Called for any incoming events. + + To receive events, implement IOnEventCallback in any class and register it via AddCallbackTarget + (either in LoadBalancingClient or PhotonNetwork). + + With the EventData.Sender you can look up the Player who sent the event. + + It is best practice to assign an eventCode for each different type of content and action, so the Code + will be essential to read the incoming events. + + + + + Interface for "WebRpc" callbacks for the Realtime Api. Currently includes only responses for Web RPCs. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called when the response to a WebRPC is available. See . + + + Important: The response.ReturnCode is 0 if Photon was able to reach your web-service.
+ The content of the response is what your web-service sent. You can create a WebRpcResponse from it.
+ Example: WebRpcResponse webResponse = new WebRpcResponse(operationResponse);
+ + Please note: Class OperationResponse is in a namespace which needs to be "used":
+ using ExitGames.Client.Photon; // includes OperationResponse (and other classes) +
+ + public void OnWebRpcResponse(OperationResponse response) + { + Debug.LogFormat("WebRPC operation response {0}", response.ToStringFull()); + switch (response.ReturnCode) + { + case ErrorCode.Ok: + WebRpcResponse webRpcResponse = new WebRpcResponse(response); + Debug.LogFormat("Parsed WebRPC response {0}", response.ToStringFull()); + if (string.IsNullOrEmpty(webRpcResponse.Name)) + { + Debug.LogError("Unexpected: WebRPC response did not contain WebRPC method name"); + } + if (webRpcResponse.ResultCode == 0) // success + { + switch (webRpcResponse.Name) + { + // todo: add your code here + case GetGameListWebRpcMethodName: // example + // ... + break; + } + } + else if (webRpcResponse.ResultCode == -1) + { + Debug.LogErrorFormat("Web server did not return ResultCode for WebRPC method=\"{0}\", Message={1}", webRpcResponse.Name, webRpcResponse.Message); + } + else + { + Debug.LogErrorFormat("Web server returned ResultCode={0} for WebRPC method=\"{1}\", Message={2}", webRpcResponse.ResultCode, webRpcResponse.Name, webRpcResponse.Message); + } + break; + case ErrorCode.ExternalHttpCallFailed: // web service unreachable + Debug.LogErrorFormat("WebRPC call failed as request could not be sent to the server. {0}", response.DebugMessage); + break; + case ErrorCode.HttpLimitReached: // too many WebRPCs in a short period of time + // the debug message should contain the limit exceeded + Debug.LogErrorFormat("WebRPCs rate limit exceeded: {0}", response.DebugMessage); + break; + case ErrorCode.InvalidOperation: // WebRPC not configured at all OR not configured properly OR trying to send on name server + if (PhotonNetwork.Server == ServerConnection.NameServer) + { + Debug.LogErrorFormat("WebRPC not supported on NameServer. {0}", response.DebugMessage); + } + else + { + Debug.LogErrorFormat("WebRPC not properly configured or not configured at all. {0}", response.DebugMessage); + } + break; + default: + // other unknown error, unexpected + Debug.LogErrorFormat("Unexpected error, {0} {1}", response.ReturnCode, response.DebugMessage); + break; + } + } + + +
+ + + Interface for event callback for the Realtime Api. + + + Classes that implement this interface must be registered to get callbacks for various situations. + + To register for callbacks, call and pass the class implementing this interface + To stop getting callbacks, call and pass the class implementing this interface + + + \ingroup callbacks + + + + Called when the client receives an event from the server indicating that an error happened there. + + + In most cases this could be either: + 1. an error from webhooks plugin (if HasErrorInfo is enabled), read more here: + https://doc.photonengine.com/en-us/realtime/current/gameplay/web-extensions/webhooks#options + 2. an error sent from a custom server plugin via PluginHost.BroadcastErrorInfoEvent, see example here: + https://doc.photonengine.com/en-us/server/current/plugins/manual#handling_http_response + 3. an error sent from the server, for example, when the limit of cached events has been exceeded in the room + (all clients will be disconnected and the room will be closed in this case) + read more here: https://doc.photonengine.com/en-us/realtime/current/gameplay/cached-events#special_considerations + + If you implement or you will also get this event. + + Object containing information about the error + + + + Container type for callbacks defined by IConnectionCallbacks. See LoadBalancingCallbackTargets. + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Container type for callbacks defined by IMatchmakingCallbacks. See MatchMakingCallbackTargets. + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Container type for callbacks defined by IInRoomCallbacks. See InRoomCallbackTargets. + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Container type for callbacks defined by ILobbyCallbacks. See LobbyCallbackTargets. + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Container type for callbacks defined by IWebRpcCallback. See WebRpcCallbackTargets. + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Container type for callbacks defined by . See . + + + While the interfaces of callbacks wrap up the methods that will be called, + the container classes implement a simple way to call a method on all registered objects. + + + + + Class wrapping the received event. + + + This is passed inside callback. + If you implement or you will also get but not parsed. + + In most cases this could be either: + 1. an error from webhooks plugin (if HasErrorInfo is enabled), read more here: + https://doc.photonengine.com/en-us/realtime/current/gameplay/web-extensions/webhooks#options + 2. an error sent from a custom server plugin via PluginHost.BroadcastErrorInfoEvent, see example here: + https://doc.photonengine.com/en-us/server/current/plugins/manual#handling_http_response + 3. an error sent from the server, for example, when the limit of cached events has been exceeded in the room + (all clients will be disconnected and the room will be closed in this case) + read more here: https://doc.photonengine.com/en-us/realtime/current/gameplay/cached-events#special_considerations + + + + + String containing information about the error. + + + + + A LoadBalancingPeer provides the operations and enum definitions needed to use the LoadBalancing server application which is also used in Photon Cloud. + + + This class is internally used. + The LoadBalancingPeer does not keep a state, instead this is done by a LoadBalancingClient. + + + + Obsolete accessor to the RegionHandler.PingImplementation. + + + + Creates a Peer with specified connection protocol. You need to set the Listener before using the peer. + + Each connection protocol has it's own default networking ports for Photon. + The preferred option is UDP. + + + + Creates a Peer with specified connection protocol and a Listener for callbacks. + + + + + Joins the lobby on the Master Server, where you get a list of RoomInfos of currently open rooms. + This is an async request which triggers a OnOperationResponse() call. + + The lobby join to. + If the operation could be sent (has to be connected). + + + + Leaves the lobby on the Master Server. + This is an async request which triggers a OnOperationResponse() call. + + If the operation could be sent (requires connection). + + + Used by OpJoinRoom and by OpCreateRoom alike. + + + + Creates a room (on either Master or Game Server). + The OperationResponse depends on the server the peer is connected to: + Master will return a Game Server to connect to. + Game Server will return the joined Room's data. + This is an async request which triggers a OnOperationResponse() call. + + + If the room is already existing, the OperationResponse will have a returnCode of ErrorCode.GameAlreadyExists. + + + + + Joins a room by name or creates new room if room with given name not exists. + The OperationResponse depends on the server the peer is connected to: + Master will return a Game Server to connect to. + Game Server will return the joined Room's data. + This is an async request which triggers a OnOperationResponse() call. + + + If the room is not existing (anymore), the OperationResponse will have a returnCode of ErrorCode.GameDoesNotExist. + Other possible ErrorCodes are: GameClosed, GameFull. + + If the operation could be sent (requires connection). + + + + Operation to join a random, available room. Overloads take additional player properties. + This is an async request which triggers a OnOperationResponse() call. + If all rooms are closed or full, the OperationResponse will have a returnCode of ErrorCode.NoRandomMatchFound. + If successful, the OperationResponse contains a gameserver address and the name of some room. + + If the operation could be sent currently (requires connection). + + + + Only used on the Master Server. It will assign a game server and room to join-or-create. + On the Game Server, the OpJoin is used with option "create if not exists". + + + + + Leaves a room with option to come back later or "for good". + + Async games can be re-joined (loaded) later on. Set to false, if you want to abandon a game entirely. + WebFlag: Securely transmit the encrypted object AuthCookie to the web service in PathLeave webhook when available + If the opteration can be send currently. + + + Gets a list of games matching a SQL-like where clause. + + Operation is only available in lobbies of type SqlLobby. + This is an async request which triggers a OnOperationResponse() call. + Returned game list is stored in RoomInfoList. + + + The lobby to query. Has to be of type SqlLobby. + The sql query statement. + If the operation could be sent (has to be connected). + + + + Request the rooms and online status for a list of friends (each client must set a unique username via OpAuthenticate). + + + Used on Master Server to find the rooms played by a selected list of users. + Users identify themselves by using OpAuthenticate with a unique user ID. + The list of user IDs must be fetched from some other source (not provided by Photon). + + The server response includes 2 arrays of info (each index matching a friend from the request):
+ ParameterCode.FindFriendsResponseOnlineList = bool[] of online states
+ ParameterCode.FindFriendsResponseRoomIdList = string[] of room names (empty string if not in a room)
+
+ The options may be used to define which state a room must match to be returned. +
+ Array of friend's names (make sure they are unique). + Options that affect the result of the FindFriends operation. + If the operation could be sent (requires connection). +
+ + + Sets properties of a player / actor. + Internally this uses OpSetProperties, which can be used to either set room or player properties. + + The payer ID (a.k.a. actorNumber) of the player to attach these properties to. + The properties to add or update. + If set, these must be in the current properties-set (on the server) to set actorProperties: CAS. + Set these to forward the properties to a WebHook as defined for this app (in Dashboard). + If the operation could be sent (requires connection). + + + + Sets properties of a room. + Internally this uses OpSetProperties, which can be used to either set room or player properties. + + The properties to add or update. + The properties expected when update occurs. (CAS : "Check And Swap") + WebFlag to indicate if request should be forwarded as "PathProperties" webhook or not. + If the operation could be sent (has to be connected). + + + + Sends this app's appId and appVersion to identify this application server side. + This is an async request which triggers a OnOperationResponse() call. + + + This operation makes use of encryption, if that is established before. + See: EstablishEncryption(). Check encryption with IsEncryptionAvailable. + This operation is allowed only once per connection (multiple calls will have ErrorCode != Ok). + + Your application's name or ID to authenticate. This is assigned by Photon Cloud (webpage). + The client's version (clients with differing client appVersions are separated and players don't meet). + Contains all values relevant for authentication. Even without account system (external Custom Auth), the clients are allowed to identify themselves. + Optional region code, if the client should connect to a specific Photon Cloud Region. + Set to true on Master Server to receive "Lobby Statistics" events. + If the operation could be sent (has to be connected). + + + + Sends this app's appId and appVersion to identify this application server side. + This is an async request which triggers a OnOperationResponse() call. + + + This operation makes use of encryption, if that is established before. + See: EstablishEncryption(). Check encryption with IsEncryptionAvailable. + This operation is allowed only once per connection (multiple calls will have ErrorCode != Ok). + + Your application's name or ID to authenticate. This is assigned by Photon Cloud (webpage). + The client's version (clients with differing client appVersions are separated and players don't meet). + Optional authentication values. The client can set no values or a UserId or some parameters for Custom Authentication by a server. + Optional region code, if the client should connect to a specific Photon Cloud Region. + + + If the operation could be sent (has to be connected). + + + + Operation to handle this client's interest groups (for events in room). + + + Note the difference between passing null and byte[0]: + null won't add/remove any groups. + byte[0] will add/remove all (existing) groups. + First, removing groups is executed. This way, you could leave all groups and join only the ones provided. + + Changes become active not immediately but when the server executes this operation (approximately RTT/2). + + Groups to remove from interest. Null will not remove any. A byte[0] will remove all. + Groups to add to interest. Null will not add any. A byte[0] will add all current. + If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands. + + + + Send an event with custom code/type and any content to the other players in the same room. + + This override explicitly uses another parameter order to not mix it up with the implementation for Hashtable only. + Identifies this type of event (and the content). Your game's event codes can start with 0. + Any serializable datatype (including Hashtable like the other OpRaiseEvent overloads). + Contains (slightly) less often used options. If you pass null, the default options will be used. + Send options for reliable, encryption etc + If operation could be enqueued for sending. Sent when calling: Service or SendOutgoingCommands. + + + + Internally used operation to set some "per server" settings. This is for the Master Server. + + Set to true, to get Lobby Statistics (lists of existing lobbies). + False if the operation could not be sent. + + + Used in the RoomOptionFlags parameter, this bitmask toggles options in the room. + + + + Options for OpFindFriends can be combined to filter which rooms of friends are returned. + + + + Include a friend's room only if it is created and confirmed by the game server. + + + Include a friend's room only if it is visible (using Room.IsVisible). + + + Include a friend's room only if it is open (using Room.IsOpen). + + + Turns the bool options into an integer, which is sent as option flags for Op FindFriends. + The options applied to bits of an integer. + + + + Parameters for the matchmaking of JoinRandomRoom and JoinRandomOrCreateRoom. + + + More about matchmaking: . + + + + The custom room properties a room must have to fit. All key-values must be present to match. In SQL Lobby, use SqlLobbyFilter instead. + + + Filters by the MaxPlayers value of rooms. + + + The MatchmakingMode affects how rooms get filled. By default, the server fills rooms. + + + The lobby in which to match. The type affects how filters are applied. + + + SQL query to filter room matches. For default-typed lobbies, use ExpectedCustomRoomProperties instead. + + + The expected users list blocks player slots for your friends or team mates to join the room, too. + See: https://doc.photonengine.com/en-us/pun/v2/lobby-and-matchmaking/matchmaking-and-lobby#matchmaking_slot_reservation + + + Parameters for creating rooms. + + + The name of the room to create. If null, the server generates a unique name. If not null, it must be unique and new or will cause an error. + + + The RoomOptions define the optional behaviour of rooms. + + + A lobby to attach the new room to. If set, this overrides a joined lobby (if any). + + + The custom player properties that describe this client / user. Keys must be strings. + + + Internally used value to skip some values when the operation is sent to the Master Server. + + + Internally used value to check which join mode we should call. + + + A list of users who are expected to join the room along with this client. Reserves slots for rooms with MaxPlayers value. + + + + ErrorCode defines the default codes associated with Photon client/server communication. + + + + (0) is always "OK", anything else an error or specific situation. + + + + (-3) Operation can't be executed yet (e.g. OpJoin can't be called before being authenticated, RaiseEvent cant be used before getting into a room). + + + Before you call any operations on the Cloud servers, the automated client workflow must complete its authorization. + Wait until State is: JoinedLobby or ConnectedToMasterServer + + + + (-2) The operation you called is not implemented on the server (application) you connect to. Make sure you run the fitting applications. + + + (-2) The operation you called could not be executed on the server. + + Make sure you are connected to the server you expect. + + This code is used in several cases: + The arguments/parameters of the operation might be out of range, missing entirely or conflicting. + The operation you called is not implemented on the server (application). Server-side plugins affect the available operations. + + + + (-1) Something went wrong in the server. Try to reproduce and contact Exit Games. + + + (32767) Authentication failed. Possible cause: AppId is unknown to Photon (in cloud service). + + + (32766) GameId (name) already in use (can't create another). Change name. + + + (32765) Game is full. This rarely happens when some player joined the room before your join completed. + + + (32764) Game is closed and can't be joined. Join another game. + + + (32762) All servers are busy. This is a temporary issue and the game logic should try again after a brief wait time. + + This error may happen for all operations that create rooms. The operation response will contain this error code. + + This error is very unlikely to happen as we monitor load on all servers and add them on demand. + However, it's good to be prepared for a shortage of machines or surge in CCUs. + + + + (32761) Not in use currently. + + + (32760) Random matchmaking only succeeds if a room exists thats neither closed nor full. Repeat in a few seconds or create a new room. + + + (32758) Join can fail if the room (name) is not existing (anymore). This can happen when players leave while you join. + + + (32757) Authorization on the Photon Cloud failed because the concurrent users (CCU) limit of the app's subscription is reached. + + Unless you have a plan with "CCU Burst", clients might fail the authentication step during connect. + Affected client are unable to call operations. Please note that players who end a game and return + to the master server will disconnect and re-connect, which means that they just played and are rejected + in the next minute / re-connect. + This is a temporary measure. Once the CCU is below the limit, players will be able to connect an play again. + + OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen. + Self-hosted Photon servers with a CCU limited license won't let a client connect at all. + + + + (32756) Authorization on the Photon Cloud failed because the app's subscription does not allow to use a particular region's server. + + Some subscription plans for the Photon Cloud are region-bound. Servers of other regions can't be used then. + Check your master server address and compare it with your Photon Cloud Dashboard's info. + https://dashboard.photonengine.com + + OpAuthorize is part of connection workflow but only on the Photon Cloud, this error can happen. + Self-hosted Photon servers with a CCU limited license won't let a client connect at all. + + + + + (32755) Custom Authentication of the user failed due to setup reasons (see Cloud Dashboard) or the provided user data (like username or token). Check error message for details. + + + + (32753) The Authentication ticket expired. Usually, this is refreshed behind the scenes. Connect (and authorize) again. + + + + (32752) A server-side plugin (or webhook) failed to execute and reported an error. Check the OperationResponse.DebugMessage. + + + + + (32751) CreateGame/JoinGame/Join operation fails if expected plugin does not correspond to loaded one. + + + + + (32750) for join requests. Indicates the current peer already called join and is joined to the room. + + + + + (32749) for join requests. Indicates the list of InactiveActors already contains an actor with the requested ActorNr or UserId. + + + + + (32748) for join requests. Indicates the list of Actors (active and inactive) did not contain an actor with the requested ActorNr or UserId. + + + + + (32747) for join requests. Note: for future use - Indicates the requested UserId was found in the ExcludedList. + + + + + (32746) for join requests. Indicates the list of ActiveActors already contains an actor with the requested ActorNr or UserId. + + + + + (32745) for SetProperties and RaiseEvent (if flag HttpForward is true) requests. Indicates the maximum allowed http requests per minute was reached. + + + + + (32744) for WebRpc requests. Indicates the the call to the external service failed. + + + + + (32743) for operations with defined limits (as in calls per second, content count or size). + + + + + (32742) Server error during matchmaking with slot reservation. E.g. the reserved slots can not exceed MaxPlayers. + + + + + (32741) Server will react with this error if invalid encryption parameters provided by token + + + + + Class for constants. These (byte) values define "well known" properties for an Actor / Player. + + + These constants are used internally. + "Custom properties" have to use a string-type as key. They can be assigned at will. + + + + (255) Name of a player/actor. + + + (254) Tells you if the player is currently in this game (getting events live). + A server-set value for async games, where players can leave the game and return later. + + + (253) UserId of the player. Sent when room gets created with RoomOptions.PublishUserId = true. + + + + Class for constants. These (byte) values are for "well known" room/game properties used in Photon LoadBalancing. + + + These constants are used internally. + "Custom properties" have to use a string-type as key. They can be assigned at will. + + + + (255) Max number of players that "fit" into this room. 0 is for "unlimited". + + + (254) Makes this room listed or not in the lobby on master. + + + (253) Allows more players to join a room (or not). + + + (252) Current count of players in the room. Used only in the lobby on master. + + + (251) True if the room is to be removed from room listing (used in update to room list in lobby on master) + + + (250) A list of the room properties to pass to the RoomInfo list in a lobby. This is used in CreateRoom, which defines this list once per room. + + + (249) Equivalent of Operation Join parameter CleanupCacheOnLeave. + + + (248) Code for MasterClientId, which is synced by server. When sent as op-parameter this is (byte)203. As room property this is (byte)248. + Tightly related to ParameterCode.MasterClientId. + + + (247) Code for ExpectedUsers in a room. Matchmaking keeps a slot open for the players with these userIDs. + + + (246) Player Time To Live. How long any player can be inactive (due to disconnect or leave) before the user gets removed from the playerlist (freeing a slot). + + + (245) Room Time To Live. How long a room stays available (and in server-memory), after the last player becomes inactive. After this time, the room gets persisted or destroyed. + + + + Class for constants. These values are for events defined by Photon LoadBalancing. + + They start at 255 and go DOWN. Your own in-game events can start at 0. These constants are used internally. + + + (230) Initial list of RoomInfos (in lobby on Master) + + + (229) Update of RoomInfos to be merged into "initial" list (in lobby on Master) + + + (228) Currently not used. State of queueing in case of server-full + + + (227) Currently not used. Event for matchmaking + + + (226) Event with stats about this application (players, rooms, etc) + + + (224) This event provides a list of lobbies with their player and game counts. + + + (210) Internally used in case of hosting by Azure + + + (255) Event Join: someone joined the game. The new actorNumber is provided as well as the properties of that actor (if set in OpJoin). + + + (254) Event Leave: The player who left the game can be identified by the actorNumber. + + + (253) When you call OpSetProperties with the broadcast option "on", this event is fired. It contains the properties being set. + + + (253) When you call OpSetProperties with the broadcast option "on", this event is fired. It contains the properties being set. + + + (252) When player left game unexpected and the room has a playerTtl != 0, this event is fired to let everyone know about the timeout. + Obsolete. Replaced by Leave. public const byte Disconnect = LiteEventCode.Disconnect; + (251) Sent by Photon Cloud when a plugin-call or webhook-call failed or events cache limit exceeded. Usually, the execution on the server continues, despite the issue. Contains: ParameterCode.Info. + + + + (250) Sent by Photon whent he event cache slice was changed. Done by OpRaiseEvent. + + + (223) Sent by Photon to update a token before it times out. + + + Class for constants. Codes for parameters of Operations and Events. + These constants are used internally. + + + (237) A bool parameter for creating games. If set to true, no room events are sent to the clients on join and leave. Default: false (and not sent). + + + (236) Time To Live (TTL) for a room when the last player leaves. Keeps room in memory for case a player re-joins soon. In milliseconds. + + + (235) Time To Live (TTL) for an 'actor' in a room. If a client disconnects, this actor is inactive first and removed after this timeout. In milliseconds. + + + (234) Optional parameter of OpRaiseEvent and OpSetCustomProperties to forward the event/operation to a web-service. + + + (233) Optional parameter of OpLeave in async games. If false, the player does abandons the game (forever). By default players become inactive and can re-join. + + + (233) Used in EvLeave to describe if a user is inactive (and might come back) or not. In rooms with PlayerTTL, becoming inactive is the default case. + + + (232) Used when creating rooms to define if any userid can join the room only once. + + + (231) Code for "Check And Swap" (CAS) when changing properties. + + + (230) Address of a (game) server to use. + + + (229) Count of players in this application in a rooms (used in stats event) + + + (228) Count of games in this application (used in stats event) + + + (227) Count of players on the master server (in this app, looking for rooms) + + + (225) User's ID + + + (224) Your application's ID: a name on your own Photon or a GUID on the Photon Cloud + + + (223) Not used currently (as "Position"). If you get queued before connect, this is your position + + + (223) Modifies the matchmaking algorithm used for OpJoinRandom. Allowed parameter values are defined in enum MatchmakingMode. + + + (222) List of RoomInfos about open / listed rooms + + + (221) Internally used to establish encryption + + + (220) Version of your application + + + (210) Internally used in case of hosting by Azure + + + (209) Internally used in case of hosting by Azure + + + (208) Internally used in case of hosting by Azure + + + (255) Code for the gameId/roomName (a unique name per room). Used in OpJoin and similar. + + + (250) Code for broadcast parameter of OpSetProperties method. + + + (252) Code for list of players in a room. + + + (254) Code of the Actor of an operation. Used for property get and set. + + + (249) Code for property set (Hashtable). + + + (245) Code of data/custom content of an event. Used in OpRaiseEvent. + + + (245) Code of data of an event. Used in OpRaiseEvent. + + + (244) Code used when sending some code-related parameter, like OpRaiseEvent's event-code. + This is not the same as the Operation's code, which is no longer sent as part of the parameter Dictionary in Photon 3. + + + (248) Code for property set (Hashtable). + + + + (251) Code for property-set (Hashtable). This key is used when sending only one set of properties. + If either ActorProperties or GameProperties are used (or both), check those keys. + + + + (253) Code of the target Actor of an operation. Used for property set. Is 0 for game + + + (246) Code to select the receivers of events (used in Lite, Operation RaiseEvent). + + + (247) Code for caching events while raising them. + + + (241) Bool parameter of CreateGame Operation. If true, server cleans up roomcache of leaving players (their cached events get removed). + + + (240) Code for "group" operation-parameter (as used in Op RaiseEvent). + + + (239) The "Remove" operation-parameter can be used to remove something from a list. E.g. remove groups from player's interest groups. + + + (239) Used in Op Join to define if UserIds of the players are broadcast in the room. Useful for FindFriends and reserving slots for expected users. + + + (238) The "Add" operation-parameter can be used to add something to some list or set. E.g. add groups to player's interest groups. + + + (218) Content for EventCode.ErrorInfo and internal debug operations. + + + (217) This key's (byte) value defines the target custom authentication type/service the client connects with. Used in OpAuthenticate + + + (216) This key's (string) value provides parameters sent to the custom authentication type/service the client connects with. Used in OpAuthenticate + + + (215) The JoinMode enum defines which variant of joining a room will be executed: Join only if available, create if not exists or re-join. + Replaces CreateIfNotExists which was only a bool-value. + + + (214) This key's (string or byte[]) value provides parameters sent to the custom authentication service setup in Photon Dashboard. Used in OpAuthenticate + + + (203) Code for MasterClientId, which is synced by server. When sent as op-parameter this is code 203. + Tightly related to GamePropertyKey.MasterClientId. + + + (1) Used in Op FindFriends request. Value must be string[] of friends to look up. + + + (2) Used in Op FindFriends request. An integer containing option-flags to filter the results. + + + (1) Used in Op FindFriends response. Contains bool[] list of online states (false if not online). + + + (2) Used in Op FindFriends response. Contains string[] of room names ("" where not known or no room joined). + + + (213) Used in matchmaking-related methods and when creating a room to name a lobby (to join or to attach a room to). + + + (212) Used in matchmaking-related methods and when creating a room to define the type of a lobby. Combined with the lobby name this identifies the lobby. + + + (211) This (optional) parameter can be sent in Op Authenticate to turn on Lobby Stats (info about lobby names and their user- and game-counts). + + + (210) Used for region values in OpAuth and OpGetRegions. + + + (209) Path of the WebRPC that got called. Also known as "WebRpc Name". Type: string. + + + (208) Parameters for a WebRPC as: Dictionary<string, object>. This will get serialized to JSon. + + + (207) ReturnCode for the WebRPC, as sent by the web service (not by Photon, which uses ErrorCode). Type: byte. + + + (206) Message returned by WebRPC server. Analog to Photon's debug message. Type: string. + + + (205) Used to define a "slice" for cached events. Slices can easily be removed from cache. Type: int. + + + (204) Informs the server of the expected plugin setup. + + The operation will fail in case of a plugin mismatch returning error code PluginMismatch 32751(0x7FFF - 16). + Setting string[]{} means the client expects no plugin to be setup. + Note: for backwards compatibility null omits any check. + + + + (202) Used by the server in Operation Responses, when it sends the nickname of the client (the user's nickname). + + + (201) Informs user about name of plugin load to game + + + (200) Informs user about version of plugin load to game + + + (196) Cluster info provided in OpAuthenticate/OpAuthenticateOnce responses. + + + (195) Protocol which will be used by client to connect master/game servers. Used for nameserver. + + + (194) Set of custom parameters which are sent in auth request. + + + (193) How are we going to encrypt data. + + + (192) Parameter of Authentication, which contains encryption keys (depends on AuthMode and EncryptionMode). + + + (191) An int parameter summarizing several boolean room-options with bit-flags. + + + + Class for constants. Contains operation codes. + + These constants are used internally. + + + (255) Code for OpJoin, to get into a room. + + + (231) Authenticates this peer and connects to a virtual application + + + (230) Authenticates this peer and connects to a virtual application + + + (229) Joins lobby (on master) + + + (228) Leaves lobby (on master) + + + (227) Creates a game (or fails if name exists) + + + (226) Join game (by name) + + + (225) Joins random game (on master) + + + (254) Code for OpLeave, to get out of a room. + + + (253) Raise event (in a room, for other actors/players) + + + (252) Set Properties (of room or actor/player) + + + (251) Get Properties + + + (248) Operation code to change interest groups in Rooms (Lite application and extending ones). + + + (222) Request the rooms and online status for a list of friends (by name, which should be unique). + + + (221) Request statistics about a specific list of lobbies (their user and game count). + + + (220) Get list of regional servers from a NameServer. + + + (219) WebRpc Operation. + + + (218) Operation to set some server settings. Used with different parameters on various servers. + + + (217) Get the game list matching a supplied sql filter (SqlListLobby only) + + + Defines possible values for OpJoinRoom and OpJoinOrCreate. It tells the server if the room can be only be joined normally, created implicitly or found on a web-service for Turnbased games. + These values are not directly used by a game but implicitly set. + + + Regular join. The room must exist. + + + Join or create the room if it's not existing. Used for OpJoinOrCreate for example. + + + The room might be out of memory and should be loaded (if possible) from a Turnbased web-service. + + + Only re-join will be allowed. If the user is not yet in the room, this will fail. + + + + Options for matchmaking rules for OpJoinRandom. + + + + Fills up rooms (oldest first) to get players together as fast as possible. Default. + Makes most sense with MaxPlayers > 0 and games that can only start with more players. + + + Distributes players across available rooms sequentially but takes filter into account. Without filter, rooms get players evenly distributed. + + + Joins a (fully) random room. Expected properties must match but aside from this, any available room might be selected. + + + + Lite - OpRaiseEvent lets you chose which actors in the room should receive events. + By default, events are sent to "Others" but you can overrule this. + + + + Default value (not sent). Anyone else gets my event. + + + Everyone in the current room (including this peer) will get this event. + + + The server sends this event only to the actor with the lowest actorNumber. + The "master client" does not have special rights but is the one who is in this room the longest time. + + + + Lite - OpRaiseEvent allows you to cache events and automatically send them to joining players in a room. + Events are cached per event code and player: Event 100 (example!) can be stored once per player. + Cached events can be modified, replaced and removed. + + + Caching works only combination with ReceiverGroup options Others and All. + + + + Default value (not sent). + + + Will merge this event's keys with those already cached. + + + Replaces the event cache for this eventCode with this event's content. + + + Removes this event (by eventCode) from the cache. + + + Adds an event to the room's cache + + + Adds this event to the cache for actor 0 (becoming a "globally owned" event in the cache). + + + Remove fitting event from the room's cache. + + + Removes events of players who already left the room (cleaning up). + + + Increase the index of the sliced cache. + + + Set the index of the sliced cache. You must set RaiseEventOptions.CacheSliceIndex for this. + + + Purge cache slice with index. Exactly one slice is removed from cache. You must set RaiseEventOptions.CacheSliceIndex for this. + + + Purge cache slices with specified index and anything lower than that. You must set RaiseEventOptions.CacheSliceIndex for this. + + + + Flags for "types of properties", being used as filter in OpGetProperties. + + + + (0x00) Flag type for no property type. + + + (0x01) Flag type for game-attached properties. + + + (0x02) Flag type for actor related propeties. + + + (0x01) Flag type for game AND actor properties. Equal to 'Game' + + + Wraps up common room properties needed when you create rooms. Read the individual entries for more details. + This directly maps to the fields in the Room class. + + + Defines if this room is listed in the lobby. If not, it also is not joined randomly. + + A room that is not visible will be excluded from the room lists that are sent to the clients in lobbies. + An invisible room can be joined by name but is excluded from random matchmaking. + + Use this to "hide" a room and simulate "private rooms". Players can exchange a roomname and create it + invisble to avoid anyone else joining it. + + + + Defines if this room can be joined at all. + + If a room is closed, no player can join this. As example this makes sense when 3 of 4 possible players + start their gameplay early and don't want anyone to join during the game. + The room can still be listed in the lobby (set isVisible to control lobby-visibility). + + + + Max number of players that can be in the room at any time. 0 means "no limit". + + + Time To Live (TTL) for an 'actor' in a room. If a client disconnects, this actor is inactive first and removed after this timeout. In milliseconds. + + + Time To Live (TTL) for a room when the last player leaves. Keeps room in memory for case a player re-joins soon. In milliseconds. + + + Removes a user's events and properties from the room when a user leaves. + + This makes sense when in rooms where players can't place items in the room and just vanish entirely. + When you disable this, the event history can become too long to load if the room stays in use indefinitely. + Default: true. Cleans up the cache and props of leaving users. + + + + The room's custom properties to set. Use string keys! + + Custom room properties are any key-values you need to define the game's setup. + The shorter your keys are, the better. + Example: Map, Mode (could be "m" when used with "Map"), TileSet (could be "t"). + + + + Defines the custom room properties that get listed in the lobby. + + Name the custom room properties that should be available to clients that are in a lobby. + Use with care. Unless a custom property is essential for matchmaking or user info, it should + not be sent to the lobby, which causes traffic and delays for clients in the lobby. + + Default: No custom properties are sent to the lobby. + + + + Informs the server of the expected plugin setup. + + The operation will fail in case of a plugin missmatch returning error code PluginMismatch 32757(0x7FFF - 10). + Setting string[]{} means the client expects no plugin to be setup. + Note: for backwards compatibility null omits any check. + + + + + Tells the server to skip room events for joining and leaving players. + + + Using this makes the client unaware of the other players in a room. + That can save some traffic if you have some server logic that updates players + but it can also limit the client's usability. + + + + Disables events join and leave from the server as well as property broadcasts in a room (to minimize traffic) + + + + Defines if the UserIds of players get "published" in the room. Useful for FindFriends, if players want to play another game together. + + + When you set this to true, Photon will publish the UserIds of the players in that room. + In that case, you can use PhotonPlayer.userId, to access any player's userID. + This is useful for FindFriends and to set "expected users" to reserve slots in a room. + + + + Optionally, properties get deleted, when null gets assigned as value. Defaults to off / false. + + When Op SetProperties is setting a key's value to null, the server and clients should remove the key/value from the Custom Properties. + By default, the server keeps the keys (and null values) and sends them to joining players. + + Important: Only when SetProperties does a "broadcast", the change (key, value = null) is sent to clients to update accordingly. + This applies to Custom Properties for rooms and actors/players. + + + + By default, property changes are sent back to the client that's setting them to avoid de-sync when properties are set concurrently. + + This option is enables by default to fix this scenario: + + 1) On server, room property ABC is set to value FOO, which triggers notifications to all the clients telling them that the property changed. + 2) While that notification is in flight, a client sets the ABC property to value BAR. + 3) Client receives notification from the server and changes it�s local copy of ABC to FOO. + 4) Server receives the set operation and changes the official value of ABC to BAR, but never notifies the client that sent the set operation that the value is now BAR. + + Without this option, the client that set the value to BAR never hears from the server that the official copy has been updated to BAR, and thus gets stuck with a value of FOO. + + + + Aggregates several less-often used options for operation RaiseEvent. See field descriptions for usage details. + + + Default options: CachingOption: DoNotCache, InterestGroup: 0, targetActors: null, receivers: Others, sequenceChannel: 0. + + + Defines if the server should simply send the event, put it in the cache or remove events that are like this one. + + When using option: SliceSetIndex, SlicePurgeIndex or SlicePurgeUpToIndex, set a CacheSliceIndex. All other options except SequenceChannel get ignored. + + + + The number of the Interest Group to send this to. 0 goes to all users but to get 1 and up, clients must subscribe to the group first. + + + A list of Player.ActorNumbers to send this event to. You can implement events that just go to specific users this way. + + + Sends the event to All, MasterClient or Others (default). Be careful with MasterClient, as the client might disconnect before it got the event and it gets lost. + + + Events are ordered per "channel". If you have events that are independent of others, they can go into another sequence or channel. + + + Optional flags to be used in Photon client SDKs with Op RaiseEvent and Op SetProperties. + Introduced mainly for webhooks 1.2 to control behavior of forwarded HTTP requests. + + + Types of lobbies define their behaviour and capabilities. Check each value for details. + Values of this enum must be matched by the server. + + + Standard type and behaviour: While joined to this lobby clients get room-lists and JoinRandomRoom can use a simple filter to match properties (perfectly). + + + This lobby type lists rooms like Default but JoinRandom has a parameter for SQL-like "where" clauses for filtering. This allows bigger, less, or and and combinations. + + + This lobby does not send lists of games. It is only used for OpJoinRandomRoom. It keeps rooms available for a while when there are only inactive users left. + + + Refers to a specific lobby on the server. + + Name and Type combined are the unique identifier for a lobby.
+ The server will create lobbies "on demand", so no registration or setup is required.
+ An empty or null Name always points to the "default lobby" as special case. +
+
+ + + Name of the lobby. Default: null, pointing to the "default lobby". + + + If Name is null or empty, a TypedLobby will point to the "default lobby". This ignores the Type value and always acts as . + + + + + Type (and behaviour) of the lobby. + + + An empty or null Name always points to the "default lobby" as special case. + + + + + A reference to the default lobby which is the unique lobby that uses null as name and is of type . + + + There is only a single lobby with an empty name on the server. It is always of type .
+ On the other hand, this is a shortcut and reusable reference to the default lobby.
+ Do not change Name or Type.
+
+
+ + + Returns whether or not this instance points to the "default lobby" (). + + + This comes up to checking if the Name is null or empty. + is not the same thing as the "default lobby" (). + + + + + Creates a TypedLobby instance. Unless Name is changed, this points to the "default lobby" (). + + + + + Sets Name and Type of the new instance. Make sure name is not empty or null, as that always points to the "default lobby" (). + + Some string to identify a lobby. + The type of a lobby defines it's capabilities and behaviour. + + + + Info for a lobby on the server. Used when is true. + + + + Count of players that currently joined this lobby. + + + Count of rooms currently associated with this lobby. + + + + Options for authentication modes. From "classic" auth on each server to AuthOnce (on NameServer). + + + + + Options for optional "Custom Authentication" services used with Photon. Used by OpAuthenticate after connecting to Photon. + + + + Use a custom authentication service. Currently the only implemented option. + + + Authenticates users by their Steam Account. Set Steam's ticket as "ticket" via AddAuthParameter(). + + + Authenticates users by their Facebook Account. Set Facebooks's tocken as "token" via AddAuthParameter(). + + + Authenticates users by their Oculus Account and token. Set Oculus' userid as "userid" and nonce as "nonce" via AddAuthParameter(). + + + Authenticates users by their PSN Account and token on PS4. Set token as "token", env as "env" and userName as "userName" via AddAuthParameter(). + + + Authenticates users by their Xbox Account. Pass the XSTS token via SetAuthPostData(). + + + Authenticates users by their HTC Viveport Account. Set userToken as "userToken" via AddAuthParameter(). + + + Authenticates users by their NSA ID. Set token as "token" and appversion as "appversion" via AddAuthParameter(). The appversion is optional. + + + Authenticates users by their PSN Account and token on PS5. Set token as "token", env as "env" and userName as "userName" via AddAuthParameter(). + + + Authenticates users with Epic Online Services (EOS). Set token as "token" and ownershipToken as "ownershipToken" via AddAuthParameter(). The ownershipToken is optional. + + + Authenticates users with Facebook Gaming api. Set token as "token" via AddAuthParameter(). + + + Disables custom authentication. Same as not providing any AuthenticationValues for connect (more precisely for: OpAuthenticate). + + + + Container for user authentication in Photon. Set AuthValues before you connect - all else is handled. + + + On Photon, user authentication is optional but can be useful in many cases. + If you want to FindFriends, a unique ID per user is very practical. + + There are basically three options for user authentication: None at all, the client sets some UserId + or you can use some account web-service to authenticate a user (and set the UserId server-side). + + Custom Authentication lets you verify end-users by some kind of login or token. It sends those + values to Photon which will verify them before granting access or disconnecting the client. + + The AuthValues are sent in OpAuthenticate when you connect, so they must be set before you connect. + If the AuthValues.UserId is null or empty when it's sent to the server, then the Photon Server assigns a UserId! + + The Photon Cloud Dashboard will let you enable this feature and set important server values for it. + https://dashboard.photonengine.com + + + + See AuthType. + + + The type of authentication provider that should be used. Defaults to None (no auth whatsoever). + Several auth providers are available and CustomAuthenticationType.Custom can be used if you build your own service. + + + This string must contain any (http get) parameters expected by the used authentication service. By default, username and token. + + Maps to operation parameter 216. + Standard http get parameters are used here and passed on to the service that's defined in the server (Photon Cloud Dashboard). + + + + Data to be passed-on to the auth service via POST. Default: null (not sent). Either string or byte[] (see setters). + Maps to operation parameter 214. + + + Internal Photon token. After initial authentication, Photon provides a token for this client, subsequently used as (cached) validation. + Any token for custom authentication should be set via SetAuthPostData or AddAuthParameter. + + + The UserId should be a unique identifier per user. This is for finding friends, etc.. + See remarks of AuthValues for info about how this is set and used. + + + Creates empty auth values without any info. + + + Creates minimal info about the user. If this is authenticated or not, depends on the set AuthType. + Some UserId to set in Photon. + + + Sets the data to be passed-on to the auth service via POST. + AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary. + String data to be used in the body of the POST request. Null or empty string will set AuthPostData to null. + + + Sets the data to be passed-on to the auth service via POST. + AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary. + Binary token / auth-data to pass on. + + + Sets data to be passed-on to the auth service as Json (Content-Type: "application/json") via Post. + AuthPostData is just one value. Each SetAuthPostData replaces any previous value. It can be either a string, a byte[] or a dictionary. + A authentication-data dictionary will be converted to Json and passed to the Auth webservice via HTTP Post. + + + Adds a key-value pair to the get-parameters used for Custom Auth (AuthGetParameters). + This method does uri-encoding for you. + Key for the value to set. + Some value relevant for Custom Authentication. + + + + Transform this object into string. + + String info about this object's values. + + + + Make a copy of the current object. + + The object to be copied into. + The copied object. + + + + Abstract implementation of PhotonPing, ase for pinging servers to find the "Best Region". + + + + Uses C# Socket class from System.Net.Sockets (as Unity usually does). + Incompatible with Windows 8 Store/Phone API. + + + + Sends a "Photon Ping" to a server. + + Address in IPv4 or IPv6 format. An address containing a '.' will be interpreted as IPv4. + True if the Photon Ping could be sent. + + + + Summarizes a "player" within a room, identified (in that room) by ID (or "actorNumber"). + + + Each player has a actorNumber, valid for that room. It's -1 until assigned by server (and client logic). + + + + + Used internally to identify the masterclient of a room. + + + + Backing field for property. + + + Identifier of this player in current room. Also known as: actorNumber or actorNumber. It's -1 outside of rooms. + The ID is assigned per room and only valid in that context. It will change even on leave and re-join. IDs are never re-used per room. + + + Only one player is controlled by each client. Others are not local. + + + Background field for nickName. + + + Non-unique nickname of this player. Synced automatically in a room. + + A player might change his own playername in a room (it's only a property). + Setting this value updates the server and other players (using an operation). + + + + UserId of the player, available when the room got created with RoomOptions.PublishUserId = true. + Useful for and blocking slots in a room for expected players (e.g. in ). + + + + True if this player is the Master Client of the current room. + + + + If this player is active in the room (and getting events which are currently being sent). + + Inactive players keep their spot in a room but otherwise behave as if offline (no matter what their actual connection status is). + The room needs a PlayerTTL != 0. If a player is inactive for longer than PlayerTTL, the server will remove this player from the room. + For a client "rejoining" a room, is the same as joining it: It gets properties, cached events and then the live events. + + + + Read-only cache for custom properties of player. Set via Player.SetCustomProperties. + + Don't modify the content of this Hashtable. Use SetCustomProperties and the + properties of this class to modify values. When you use those, the client will + sync values with the server. + + + + + Can be used to store a reference that's useful to know "by player". + Example: Set a player's character as Tag by assigning the GameObject on Instantiate. + + + + Creates a player instance. + To extend and replace this Player, override LoadBalancingPeer.CreatePlayer(). + + NickName of the player (a "well known property"). + ID or ActorNumber of this player in the current room (a shortcut to identify each player in room) + If this is the local peer's player (or a remote one). + + + + Creates a player instance. + To extend and replace this Player, override LoadBalancingPeer.CreatePlayer(). + + NickName of the player (a "well known property"). + ID or ActorNumber of this player in the current room (a shortcut to identify each player in room) + If this is the local peer's player (or a remote one). + A Hashtable of custom properties to be synced. Must use String-typed keys and serializable datatypes as values. + + + + Get a Player by ActorNumber (Player.ID). + + ActorNumber of the a player in this room. + Player or null. + + + Gets this Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around. + Player or null. + + + Gets a Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around. + Useful when you pass something to the next player. For example: passing the turn to the next player. + The Player for which the next is being needed. + Player or null. + + + Gets a Player's next Player, as sorted by ActorNumber (Player.ID). Wraps around. + Useful when you pass something to the next player. For example: passing the turn to the next player. + The ActorNumber (Player.ID) for which the next is being needed. + Player or null. + + + Caches properties for new Players or when updates of remote players are received. Use SetCustomProperties() for a synced update. + + This only updates the CustomProperties and doesn't send them to the server. + Mostly used when creating new remote players, where the server sends their properties. + + + + + Brief summary string of the Player: ActorNumber and NickName + + + + + String summary of the Player: player.ID, name and all custom properties of this user. + + + Use with care and not every frame! + Converts the customProperties to a String on every single call. + + + + + If players are equal (by GetHasCode, which returns this.ID). + + + + + Accompanies Equals, using the ID (actorNumber) as HashCode to return. + + + + + Used internally, to update this client's playerID when assigned (doesn't change after assignment). + + + + + Updates and synchronizes this Player's Custom Properties. Optionally, expectedProperties can be provided as condition. + + + Custom Properties are a set of string keys and arbitrary values which is synchronized + for the players in a Room. They are available when the client enters the room, as + they are in the response of OpJoin and OpCreate. + + Custom Properties either relate to the (current) Room or a Player (in that Room). + + Both classes locally cache the current key/values and make them available as + property: CustomProperties. This is provided only to read them. + You must use the method SetCustomProperties to set/modify them. + + Any client can set any Custom Properties anytime (when in a room). + It's up to the game logic to organize how they are best used. + + You should call SetCustomProperties only with key/values that are new or changed. This reduces + traffic and performance. + + Unless you define some expectedProperties, setting key/values is always permitted. + In this case, the property-setting client will not receive the new values from the server but + instead update its local cache in SetCustomProperties. + + If you define expectedProperties, the server will skip updates if the server property-cache + does not contain all expectedProperties with the same values. + In this case, the property-setting client will get an update from the server and update it's + cached key/values at about the same time as everyone else. + + The benefit of using expectedProperties can be only one client successfully sets a key from + one known value to another. + As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally. + When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their + actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to + take the item will have it (and the others fail to set the ownership). + + Properties get saved with the game state for Turnbased games (which use IsPersistent = true). + + Hashtable of Custom Properties to be set. + If non-null, these are the property-values the server will check as condition for this update. + Defines if this SetCustomProperties-operation gets forwarded to your WebHooks. Client must be in room. + + False if propertiesToSet is null or empty or have zero string keys. + True in offline mode even if expectedProperties or webFlags are used. + If not in a room, returns true if local player and expectedValues and webFlags are null. + (Use this to cache properties to be sent when joining a room). + Otherwise, returns if this operation could be sent to the server. + + + + Uses OpSetPropertiesOfActor to sync this player's NickName (server is being updated with this.NickName). + + + Unlike the CloudRegionCode, this may contain cluster information. + + + + Provides methods to work with Photon's regions (Photon Cloud) and can be use to find the one with best ping. + + + When a client uses a Name Server to fetch the list of available regions, the LoadBalancingClient will create a RegionHandler + and provide it via the OnRegionListReceived callback, as soon as the list is available. No pings were sent for Best Region selection yet. + + Your logic can decide to either connect to one of those regional servers, or it may use PingMinimumOfRegions to test + which region provides the best ping. Alternatively the client may be set to connect to the Best Region (lowest ping), one or + more regions get pinged. + Not all regions will be pinged. As soon as the results are final, the client will connect to the best region, + so you can check the ping results when connected to the Master Server. + + It makes sense to make clients "sticky" to a region when one gets selected. + This can be achieved by storing the SummaryToCache value, once pinging was done. + When the client connects again, the previous SummaryToCache helps limiting the number of regions to ping. + In best case, only the previously selected region gets re-pinged and if the current ping is not much worse, this one region is used again. + + + + The implementation of PhotonPing to use for region pinging (Best Region detection). + Defaults to null, which means the Type is set automatically. + + + A list of region names for the Photon Cloud. Set by the result of OpGetRegions(). + + Implement ILoadBalancingCallbacks and register for the callbacks to get OnRegionListReceived(RegionHandler regionHandler). + You can also put a "case OperationCode.GetRegions:" into your OnOperationResponse method to notice when the result is available. + + + + + When PingMinimumOfRegions was called and completed, the BestRegion is identified by best ping. + + + + + This value summarizes the results of pinging currently available regions (after PingMinimumOfRegions finished). + + + This value should be stored in the client by the game logic. + When connecting again, use it as previous summary to speed up pinging regions and to make the best region sticky for the client. + + + + Selects the best fitting ping implementation or uses the one set in RegionHandler.PingImplementation. + PhotonPing instance to use. + + + + Starts the ping routine for the assigned region. + + + Pinging runs in a ThreadPool worker item or (if needed) in a Thread. + WebGL runs pinging on the Main Thread as coroutine. + + Always true. + + + + Affected by frame-rate of app, as this Coroutine checks the socket for a result once per frame. + + + + + Attempts to resolve a hostname into an IP string or returns empty string if that fails. + + + To be compatible with most platforms, the address family is checked like this:
+ if (ipAddress.AddressFamily.ToString().Contains("6")) // ipv6... +
+ Hostname to resolve. + IP string or empty string if resolution fails +
+ + + This class represents a room a client joins/joined. + + + Contains a list of current players, their properties and those of this room, too. + A room instance has a number of "well known" properties like IsOpen, MaxPlayers which can be changed. + Your own, custom properties can be set via SetCustomProperties() while being in the room. + + Typically, this class should be extended by a game-specific implementation with logic and extra features. + + + + + A reference to the LoadBalancingClient which is currently keeping the connection and state. + + + + The name of a room. Unique identifier (per region and virtual appid) for a room/match. + The name can't be changed once it's set by the server. + + + + Defines if the room can be joined. + + + This does not affect listing in a lobby but joining the room will fail if not open. + If not open, the room is excluded from random matchmaking. + Due to racing conditions, found matches might become closed while users are trying to join. + Simply re-connect to master and find another. + Use property "IsVisible" to not list the room. + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + + Defines if the room is listed in its lobby. + + + Rooms can be created invisible, or changed to invisible. + To change if a room can be joined, use property: open. + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + + Sets a limit of players to this room. This property is synced and shown in lobby, too. + If the room is full (players count == maxplayers), joining this room will fail. + + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + The count of players in this Room (using this.Players.Count). + + + While inside a Room, this is the list of players who are also in that room. + + + While inside a Room, this is the list of players who are also in that room. + + + + List of users who are expected to join this room. In matchmaking, Photon blocks a slot for each of these UserIDs out of the MaxPlayers. + + + The corresponding feature in Photon is called "Slot Reservation" and can be found in the doc pages. + Define expected players in the methods: , and . + + + + Player Time To Live. How long any player can be inactive (due to disconnect or leave) before the user gets removed from the playerlist (freeing a slot). + + + Room Time To Live. How long a room stays available (and in server-memory), after the last player becomes inactive. After this time, the room gets persisted or destroyed. + + + + The ID (actorNumber, actorNumber) of the player who's the master of this Room. + Note: This changes when the current master leaves the room. + + + + + Gets a list of custom properties that are in the RoomInfo of the Lobby. + This list is defined when creating the room and can't be changed afterwards. Compare: LoadBalancingClient.OpCreateRoom() + + You could name properties that are not set from the beginning. Those will be synced with the lobby when added later on. + + + + Gets if this room cleans up the event cache when a player (actor) leaves. + + + This affects which events joining players get. + + Set in room creation via RoomOptions.CleanupCacheOnLeave. + + Within PUN, auto cleanup of events means that cached RPCs and instantiated networked objects are deleted from the room. + + + + Define if the client who calls SetProperties should receive the properties update event or not. + + + Define if Join and Leave events should not be sent to clients in the room. + + + Extends SuppressRoomEvents: Define if Join and Leave events but also the actors' list and their respective properties should not be sent to clients. + + + Define if UserIds of the players are broadcast in the room. Useful for FindFriends and reserving slots for expected users. + + + Define if actor or room properties with null values are removed on the server or kept. + + + Creates a Room (representation) with given name and properties and the "listing options" as provided by parameters. + Name of the room (can be null until it's actually created on server). + Room options. + + + Read (received) room option flags into related bool parameters. + This is for internal use. The operation response for join and create room operations is read this way. + + + + + Updates and synchronizes this Room's Custom Properties. Optionally, expectedProperties can be provided as condition. + + + Custom Properties are a set of string keys and arbitrary values which is synchronized + for the players in a Room. They are available when the client enters the room, as + they are in the response of OpJoin and OpCreate. + + Custom Properties either relate to the (current) Room or a Player (in that Room). + + Both classes locally cache the current key/values and make them available as + property: CustomProperties. This is provided only to read them. + You must use the method SetCustomProperties to set/modify them. + + Any client can set any Custom Properties anytime (when in a room). + It's up to the game logic to organize how they are best used. + + You should call SetCustomProperties only with key/values that are new or changed. This reduces + traffic and performance. + + Unless you define some expectedProperties, setting key/values is always permitted. + In this case, the property-setting client will not receive the new values from the server but + instead update its local cache in SetCustomProperties. + + If you define expectedProperties, the server will skip updates if the server property-cache + does not contain all expectedProperties with the same values. + In this case, the property-setting client will get an update from the server and update it's + cached key/values at about the same time as everyone else. + + The benefit of using expectedProperties can be only one client successfully sets a key from + one known value to another. + As example: Store who owns an item in a Custom Property "ownedBy". It's 0 initally. + When multiple players reach the item, they all attempt to change "ownedBy" from 0 to their + actorNumber. If you use expectedProperties {"ownedBy", 0} as condition, the first player to + take the item will have it (and the others fail to set the ownership). + + Properties get saved with the game state for Turnbased games (which use IsPersistent = true). + + Hashtable of Custom Properties that changes. + Provide some keys/values to use as condition for setting the new values. Client must be in room. + Defines if this SetCustomProperties-operation gets forwarded to your WebHooks. Client must be in room. + + False if propertiesToSet is null or empty or have zero string keys. + True in offline mode even if expectedProperties or webFlags are used. + Otherwise, returns if this operation could be sent to the server. + + + + + Enables you to define the properties available in the lobby if not all properties are needed to pick a room. + + + Limit the amount of properties sent to users in the lobby to improve speed and stability. + + An array of custom room property names to forward to the lobby. + If the operation could be sent to the server. + + + + Removes a player from this room's Players Dictionary. + This is internally used by the LoadBalancing API. There is usually no need to remove players yourself. + This is not a way to "kick" players. + + + + + Removes a player from this room's Players Dictionary. + + + + + Asks the server to assign another player as Master Client of your current room. + + + RaiseEvent has the option to send messages only to the Master Client of a room. + SetMasterClient affects which client gets those messages. + + This method calls an operation on the server to set a new Master Client, which takes a roundtrip. + In case of success, this client and the others get the new Master Client from the server. + + SetMasterClient tells the server which current Master Client should be replaced with the new one. + It will fail, if anything switches the Master Client moments earlier. There is no callback for this + error. All clients should get the new Master Client assigned by the server anyways. + + See also: MasterClientId + + The player to become the next Master Client. + False when this operation couldn't be done currently. Requires a v4 Photon Server. + + + + Checks if the player is in the room's list already and calls StorePlayer() if not. + + The new player - identified by ID. + False if the player could not be added (cause it was in the list already). + + + + Updates a player reference in the Players dictionary (no matter if it existed before or not). + + The Player instance to insert into the room. + + + + Tries to find the player with given actorNumber (a.k.a. ID). + Only useful when in a Room, as IDs are only valid per Room. + + ID to look for. + If true, the Master Client is returned for ID == 0. + The player with the ID or null. + + + + Attempts to remove all current expected users from the server's Slot Reservation list. + + + Note that this operation can conflict with new/other users joining. They might be + adding users to the list of expected users before or after this client called ClearExpectedUsers. + + This room's expectedUsers value will update, when the server sends a successful update. + + Internals: This methods wraps up setting the ExpectedUsers property of a room. + + If the operation could be sent to the server. + + + + Attempts to update the expected users from the server's Slot Reservation list. + + + Note that this operation can conflict with new/other users joining. They might be + adding users to the list of expected users before or after this client called SetExpectedUsers. + + This room's expectedUsers value will update, when the server sends a successful update. + + Internals: This methods wraps up setting the ExpectedUsers property of a room. + + The new array of UserIDs to be reserved in the room. + If the operation could be sent to the server. + + + Returns a summary of this Room instance as string. + Summary of this Room instance. + + + Returns a summary of this Room instance as longer string, including Custom Properties. + Summary of this Room instance. + + + + A simplified room with just the info required to list and join, used for the room listing in the lobby. + The properties are not settable (IsOpen, MaxPlayers, etc). + + + This class resembles info about available rooms, as sent by the Master server's lobby. + Consider all values as readonly. None are synced (only updated by events by server). + + + + Used in lobby, to mark rooms that are no longer listed (for being full, closed or hidden). + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. + + + Backing field for property. False unless the GameProperty is set to true (else it's not sent). + + + Backing field for property. + + + Backing field for master client id (actorNumber). defined by server in room props and ev leave. + + + Backing field for property. + + + Read-only "cache" of custom properties of a room. Set via Room.SetCustomProperties (not available for RoomInfo class!). + All keys are string-typed and the values depend on the game/application. + + + + The name of a room. Unique identifier for a room/match (per AppId + game-Version). + + + + Count of players currently in room. This property is overwritten by the Room class (used when you're in a Room). + + + + + The limit of players for this room. This property is shown in lobby, too. + If the room is full (players count == maxplayers), joining this room will fail. + + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + + Defines if the room can be joined. + This does not affect listing in a lobby but joining the room will fail if not open. + If not open, the room is excluded from random matchmaking. + Due to racing conditions, found matches might become closed even while you join them. + Simply re-connect to master and find another. + Use property "IsVisible" to not list the room. + + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + + Defines if the room is listed in its lobby. + Rooms can be created invisible, or changed to invisible. + To change if a room can be joined, use property: open. + + + As part of RoomInfo this can't be set. + As part of a Room (which the player joined), the setter will update the server and all clients. + + + + + Constructs a RoomInfo to be used in room listings in lobby. + + Name of the room and unique ID at the same time. + Properties for this room. + + + + Makes RoomInfo comparable (by name). + + + + + Accompanies Equals, using the name's HashCode as return. + + + + + Returns most interesting room values as string. + Summary of this RoomInfo instance. + + + Returns most interesting room values as string, including custom properties. + Summary of this RoomInfo instance. + + + Copies "well known" properties to fields (IsVisible, etc) and caches the custom properties (string-keys only) in a local hashtable. + New or updated properties to store in this RoomInfo. + + + + Helper class to debug log basic information about Photon client and vital traffic statistics. + + + Set SupportLogger.Client for this to work. + + + + + Toggle to enable or disable traffic statistics logging. + + + + helps skip the initial OnApplicationPause call, which is not really of interest on start + + + + Photon client to log information and statistics from. + + + + + Debug logs vital traffic statistics about the attached Photon Client. + + + + + Debug logs basic information (AppId, AppVersion, PeerID, Server address, Region) about the attached Photon Client. + + + + Reads an operation response of a WebRpc and provides convenient access to most common values. + + See LoadBalancingClient.OpWebRpc.
+ Create a WebRpcResponse to access common result values.
+ The operationResponse.OperationCode should be: OperationCode.WebRpc.
+
+
+ + Name of the WebRpc that was called. + + + ResultCode of the WebService that answered the WebRpc. + + 0 is: "OK" for WebRPCs.
+ -1 is: No ResultCode by WebRpc service (check ).
+ Other ResultCode are defined by the individual WebRpc and service. +
+
+ + Might be empty or null. + + + Other key/values returned by the webservice that answered the WebRpc. + + + An OperationResponse for a WebRpc is needed to read it's values. + + + Turns the response into an easier to read string. + String resembling the result. + + + + Optional flags to be used in Photon client SDKs with Op RaiseEvent and Op SetProperties. + Introduced mainly for webhooks 1.2 to control behavior of forwarded HTTP requests. + + + + + Indicates whether to forward HTTP request to web service or not. + + + + + Indicates whether to send AuthCookie of actor in the HTTP request to web service or not. + + + + + Indicates whether to send HTTP request synchronously or asynchronously to web service. + + + + + Indicates whether to send serialized game state in HTTP request to web service or not. + + + + + Collection of connection-relevant settings, used internally by PhotonNetwork.ConnectUsingSettings. + + + Includes the AppSettings class from the Realtime APIs plus some other, PUN-relevant, settings. + + + + The photon settings class, which is wrapped by this ScriptableObject. + + + + Serialized server settings, written by the Setup Wizard for use in ConnectUsingSettings. + + + + Holds a Custom Session Property value + + + + + Internal stored value + + + + + Get the Type of the internal stored value + + + + + Signal if this Session Property is a int value + + + + + Signal if this Session Property is a string value + + + + + Convert a into int + + + + + Convert a int into a + + + + + Convert a into string + + + + + Convert a string into a + + + + + Signal if a particular object is supported as a + + Object ref to check + True if obj is of a supported type, false otherwise + + + + Convert a particular object into a . + If the object type is not supported, null will be returned. + + Object reference to be converted + Instance of a if type is supported, null otherwise + +
+
diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml.meta new file mode 100644 index 0000000..f713fad --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Realtime.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 346cdb2392b5bce4b8b2756ef4e32cdb +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll new file mode 100644 index 0000000..5497588 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug new file mode 100644 index 0000000..80fe19c Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug.meta new file mode 100644 index 0000000..a813ca5 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fb8184b1b08ca0f408b87eb72e85e2bd +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.meta new file mode 100644 index 0000000..998983a --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.dll.meta @@ -0,0 +1,34 @@ +fileFormatVersion: 2 +guid: e725a070cec140c4caffb81624c8c787 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: + Fusion.NetworkObject: 500 + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug new file mode 100644 index 0000000..c0deb8c Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug.meta new file mode 100644 index 0000000..bdb869d --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.pdb.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7135dc2f84a2d814eb8ed3c3e41b0772 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml new file mode 100644 index 0000000..94b42bf --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml @@ -0,0 +1,8335 @@ + + + + Fusion.Runtime + + + + + Page Bit Shift Lookup Table + + + + + Additional companion attribute to NetworkedAttribute, which indicates how floats should be compressed. + + + + + Constructor new accuracy. + + + + + Constructor new accuracy. + + + + + Constructor that takes a named constant. + Accuracy for this property will be acquired from the settings. + + + + + + Allows for a specific slider range on Accuracy types to be displayed. Without this Accuracy values use a default slider range. + + + + + Allows for a specific slider range on Accuracy types to be displayed. Without this Accuracy values use a default slider range. + + + + + + Attribute for use on Fusion.Behaviour classes. + Automatically runs this method when the inspector refreshes. GUILayout calls be in the method and will render into the inspector. + Allows for editor code to be added to a component, without requiring a custom Editor script. + + + + + Conditions flags for when this action should run. + + + + + Run this action when playing. + + + + + Run this action when not playing. + + + + + Always run this action, regardless of play mode. + + + + + Tells the editor to dirty the class instance after executing a button action. + + + + + Specific flags for when this element should be displayed. + + + + + Define a BehaviourAction. + + The name of the method to execute. If excluded, this will be the method this attribute is on. + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. + Specific conditions for when the action should occur. + + + + Define a BehaviourAction, with the action being the method this attribute is attached to. + + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. + Specific conditions for when the action should occur. + + + + Attribute for use on Fusion.Behaviour classes. + Automatically creates a button in the inspector of derived classes. + Can be used on Fields or Method member types. + For fields, the name of the method the button will execute must be supplied. + When used on methods, that method will be called. + + + + + Define a ButtonBehaviourAction. + + The name that will be displayed on the button. + The name of the method to execute. If excluded, this will be the method this attribute is on. + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. + + + + Define a ButtonBehaviourAction. + + The name that will be displayed on the button. + Indicates if this button should appear while playing. + Indicates if this button should appear while not playing. + The name of the method to execute. If excluded, this will be the method this attribute is on. + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. + + + + Define a ButtonBehaviourAction. + + The name that will be displayed on the button. + Indicates if this button should appear while playing. + Indicates if this button should appear while not playing. + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. This member can be a property, field or method. + The return value of any of these is converted into a double. True = 1, False = 0. Null = 0. + + + + Attribute for use on Fusion.Behaviour classes. + Automatically draws a warning box in the inspector if the conditions are met. + + + + + Define a ButtonBehaviourAction. + + The text that will be shown in the warning box. + The name of the member (in the same class) to evaluate, to determine if the action should be performed. + Typically null, as the action method can have its own logic. This member can be a property, field or method. + The return value of any of these is converted into a double. True = 1, False = 0. Null = 0. + + + + Casts an enum or int value in the inspector to specific enum type for rendering of its popup list. + Supplying a method name rather than a type allows a property with the type Type to be used to dynamically get the enum type. + + + + + Options for hiding a field. + + + + + Field will be disabled, but still visible. + + + + + Field will be hidden. + + + + + Fusion editor attribute for selectively drawing/hiding fields. Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + How the field should be hidden (disabled or removed) + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + How the field should be hidden (disabled or removed) + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + How the field should be hidden (disabled or removed) + How the condition member value and compareToValye will be evaluated. + + + + Fusion editor attribute for disabling fields. + + + + + Constructor. + + If using the release version of the fusion.runtime.dll, field will be hidden, rather than just disabled. + + + + Attribute that indicates an enum should render as multi-select mask drop list in the inspector + + + + + Defines the target authority mask for OnChanged callbacks + + + + + Receive callback if you have state authority over the object + + + + + Receive callback if you have input authority over the object + + + + + Receive callback if you have neither input or state authority over the object + + + + + Shorthand for StateAuthority|InputAuthority|Proxies + + + + + + Flags a property of for network state synchronization. + The property should have empty get and set defines, which will automatically be replaced with networking code via IL Weaving. + OnChanged can be assigned with the name of a method in the same NetworkBehaviour. + The named method will get called whenever this property value has been changed by the State Authority. + + | [Networked(OnChanged = nameof(MyCallbackMethod)]

+ | public int MyProperty { get; set; } + | + | protected static void MyCallbackMethod(Changed<ChangedCallbackParent> changed) { + | changed.LoadNew(); + | var newval = changed.Behaviour.MyProperty; + | changed.LoadOld(); + | var oldval = changed.Behaviour.MyProperty; + | Debug.Log($"Changed from {oldval} to {newval}"); + | } +
+
+ + Inside of INetworkStruct, do not use AutoProperties (get; set;), as these will introduce managed types into the struct, which are not allowed. Instead use '=> default'. + + | [Networked]

+ | public string StringProp { get => default; set { } } +
+
+
+
+ + + Signature for the callback is: + static void OnChanged(Changed<MyClass> changed){} + + + + + Which targets should receive the OnChanged callback. + + + + + Name of the field that holds the default value for this networked property. + + + + + Interest group for this property + + + + + Default constructor for NetworkedAttribute + + + + + Group constructor + + The interest group this property belongs to + + + + If set, this changes expected Wrap method signature to int Name(NetworkRunner, T, byte*) and Unwrap to int Name(NetworkRunner, byte*, ref T). + In both cases, the result is the number of bytes written/read and can not be greater than what's declared here. + + + + + Describes the total number of WORDs a uses. + + + + + Enables a special inspector drawer for Unity Rect type, specially designed for editing RectTransforms using normalized values. + + + + + Constructor for . InvertY inverts Y handling, for RectTransforms which treat lowerRight as origin, rather than upper left. + + + Expressed as Width/Height, this defines the ratio of the box shown in the inspector. Value of 0 indicates game window resolution will be used. + + + + Attribute used to mark a field that needs to change based on an exponent + via a UI slider + + + + + Determines if a slider power of 0 results in an actual value of one, or zero. + + + + + + + + + + Color of the inspector header for this component type. None indicates no header graphic should be used. + + + + + Icon used for the inspector header for this component type. None indicates no header graphic should be used. + + + + + Unit Type for a certain field. + This helps to identify the unit that a certain value represents, like Seconds or Percentage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unit Attribute class. + Used to mark a field with the respective + + + + + The rounding used by the value slider. + + + + + The rounding used by the inverse slider (if is enabled). + + + + + Specify the field type, without Min and Max values + + Unit type + + + + Specify the field type, and its Min and Max values. + + Unit type + Min value for this field + Max value for this field + If values outside of the min/max range will get clamped, or be allowed. + + + + Specify the field type, and its Min and Max values. + + Unit type + Min value for this field + Max value for this field + If values outside of the min/max range will get clamped, or be allowed. + + + + Comparison method for evaluating condition member value against compareToValues. + + + + + True if condition member value equals compareToValue. + + + + + True if condition member value is not equal to compareToValue. + + + + + True if condition member value is less than compareToValue. + + + + + True if condition member value is less than or equal to compareToValue. + + + + + True if condition member value is greater than or equal to compareToValue. + + + + + True if condition member value is greater than compareToValue. + + + + + Fusion editor attribute for selective editor rendering. Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + + + + + Base Constructor. + + + + + + + + Base Constructor. + + + + + + + + Base Constructor. + + + + + + + Fusion editor attribute for selective editor rendering of warnings/info boxes in the inspector. + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + The text of the info box. + The icon of the message box. Use (int)UnityEditor.MessageType + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The value to compare the member value against. + The text of the info box. + The icon of the message box. Use (int)UnityEditor.MessageType + How the condition member value and compareToValye will be evaluated. + + + + Constructor. + + Condition member can be a property, field or method (with a return value). + Value of condition method is converted to a long. Null = 0, False = 0, True = 1, Unity Object = InstanceId + The text of the info box. + The icon of the message box. Use (int)UnityEditor.MessageType + How the condition member value and compareToValye will be evaluated. + + + + Wrapper around the Fusion LBC Implementation + + It will control and manage the communication between Fusion and the Photon Cloud + + + + + Fusion LBC Client Reference + + + + + ID of this Communicator. This reflects the Actor Number of the Peer inside the Room + + + + + Flag to signal if this Communicator was extracted and will be reused + + + + + Responsible to deal with LoadBalancingClient Events + + + Responsible for dealing and managing the API used to communicate with the Photon Cloud. + This also includes: + - Send/Reply to Protocol Messages + - Query for Reflexive Information + - Perform NAT Punchthrough + - Manage the Realtime client + - Respond to/deal with Photon Cloud events + + + + + Signal if the local peer is connected to the Photon Cloud and can perform extra actions, like creating/joining a Room. + + + + + Photon Client UserID + + + + + Signal if the local peer is already inside a Room + + + + + Signal if the local peer is also the Master Client of the Current Room + + + + + Get the internal used by the Client to perform the authentication + + + + + Reference to the current active communicator + + + + + Signal if the local peer will try or accept connections using NAT Punch + + + + + Custom STUN Server + + + + + Exposes the current NAT Type from the local Peer + + + + + Builds a new CloudService reference + + Reference to the current active Runner + Opitional external Communicator + + + + Extract the internal Communicator for later re-use + + Current used by this instance with all resetted settings + + + + Update and perform all pending actions related to the Photon Cloud communication + + + + + Connect the local peer to Photon Cloud using an async process. + + Custom Authentication Values used to Auth the local peer + Custom Photon App Settings + Async Task of the connect to Photon Cloud process. Can be used to wait for the process to be finished + + + + Join the Peer to a specific Lobby, either a prebuild or a custom one + + Lobby Type to Join + Lobby ID + True if the operation could be completed. + + + + Make the local Peer Create/Join a Room based on Start Game Arguments + + --------------------->Yes--->CreateOrJoin + SharedMode--->| Valid Room Name | + --------------------->No---->[RandomRoomName]-->JoinRandomOrCreate + + --------------------->Yes--->CreateOrJoin + ClientMode--->| Valid Room Name | + --------------------->No---->[RandomRoomName]-->JoinRandom + + ServerMode-- --------------------->Yes----------------------------| + |->| Valid Room Name | v + HostMode---- --------------------->No---->[RandomRoomName]-->CreateOrJoin + + + Start Game Args ref + Task of the Join Room process + + + + Disconnect the Local Peer from the Photon Cloud. + + Async Task of the disconnect from Photon Cloud process. Can be used to wait for the process to be finished + + + + Get the UserID of another Player Actor in the Room + + ActorID of a Player inside the Room + Player UserID + + + + Callback fire on every connection attempt with a remote Server. + + It is used while trying to hole-punch the remote server and enables the manager to swap the target endpoint in between attempts. + This is necessary to maintain a flow of attempts even if we exchange the local/public/relay endpoints + + Current attempt number + Max number of attempts + Flag if target EndPoint should change + New target EndPoint + + + + Start the connection process with a Remote Server + + Starting NAT Punch state, see for more info + Remote Server EndPoint to connect to + + + + Disposes the current + + + + + Update the data based on the current LBC Information after the peer enters a Room + + + + + Callback invoked when any Room Property has changed + + + + + Callback invoked when the Room list is updated with data from the Cloud + + New List of + + + + Send a Protocol Message to Fusion Plugin + + + + + Send a Protocol Message to the Fusion Plugin + + Reference to the Project Config to be sent + + + + Send a Protocol Message to the Fusion Plugin + + Reference to StunResult used to build the Protocol Message + + + + Build and send the latest Server Snapshot to the Fusion Plugin + + + + + Handles a Confirmation Protocol Message sent by the Fusion Plugin + + Sender Actor Number + Join Protocol Message + + + + Handles a Protocol Message sent by the Fusion Plugin + + Sender Actor Number + Start Protocol Message + + + + Handles a Protocol Message sent by the Fusion Plugin + + Sender Actor Number + Disconnect Protocol Message + + + + Handles a Protocol Message sent by the Fusion Plugin + + Sender Actor Number + NetworkConfigSync Protocol Message + + + + Handles a Protocol Message sent by the Fusion Plugin + + Sender Actor Number + ReflexiveInfo Protocol Message + + + + Check for scheduled requests + + + + + Confirms or waits for confirmation from the Plugin of the Join Message + + True if the Join Confirmation was received, false otherwise + + + + Run the STUN Service to retrieve the current Reflexive Addresses of the local peer + + Running Task of the STUN Query Procedure + + + + Update the internal used to start the Fusion Runner + + New arguments + + + + Reverse ping will send Empty UDP Packets to the RemoteAddr in order to setup the Routing Table + on the current NAT of the Server, forcing it to allow packages from the remote client to be received + + Remove EndPoint to ping + CancellationToken + + + + Send a "ping" to an EndPoint + + EndPoint to send the ping + True if the Ping was sent, false otherwise + + + + Try to send Server Snapshot to Plugin + + + + + Check if Remote Private EndPoint appears to be in the same Subnet + + True if in same Subnet + + + + Initilize the Log system from the Realtime SDK to use the Fusion Log System + + + + + Converts a to a + + + ref + ref + + + + Holds information about the local peer used to Join/Start/Connect to a remote Peer + using the Photon Cloud as backend + + + + + Timeout between sending a Request and receive a Join Confirmation + + + + + Client Server Lobby + + + + + Shared Lobby + + + + + Reference to the initialization arguments set by the user. + They are used to start the Fusion Runner + + + + + Connection Stage related to the current EndPoint Type used by the client to connect a remote server + + + + + Describes the current state of the Join process + + + + + Describe the current protocol version we are using to communicate with the Plugin + + + + + Remote Server Reflexive Info. Stores private and public EndPoint of the remote server. + + + + + Local Reflexive Info. Stores private and public EndPoint of the local peer. + + + + + Stores requests sent by the plugin + + + + + Last Disconnect Msg Received from the Plugin + + + + + Define a list of Requests that may be asked by the Plugin Server + + + + + No Request + + + + + Request for the Local Reflexive Info + + + + + Describes the current Target Address Type used in the NAT Punch procedure + + + + + No connection procedure is running + + + + + Trying to Connect to LAN EndPoint + + + + + Trying to Connect to WAN EndPoint + + + + + Trying to Connect to Relay EndPoint + + + + + Stage of the Join Process. + + When starting the peer, the first thing we need to make sure is to have Joined the Room + with a confirmation from the Plugin, this will signal the current stage of this + + + + + Join Request not sent yet + + + + + Join Request Sent, waiting for confirmation + + + + + Join Confirmation Received, all good + + + + + Failed to receive Join Confirmation after a timeout () + + + + + Stores the data of a "Request to Ping" used by the Server in Client-Server Mode + to send arbitrary "pings" to a connecting Client. + + This allows the local NAT Table to be updated with the right mapping information + from the remote client, increasing the chance of the local Server to receive any + connect request from the remote peer. + + + + + Delay between pings + + + + + Total number of pings to send + + + + + Countdown for the next ping + + + + + Remote Client Reflexive Info, used to getter the Public EndPoint to send the ping + + + + + Extension methods to + + + + + Interface for callback. + Called after the resimulation loop (when applicable), and also after the forward simulation loop. + Implement this interface on and classes. + + + + + Called after the resimulation loop (when applicable), and also after the forward simulation loop. + Only called on Updates where resimulation or forward ticks are processed. + + True if this is being called during the resimulation loop. False if during the forward simulation loop. + How many resimulation or forward ticks are going to be processed. + + + + Callback interface for . + Called at the very start of the resimulation loop (on clients with prediction enabled), + immediately after state is set to the latest server snapshot. + Implement this interface on and classes. + + + + + Called at the very start of the resimulation loop (on clients with prediction enabled), + immediately after state is set to the latest server snapshot. + + + + + Interface for callback. Called immediately after Physics.Simulate(). + Implement this interface on and classes. + + + + + Called immediately after Physics.Simulate(). + + + + + Interface for callback. + Called after each tick simulation completes. + Implement this interface on and classes. + + + + + Called after each tick simulation completes. + + + + + Interface for the callback, which is called at the end of each Fusion Update segment. + Implement this interface on and classes. + + + + + Called at the end of the Fusion Update loop, before all Unity MonoBehaviour.Update() callbacks. + + + + + Interface for callback. + Called before the resimulation loop (when applicable), and also before the forward simulation loop. + Implement this interface on and classes. + + + + + Called before the resimulation loop (when applicable), and also before the forward simulation loop. + Only called on Updates where resimulation or forward ticks are processed. + + True if this is being called during the resimulation loop. False if during the forward simulation loop. + How many resimulation or forward ticks are going to be processed. + + + + Callback interface for . + Called at the very start of the resimulation loop (on clients with prediction enabled), + before state is set to the latest server snapshot. + Implement this interface on and classes. + + + + + Called at the very start of the resimulation loop (on clients with prediction enabled), + before state is set to the latest server snapshot. + + + + + Interface for callback. Called immediately before Physics.Simulate(). + Implement this interface on and classes. + + + + + Called immediately before Physics.Simulate(). + + + + + Interface for callback. + Called before each tick is simulated. + Implement this interface on and classes. + + + + + Called before each tick is simulated. + + + + + Interface for the callback, which is called at the beginning of each Fusion Update segment. + Implement this interface on and classes. + + + + + Called at the start of the Fusion Update loop, before the Fusion simulation loop. + + + + + Interface for predicted spawn callbacks. + Implement this interface on and classes. + + + + + Called in place of Spawned(), when the predicted object is initially created locally on the client. + + + + + Called every tick in place of FixedUpdateNetwork(), until or occur. + + + + + Called every tick in place of Render(), until or occur. + + + + + Called when Server does not produce the spawn that has been predicted. + The spawn did not actually happen, and this callback may be used to clean up after the missed prediction. + + + + + Called when Server spawn was has been confirmed, and the predicted spawn object has been added to the simulation. + + + + + Interface for callback. + Called when the joins AreaOfInterest. + Implement this interface on and classes. + Only applicable to . + + + + + Called when the joins AreaOfInterest. + Object is now receiving snapshot updates. + Object will execute FixedUpdateNetwork() and Render() methods until the object leaves simulation. + + + + + Interface for the callback. + Called when the leaves AreaOfInterest. + Implement this interface on and classes. + Only applicable to . + + + + + Called when the leaves AreaOfInterest. + Object is no longer receiving snapshot updates. + Object will stop executing FixedUpdateNetwork() and Render() methods until the object rejoins simulation. + + + + + Float compression value, used when writing to the fusion allocator. + + + + + Float decompression value, used when reading from the fusion allocator. + + + + + Indicates the rounding factor of compressed values. + All values will be truncated to the nearest multiple of this. + For example; A value of 0.012345f compressed with an Accuracy of 0.001, becomes 0.012f + An Accuracy setting of 0 is uncompressed. + + + + + The accuracy value that is used as a rounding factor for Fusion's compression. All values will be truncated to the nearest multiple of this. + + + + + The inverse of the Accuracy value, used in decompression. + + + + + Sets the Accuracy value.Inverse is determined and set as well. + + + + + Sets the accuracy to a global accuracy, which are defined in Fusion's . + + The name of the Accuracy Default (can be found in 's settings. + + + + Gets the value used for float compression. + + Reference to the project Config file, which is needed to look up the global values. + Typically this is Runner.Config is used for this value. + + + + + Gets the value used for float decompression. + + Reference to the project file, which is needed to look up the global values. + + + + Accuracy constructor. The provided float value is used to determine the rounding. + For example, an accuracy of .01f would result in the value .02345 being rounded to .02f. + + + + + + Accuracy constructor that binds this accuracy to a named setting. + This can be a built-in constant, like AccuracyDefaults.POSITION - + or your own custom name (provided you have defined it in . + + The string name of the tag. Can be an constant, or your own defined default. + The value that will be used if the AccuracyDefaults toggle is deselected in the inspector. + + + + Implicit operator to convert floats into accuracy. + + + + + + Summary text for this accuracy. + + + + + + Base class for network behaviours which provide a position offset for area of interest. + A is required on this GameObject or a parent of this GameObject. + + + + + The int* offset for the Ptr, for the memory location of the position data. + + + + + Base class for Fusion network components, which are associated with a . + Derived from , components derived from this class are associated with a and . + Components derived from this class are associated with a parent . + and can use the on properties to automate state synchronization, + and can use the on methods, to automate messaging. + + + + + Options for which time frame this object will render in. + + + + + Selects the most likely suitable time frame. + + + + + Renders the object in the remote time frame, using the most recently consumed remote state tick. + + + + + Render the object in the local players time frame, interpolating between the most recent tick simulation result and the previous. + + + + + Interpolation will not be calculated nor applied. + + + + + Pointer to the allocated memory associated with this Object. + + + + + The index of this , in the array. + + + + + Indicates whether the state authority snapshot tick data, or prediction tick data (if available in the current mode for the current peer) should be used for interpolation. + + + + + Gives access to the offset (in 32 bit words) and count (in 32 bit words) of this behaviour backing data + + + + + Get/Set the time frame this object is rendered in. + + + + + The unique identifier for this network behaviour. + + + + + Override this value for custom memory allocations. + This is for advanced use cases only, and cannot be used if is used in the derived class. + + + + + Copies entire state of passed in source + + Source to copy data from + + + + Override this property to change whether initial non-zero values assigned + to [Networked] properties invoke OnChanged callbacks. For clients + the initial value comes from the simulation snapshot, not from what has + been set locally in the inspector. + + Returns "true" by default. + + + + + Post spawn callback. + + + + + Called before the network object is despawned + + If the state of the behaviour is still accessible + + + + Get 'To' and 'From' states, and the Alpha (t) values needed for lerping. + + The returned values. + Force interpolation to the current s time frame + True valid state data was returned. + + + + Get 'To' and 'From' states, and the Alpha (t) values needed for interpolation. + + The returned values. + + If the interpolation data is in the predicted time frame (true) or between snapshots (false), + according to the . + + True valid state data was returned. + + + + If the behaviour data should be interpolated between latest predicted states or between snapshots. + + + + + + Clear all dynamic OnChange callbacks from this behaviour + + + + + Removes a specific OnChange callback from this behaviour + + The reference returned by OnChangeAdd + Removal succeeded + + + + Adds a OnChange callback to this behaviour which is bound to a specific word offset and word count + + Word offset to monitor for changes + How many words to monitor for changes, from offset and up + Callback to invoke + Type of the behaviour + Reference struct that can be used to remove this callback later + + + + Adds a OnChange callback to this behaviour which is bound to a specific weaved property + + Name of the property to monitor for changes + Callback to invoke + Type of the behaviour + Reference struct that can be used to remove this callback later + + + + + + + + + + Returns true if it a valid can be found for the current simulation tick (Typically this is used in ). + The returned input struct originates from the , + and if valid contains the inputs supplied by that for the current simulation tick. + + + + + RawInterpolator provides a set of methods to get the "from" and "to" ticks of the associated [Networked] property, + as well as the current interpolation value for the two. + All methods return raw memory pointers and expects the caller to be able to convert the data to the proper types. + + + + + + Interpolator provides a set of methods to get the "from" and "to" ticks of the associated [Networked] property, + as well as the current interpolation value for the two, but it also provides a Value property as a shortcut to + get the actual interpolated value for those parameters. + + + + + + Get the current interpolation value for this Interpolator. This method will silently ignore missing interpolation data and just return the default value for `T` + + + + + Get the current interpolation value for this Interpolator. Similar to the `Value` property, but will return null if interpolation data is not available. + + + + + Try and get 'To' and 'From' states, and the Alpha (t) values needed for interpolation. + + Value on the state the interpolation is going from. + Value on the state the interpolation is going to. + Interpolant factor between the two states. + + Force interpolation to the current s time frame (as opposed to between snapshots). + If no value is forced, interpolation data will be retrieved according to the . + + True if the interpolation data is available, false otherwise. + + + + Get 'To' and 'From' states, and the Alpha (t) values needed for interpolation. + + + Force interpolation to the current s time frame (as opposed to between snapshots). + If no value is forced, interpolation data will be retrieved according to the . + + A tuple containing the values. Null if interpolation data is not available. + + + + Get a raw interpolator for a networked property. + The returned `RawInterpolator` provides a way to calculate the "between-ticks" value of the named [Networked] property. + + Name of the [Networked] property + `RawInterpolator` for the networked property + + + + Get an interpolator for a networked property. + The returned `Interpolator` provides a way to calculate the "between-ticks" value of the named [Networked] property with the specified type `T`. + The value is a linear interpolation between the "from" and the "to" ticks and is available via the `Value` property on the `Interpolator`. + + Name of the [Networked] property + Type of the [Networked] property + `Interpolator` for the networked property + + + + This is a special method that is meant to be used only for [Networked] properties inline initialization. + + + + + This is a special method that is meant to be used only for [Networked] properties inline initialization. + + + + + Converts NetworkBehaviour to NetworkBehaviourId + + + + + + + This method needs to be invoked in user overrides of: + + + + + + + Reference to an OnChange callback added to a NetworkBehaviour + + + + + If this callback reference is valid or not + + + + + A component for synchronizing the Animator controller state from the State Authority to network proxies. + Requires a Unity Animator component, and a component. + NOTE: Animator Root Motion is not compatible with re-simulation and prediction. + + + + + The Animator being synced. + + + + + Flags controlling which Mecanim data will be synced. + + + + + Accuracy used for parameter float values. + + + + + Accuracy used for animator state normalized time values. + + + + + Accuracy used for layer weight values. + + + + + Queues a trigger with this animator. Call this instead of Animator.SetTrigger for the State Authority. + Triggers are prone to getting lost by animation syncs, as they may get reset before the value is synced. Setting triggers with this method ensures they get captured. + + + + + + Queues a trigger with this animator. Call this instead of Animator.SetTrigger for the State Authority. + Triggers are prone to getting lost by animation syncs, as they may get reset before the value is synced. Setting triggers with this method ensures they get captured. + + + + + + Fusion component for handling Physics2D.SyncTransforms() and Physics2D.Simulate(). + Only used when == true. + + + + + Fusion component for handling Physics.SyncTransforms() and Physics.Simulate(). + Only used when == true. + + + + + Replicates a Unity Transform's position and rotation states from the to all other peers. + Add this component to a GameObject to sync the position and rotation. + A is required on this GameObject or a parent of this GameObject. + + This component does not interpolate visuals. In order to do that, use a . + + + + + + Cached GameObject.transform reference. + + + + + Implements Unity's Awake event function. + If overriding this method in an inheritor, the base method should be called. + + + + + Implements Unity's OnEnable event function. + If overriding this method in an inheritor, the base method should be called. + + + + + Overrides . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + To extend the functionality of copying data from the networked buffer to the engine, + override and extend instead. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + To extend the functionality of copying data from the engine to the networked buffer, + override and extend instead. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Sets the values retrieved from the networked data to the respective engine (Unity) fields. + If overriding this method in an inheritor, the base method should be called. + + + + + Sets the values retrieved from the engine (Unity) to the respective networked fields. + If overriding this method in an inheritor, the base method should be called. + + + + + Gets the position value from the engine. By default, the world position is set to be the output. + + + + + Gets the rotation value from the engine. By default, the world rotation is set to be the output. + + + + + Sets a position value to the engine. By default, the value is set to the transform's world position field. + + + + + Sets a rotation value to the engine. By default, the value is set to the transform's world rotation field. + + + + + Number of words used by a instance on its networked data, + including all inherited fields. + + + + + Number of words used by this class on the networked data buffer, including all inherited fields, + to serve for reference to inheritors. + + + + + Implements by defining the word offset from the base pointer to the field that carries the position data used for Area of Interest management. + + + + + Reads from the position field on this object's networked data + with the default Runner position read accuracy. + + + + + Reads from a position field on the networked data pointed by + with the default Runner position read accuracy. + + + + + Reads from a position field on the networked data pointed by + with the defined . + + + + + Reads from the rotation field on this object's networked data + with the default Runner position read accuracy. + + + + + Reads from a rotation field on the networked data pointed by + with the default Runner position read accuracy. + + + + + Reads from a rotation field on the networked data pointed by + with the defined . + + + + + Writes to a position field on this object's networked data + with the default Runner position write accuracy. + + + + + Writes to a position field on the networked data pointed by + with the default Runner position write accuracy. + + + + + Writes to a position field on the networked data pointed by + with the defined . + + + + + Writes to a rotation field on this object's networked data + with the default Runner position write accuracy. + + + + + Writes to a rotation field on the networked data pointed by + with the default Runner position write accuracy. + + + + + Writes to a rotation field on the networked data pointed by + with the defined . + + + + + Replicates a Unity Rigidbody state from the to all other peers. + Add this component to a GameObject with a Rigidbody component. + A is required on this GameObject or a parent of this GameObject. + + + + + Cached reference of this object's component. + + + + + Sets the field to . + + + + + Sets the default teleport interpolation velocity to be the rigidbody velocity. + For more details on how this field is used, see . + + + + + Sets the default teleport interpolation angular velocity to be the rigidbody angular velocity. + For more details on how this field is used, see . + + + + + Implements Unity's Awake event function. + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + + + + + + + Number of words used by a instance on its networked data, + including all inherited fields. + + + + + + + + Reads from the field reserved to store the rigidbody position on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody position on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody position on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody rotation on this object's + networked data using the default Runner rotation read accuracy. + + + + + Reads from the field reserved to store the rigidbody rotation on the networked data pointed + by using the default Runner rotation read accuracy. + + + + + Reads from the field reserved to store the rigidbody rotation on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody velocity on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody velocity on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody velocity on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody angular velocity on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the defined . + + + + + Reads from a field reserved to store the rigidbody flags and constraints + on this object's networked data. + + + + + Reads from a field reserved to store the rigidbody flags and constraints + on the networked data pointed by . + + + + + Writes to the field reserved to store the rigidbody position on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody position on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody position on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody rotation on this object's + networked data using the default Runner rotation write accuracy. + + + + + Writes to the field reserved to store the rigidbody rotation on the networked data pointed + by using the default Runner rotation write accuracy. + + + + + Writes to the field reserved to store the rigidbody rotation on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody velocity on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody velocity on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody velocity on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody angular velocity on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the defined . + + + + + Writes to a field reserved to store the rigidbody flags and constraints on this object's networked data. + + + + + Writes a field reserved to store the rigidbody flags and constraints on the networked data pointed by . + + + + + Replicates a Unity Rigidbody state from the to all other peers. + Add this component to a GameObject with a Rigidbody component. + A is required on this GameObject or a parent of this GameObject. + + + + + Cached reference of this object's component. + + + + + Sets the field to . + + + + + Sets the default teleport interpolation velocity to be the rigidbody velocity. + For more details on how this field is used, see . + + + + + Sets the default teleport interpolation angular velocity to be the rigidbody angular velocity, around the Z axis. + For more details on how this field is used, see . + + + + + Implements Unity's Awake event function. + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + + + + + + + Number of words used by a instance on its networked data, + including all inherited fields. + + + + + + + + Reads from the field reserved to store the rigidbody position on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody position on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody position on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody rotation on this object's + networked data using the default Runner rotation read accuracy. + + + + + Reads from the field reserved to store the rigidbody rotation on the networked data pointed + by using the default Runner rotation read accuracy. + + + + + Reads from the field reserved to store the rigidbody rotation on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody velocity on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody velocity on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody velocity on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody angular velocity on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody gravity scale on this object's + networked data using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody gravity scale on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody gravity scale on the networked data pointed + by using the defined . + + + + + Reads from a field reserved to store the rigidbody flags and constraints + on this object's networked data. + + + + + Reads from a field reserved to store the rigidbody flags and constraints + on the networked data pointed by . + + + + + Writes to the field reserved to store the rigidbody position on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody position on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody position on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody rotation on this object's + networked data using the default Runner rotation write accuracy. + + + + + Writes to the field reserved to store the rigidbody rotation on the networked data pointed + by using the default Runner rotation write accuracy. + + + + + Writes to the field reserved to store the rigidbody rotation on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody velocity on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody velocity on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody velocity on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody angular velocity on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular velocity on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody gravity scale on this object's + networked data using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody gravity scale on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody gravity scale on the networked data pointed + by using the defined . + + + + + Writes to a field reserved to store the rigidbody flags and constraints on this object's networked data. + + + + + Writes a field reserved to store the rigidbody flags and constraints on the networked data pointed by . + + + + + Base class for 2D and 3D networked rigid body behaviours: + see and , respectively. + This class handles some of the data and logic shared between the two versions. + + + + + Sets a 2D or 3D rigid body kinematic field to , + according to the explicit implementation. + + + + + If the rigid body state is not predicted, but just interpolated between snapshots. + + + + + If was called at least once on this instance. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + To extend the functionality of copying data from the networked buffer to the engine, + override and extend instead. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + To extend the functionality of copying data from the engine to the networked buffer, + override and extend instead. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + + + + Networked flags representing a 2D or 3D rigid body state and characteristics. + + + + + Networked kinematic state. + See also or . + + + + + Networked state. Not used with 2D rigid bodies. + + + + + Networked sleeping state. + See also or . + + + + + Number of words used by a instance on its networked data, + including all inherited fields. + + + + + + + + Reads from the field reserved to store the rigidbody drag on this object's networked data + using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody drag on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody drag on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody angular drag on this object's networked data + using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular drag on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody angular drag on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody mass on this object's networked data + using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody mass on the networked data pointed + by using the default Runner position read accuracy. + + + + + Reads from the field reserved to store the rigidbody mass on the networked data pointed + by using the defined . + + + + + Reads from the field reserved to store the rigidbody flags on this object's networked data. + + + + + Reads from the field reserved to store the rigidbody flags on the networked data pointed by . + + + + + Reads from the field reserved to store the rigid body flags on this object's networked data + and returns the raw word value that might also carry flags specific to 2D or 3D rigid bodies. + See also + NetworkRigidbody.ReadNetworkRigidbodyFlags or + NetworkRigidbody2D.ReadNetworkRigidbodyFlags. + + + + + Reads from a field reserved to store the rigid body flags on the networked data pointed by + and returns the raw word value that might also carry flags specific to 2D or 3D rigid bodies. + See also + NetworkRigidbody.ReadNetworkRigidbodyFlags or + NetworkRigidbody2D.ReadNetworkRigidbodyFlags. + + + + + Reads the values of a rigidbody drag, angular drag and mass field on this object's networked data + using the default Runner position read accuracy. + The values are returned as the x, y and z values of a Vector3, respectively. + + + + + Reads the values of a rigidbody drag, angular drag and mass field on the networked data pointed + by using the default Runner position read accuracy. + The values are returned as the x, y and z values of a Vector3, respectively. + + + + + Reads the values of a rigidbody drag, angular drag and mass field on the networked data pointed + by using the defined . + The values are returned as the x, y and z values of a Vector3, respectively. + + + + + Writes the field reserved to store the rigidbody drag on this object's networked data + using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody drag on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody drag on the networked data pointed + by using the defined . + + + + + Writes the field reserved to store the rigidbody angular drag on this object's networked data + using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular drag on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody angular drag on the networked data pointed + by using the defined . + + + + + Writes the field reserved to store the rigidbody mass on this object's networked data + using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody mass on the networked data pointed + by using the default Runner position write accuracy. + + + + + Writes to the field reserved to store the rigidbody mass on the networked data pointed + by using the defined . + + + + + Writes to the field reserved to store the rigidbody flags on this object's networked data. + + + + + Reads from the field reserved to store the rigidbody flags on the networked data pointed by . + + + + + Writes to the field reserved to store the on this object's networked data. + See also + NetworkRigidbody.ReadNetworkRigidbodyFlags or + NetworkRigidbody2D.ReadNetworkRigidbodyFlags. + + + + + Writes to the field reserved to store the on this object's networked data. + See also + NetworkRigidbody.ReadNetworkRigidbodyFlags or + NetworkRigidbody2D.ReadNetworkRigidbodyFlags. + + + + + Writes the values of a rigidbody drag, angular drag and mass field on this object's networked data + using the default Runner position read accuracy. + The values must be provided as the x, y and z values of a Vector3, respectively. + + + + + Reads the values of a rigidbody drag, angular drag and mass field on the networked data pointed + by using the default Runner position read accuracy. + The values must be provided as the x, y and z values of a Vector3, respectively. + + + + + Reads the values of a rigidbody drag, angular drag and mass field on the networked data pointed + by using the defined . + The values must be provided as the x, y and z values of a Vector3, respectively. + + + + + Replicates a Unity Transform's position and rotation states from the to all other peers. + Add this component to a GameObject to sync the position and rotation data. + A is required on this GameObject or a parent of this GameObject. + + Besides handling smooth interpolation of a target view transform, this behaviour also provides + interpolated prediction error correction and special handling logic for teleports with controlled interpolation. + + + + + + The interpolation velocity used when calling and not specifying a value. + By default, this value is . + Some behaviours like and override + this property to provide better default values, like the latest known velocity of the rigid body. + + + + + The interpolation angular velocity used when calling and not specifying a value. + By default, this value is . + Some behaviours like and override + this property to provide better default values, like the latest known angular velocity of the rigid body. + + + + + Teleports the object to the provided position while making the networked state aware + that the object was teleported and the view interpolation needs special handling. + The is immediately set to the transform's position field + and additional data is stored in order to interpolate the view object between the ticks. + While interpolating the visual representation of the networked object TO a tick where a teleport happened, + the object's latest state (tick when the teleport is performed) will not be used, as doing so would cause + the interpolation target to visually interpolate along the teleported distance. Instead, the view will be + interpolated between the FROM state (tick before the teleport) and an artificial position + computed based on this FROM state and an interpolation velocity (see ), + in order to emulate how the object would behave if the teleport had not been performed. + + The position which the object is being teleported to. + + The emulated velocity of the interpolation target while interpolating to a tick where a teleport happened. + If null, the will be used. + + + If the artificial position for teleport interpolation should be computed backwards (artificial From, based on the + To state - the interpolation velocity) or forward (artificial To, based on the From state + interpolation velocity). + + + + + Teleports the object to the provided rotation while making the networked state aware + that the object was teleported and the view interpolation needs special handling. + The is immediately set to the transform's rotation field + and additional data is stored in order to interpolate the view object between the ticks. + While interpolating the visual representation of the networked object TO a tick where a teleport happened, + the object's latest state (tick when the teleport is performed) will not be used, as doing so would cause + the interpolation target to visually interpolate along the teleported rotation. Instead, the view will be + interpolated between the FROM state (tick before the teleport) and an artificial rotation + computed based on this FROM state and an interpolation angular velocity (see ), + in order to emulate how the object would behave if the teleport had not been performed. + + The rotation which the object is being teleported to. + + The emulated angular velocity of the interpolation target while interpolating to a tick where a teleport happened. + If null, the will be used. + + + If the artificial position for teleport interpolation should be computed backwards (artificial From, based on the + To state - the interpolation velocity) or forward (artificial To, based on the From state + interpolation velocity). + + + + + Teleports the object to the provided position and rotation while making the networked state aware + that the object was teleported and the view interpolation needs special handling. + The is immediately set to the transform's rotation field + and additional data is stored in order to interpolate the view object between the ticks. + See and for more details about why and how + the special interpolation handling is performed. + + The position which the object is being teleported to. + The rotation which the object is being teleported to. + + The emulated velocity of the interpolation target while interpolating to a tick where a teleport happened. + If null, the will be used. + + + The emulated angular velocity of the interpolation target while interpolating to a tick where a teleport happened. + If null, the will be used. + + + If the artificial position for teleport interpolation should be computed backwards (artificial From, based on the + To state - the interpolation velocity) or forward (artificial To, based on the From state + interpolation velocity). + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements and, by default, calls + . + Behaviours that change the Transform state of this predict-spawned object should either be ordered + before NetworkTransform or call after doing so. + If overriding this method in an inheritor, the base method should be called. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Implements and, by default, calls Destroy on this GameObject. + + + + + Implements and does nothing by default. + + + + + If this object is a predicted spawn, caches the current transform + state for interpolated rendering and prediction error correction. + + + + + Number of words used by a instance on its networked data, + including all inherited fields. + + + + + + + + Reads from this object's networked data field holding the interpolation velocity + (used on position teleports) using the default Runner position read accuracy. + See for more information on how this value is used. + + + + + Reads from a field holding the interpolation velocity (used on position teleports) + from the networked data pointed by using the default Runner position read accuracy. + See for more information on how this value is used. + + + + + Reads from a field holding the interpolation velocity (used on position teleports) + from the networked data pointed by with the defined . + See for more information on how this value is used. + + + + + Reads from this object's networked data field holding the interpolation angular velocity + (used on position teleports) using the default Runner position read accuracy. + See for more information on how this value is used. + + + + + Reads from a field holding the interpolation angular velocity (used on position teleports) + from the networked data pointed by using the default Runner position read accuracy. + See for more information on how this value is used. + + + + + Reads from a field holding the interpolation angular velocity (used on position teleports) + on the networked data pointed by with the defined . + See for more information on how this value is used. + + + + + Reads from this object's field holding the tick number where was last called. + + + + + Reads from the networked data pointer by + a field holding the tick number where was last called. + + + + + Reads from this object's field holding the tick number where was last called. + + + + + Reads from the networked data pointer by + a field holding the tick number where was last called. + + + + + Reads from this object's field holding information about the last call + and if should be interpolated forward (emulated To based on From state + interpolation) or backwards + (Emulated From based on To state - interpolation). + + + + + Reads from the networked data pointer by a field holding information about the last + call and if should be interpolated forward (emulated To based on From state + interpolation) + or backwards (Emulated From based on To state - interpolation). + + + + + Reads from this object's field holding information about the last call + and if should be interpolated forward (emulated To based on From state + interpolation) or backwards + (Emulated From based on To state - interpolation). + + + + + Reads from the networked data pointer by a field holding information about the last + call and if should be interpolated forward (emulated To based on From state + interpolation) + or backwards (Emulated From based on To state - interpolation). + + + + + Writes to this object's networked data field holding the interpolation velocity + (used on position teleports) using the default Runner position write accuracy. + See for more information on how this value is used. + + + + + Writes to a field holding the interpolation velocity (used on position teleports) + on the networked data pointed by using the default Runner position write accuracy. + See for more information on how this value is used. + + + + + Writes to a field holding the interpolation velocity (used on position teleports) + from the networked data pointed by with the defined . + See for more information on how this value is used. + + + + + Writes to this object's networked data field holding the interpolation angular velocity + (used on position teleports) using the default Runner position write accuracy. + See for more information on how this value is used. + + + + + Writes to a field holding the interpolation angular velocity (used on position teleports) + on the networked data pointed by using the default Runner position write accuracy. + See for more information on how this value is used. + + + + + Writes to a field holding the interpolation angular velocity (used on position teleports) + from the networked data pointed by with the defined . + See for more information on how this value is used. + + + + + Writes this object's field holding the tick number where was last called. + + + + + Writes to the networked data pointer by + a field holding the tick number where was last called. + + + + + Writes this object's field holding the tick number where was last called. + + + + + Writes to the networked data pointer by + a field holding the tick number where was last called. + + + + + Writes to this object's field holding information about the last call + and if should be interpolated forward (emulated To based on From state + interpolation) or backwards + (Emulated From based on To state - interpolation). + + + + + Writes the networked data pointer by , on a field holding information about the last + call and if should be interpolated forward (emulated To based on From state + interpolation) + or backwards (Emulated From based on To state - interpolation). + + + + + Writes to this object's field holding information about the last call + and if should be interpolated forward (emulated To based on From state + interpolation) or backwards + (Emulated From based on To state - interpolation). + + + + + Writes to this object's field holding information about the last call + and if should be interpolated forward (emulated To based on From state + interpolation) or backwards + (Emulated From based on To state - interpolation). + + + + + Struct that provides relevant parameters when interpolating a + view representation with position and rotation data between two + known states, referred as 'From' and 'To'. + + + + + Normalized factor that represents the exact point between From and To states + at which the view should be displayed. + + + + + Position value at the From state. + + + + + Position value at the To state. + + + + + Position value interpolated between From () + and To () with factor . + + + + + Computed value for smooth correction of prediction error. + In order to apply it, add its value to the + before setting the result to the view transform. + The value will be if there is no prediction error + or is disabled. + + + + + Rotation value at the From state. + + + + + Rotation value at the To state. + + + + + Rotation value interpolated between From () + and To () with factor . + + + + + Computed value for smooth correction of prediction error. + In order to apply it, multiply its value by the + before setting the result to the view transform. + The value will be if there is no prediction error + or is disabled. + + + + + If the Interpolation Target should be interpolated in world space or in the local space of a networked parent. + Interpolating in local space requires more computations, but gives more accurate visuals if/when a parent rotates. + + + + + The Transform object used for smooth view interpolation. + Should be a non-physics GameObject, typically a child of this GameObject or a separate object without colliders. + + + + + Overrides . + If overriding this method in an inheritor, the base method should be called. + + + + + Overrides . + If overriding this method in an inheritor, the base method should be called. + + + + + Overrides , computing the interpolated position and rotation values + according to the , updating the prediction error correction + and calling to apply the results to the . + + + + + Applies the interpolated position and rotation values and prediction error corrections + computed on to the . + + + + + Retrieves the world position values from the From and To states made + available in the interpolation , setting them + to and , respectively. + + + + + Retrieves the world rotation values from the From and To states made + available in the interpolation , setting them + to and , respectively. + + + + + Computes the interpolated transform parameters based on the interpolation data provided. + + + + + If the computed prediction error should be smoothly corrected on + the InterpolationTarget according to + the InterpolatedErrorCorrectionSettings. + + + + + A set of parameters to tune the interpolated correction of prediction + error to be applied on the InterpolationTarget. + + + + + Implements . + If overriding this method in an inheritor, the base method should be called. + + + + + Provides custom API and state replication for collider-based character controller movement (not related to Unity's CharacterController type). + Replicates both the internal state (Velocity, MaxSpeed, etc) and the Unity Transform data from the to all other peers. + Add this component to a GameObject to control movement and sync the position and/or rotation accurately, including client-side prediction. + Usage - For basic prototyping call the following methods: + NetworkCharacterController.Move()
+ NetworkCharacterController.Jump()
+ For more advanced uses, call only the movement query to get full surface movement data (), + and implement bespoke Move/Steering: + NetworkCharacterController.Jump() + A is required. + Not to be combined with either nor . These are mutually-exclusive options. +
+
+ + + Collision and trigger callback interface that can optionally be passed to the movement query. + Common uses: bypass a contact, apply a contact force to the target (can also be achieved just by adding a kinematic Rigidbody to the Character Controller. + + + + + Called for all contacts with a non-trigger collider that penetrates more than . + data about the collision + + + + + Called for all contacts with a trigger collider. Not affected by . + data about the trigger collision + + + + + Hit data passed as parameters to methods. + + + + + The other game object collider in touch with the Character Controller. + + + + + The normal direction of the collision. + + + + + The penetration magnitude. + + + + + True in case this collision is considered a bump (). + + + + + What to do with collisions that are considered a bump. + + + + + Collision always affects this Character Controller, no matter the movement situation. + + + + + Automatically ignore collisions when character is standing. + (the Other NetworkCharacterController will still be affected and not allowed penetrate) + + + + + (Not implemented yet!) + Automatically ignore collisions when collision is from sides or back. + (the Other NetworkCharacterController will still be affected and not allowed penetrate) + + + + + Movement type computed by the movement query based on collisions and surface tangents. + + + + + No desired movement, and standing on a ground. + + + + + No ground or slope collisions (there may be a collision on the upper part). + + + + + No collision qualifies as grounded (angle between up and normal is smaller or equals to max slope). + At least one collision that qualifies as slope (angle between up and surface normal is larger than max slope). + + + + + There's a desired movement direction. + At least one collision qualifies as grounded (angle between up and normal is smaller or equals to max slope). + + + + + Configuration data used both by ComputeRawMovement and the prototype-grade Move and ComputeRawSteering methods. + + + + + Helps stabilize grounded state when walking over slightly bumpy geometry. Influences collision and trigger callbacks. + + + + + How much penetration to correct each frame (used only by Move and ComputeRawSteering) + + + + + for collisions with this Character Controller. + + + + + Only colliders in this layermask will be considered by the movement query. + + + + + Whereas to also compute triggers or not. + + + + + Should player keep control when in FreeFall (used only by Move and ComputeRawSteering). + + + + + When moving, velocity is incremented by this factor over time (used only by Move and ComputeRawSteering). + + + + + When stopped moving, velocity is decremented by this factor over time (used only by Move and ComputeRawSteering). + + + + + Vertical impulse applied when Jump() is called without an overhauling impulse passed (used only by Jump()). + + + + + Horizontal velocity is clamped by this magnitude (used only by Move and ComputeRawSteering). + This will only be used to initialize the corresponding MaxSpeed networked property, which can be changed in runtime. + + + + + Horizontal velocity is clamped by this magnitude (used only by Move and ComputeRawSteering). + + + + + Slope-fall velocity is clamped by this magnitude (used only by Move and ComputeRawSteering). + + + + + Non-networked gravity (used only by Move and ComputeRawSteering). + In case variable gravity is required, the recommendation is to implement custom Move and RawSteering methods. + + + + + Non-networked gravity (used only by Move and ComputeRawSteering). + In case variable gravity is required, the recommendation is to implement custom Move and RawSteering methods. + + + + + Result of a movement query. + + + + + Recommended type of movement based on found collisions. + + + + + Surface normal of the nearest collision. + + + + + Average normal of all collisions. + + + + + Surface normal of the closest collision that qualifies as ground, if any. + + + + + Surface tangent (recommended movement direction) in case there's a desired direction passed. + + + + + Surface tangent (recommended movement direction) in case of a slope (based on gravity). + + + + + Full correction vector to move out of collisions. + + + + + Magnitude of the penetration over the nearest normal. + + + + + True if at least one collision (not necessarily closest) qualifies as ground. + + + + + Total number of collisions found in this execution of the movement query. + + + + + The Collider object used for the movement query (to grab surface normals, penetration correction, etc). + Recommended to be a child of this Character Controller. + + + + + Internal use for automatic Area-of-Interest management. + + + + + Networked current grounded state (used only by Move and COmputeRawSteering) + + + + + Networked current jumped state (used only by Move and ComputeRawSteering to avoid clamping a jump speed when on the first subsequent query the collider still touches the ground) + + + + + Networked current max speed (used only by Move and ComputeRawSteering to clamp horizontal components of Velocity) + + + + + Networked current velocity (used only by Move and ComputeRawSteering to move the Character Controller) + + + + + . + + + + + Basic implementation of a jump impulse (immediately integrates a vertical component to Velocity). + Jump even if not in a grounded state. + Optional, if null Config.BaseImpulse will be used. + + + + + Basic implementation of a full Move. + Performs a movement query, uses its result to compute new Velocity, then applies penetration corrections + velocity integration into the transform position. + Does not change Rotation. + Intended movement direction, subject to movement query + acceleration. + Optional custom callbacks object. + Optional layermask. If not passed, the default one from Config will be used + + + + + Static query for surface movement data. + Does not modify any data on state (neither transform, nor self). + Intended movement direction. WIll be used to compute surface tangents. + Optional custom callbacks object. + Optional layermask. If not passed, the default one from Config will be used + The results + + + + + Enables syncing of Drag, AngularDrag and Mass values. + + + + + Replicates a Unity Rigidbody state from the to all other peers. + Add this component to a GameObject with a Rigidbody component. + A is required on this GameObject or a parent of this GameObject. + + + + + Reference of the Rigidbody associated with this . + + + + + Reads the TRS values from state buffer for this tick, and converts them to world space if using . + + + + + Replicates a Unity Rigidbody state from the to all other peers. + Add this component to a GameObject with a Rigidbody component. + A is required on this GameObject or a parent of this GameObject. + + + + + Reference of the Rigidbody associated with this . + + + + + Reads the TRS values from state buffer for this tick, and converts them to world space if using . + + + + + Replicates Unity parenting. To network parent changes, the new parent must either be null or have a or class derived from that on the GameObject, + and the GameObject being re-parented also requires a based Component. + A is required on this GameObject or a parent of this GameObject. + + + + + The Ptr offset for the memory position of the Parent info. The parenting info is 2 words (8 bytes). + + + + + The Ptr offset for the secondary teleport parent information. This parent is used by the lerp TO target leading up to the teleport. + + + + + Technically, an Anchor does NOT hold position data. + However, NetworkAreaOfInterestBehaviour used to inherit from this class and this was problematic. + Now, instead, it inherits from NetworkAreaOfInterestBehaviour, so we need to have a PositionWordOffset + and leave space for a position field, even though it does not use it. + It is possible for inheritors of this class (like NetworkTransform) to have a "negative" initial offset + and overlap with this position field. + This whole thing was necessary to not break backwards compatibility while the transform-related behaviors are refactored. + + + + + Gets the Ptr offset of the Position values in allocated memory. + + + + + The total number of words used by . Derived classes using allocated memory should start their Ptr offsets using this value. + + + + + The Transform object used for interpolation. + Should be a non-physics GameObject. Typically a child of this Rigidbody, or a separate object without colliders. + The runner/simulation with State Authority will not interpolate if target is null or the same GameObject as the Rigidbody, + as this will conflict with the PhysX simulation. + + + + + + Used by the inspector gui to determine if the supplied transform is acceptable. If false will show a warning box. + + + + + The Transform object used for interpolation. + Should be a non-physics GameObject. Typically a child of this GameObject without colliders, or a separate GameObject without colliders. + + + + + EXPERIMENTAL: To improve interpolation of scaling, the interpolation target is detached from its . + Only check this if you are syncing scaling. + When re-parenting, this component's will re-parent to the parent's . + + + + + Indicates if Transform.parent will be synced. Parent syncing requires the parent of this object either be null or have a component. + + + + + Cached GameObject.transform reference. + + + + + + + Returns true if the parent has changed. + + + + If using local space, tries to get the To and From parents. + If valid parents are found, they are forced to run Render() - as parents should always interpolate before children. + + If a parent value has been serialized. Invalid means unknown parent. + If a parent value has been serialized. Invalid means unknown parent. + (From Parent, To Parent) + + + + Replicates a Unity Transform state from the to all other peers. + Add this component to a GameObject to sync the position and/or rotation. + A is required on this GameObject or a parent of this GameObject. + + + + + The Ptr offset location of the position values in allocated memory. + + + + + The Ptr offset location of the rotation values in allocated memory. + + + + + The Ptr offset location of the local scale values in allocated memory. + + + + + The Ptr offset for the secondary teleport position information. This position is used as the lerp TO target leading up to the teleport. + + + + + The Ptr offset for the secondary teleport rotation information. This rotation is used as the lerp TO target leading up to the teleport. + + + + + The Ptr offset for the secondary teleport scale information. This scale is used as the lerp TO target leading up to the teleport. + + + + + The Ptr offset of the teleport increment value. Increases by one every tick a teleport occurs. + + + + + The total number of words used by . Derived classes using allocated memory should start their Ptr offsets using this value. + + + + + Gets the Ptr offset of the Position values in allocated memory. + + + + + Indicates if Scale should be included in Transform sync and interpolation (when enabled). + + + + + Select if the synced state uses World or Local position and rotation values. + + + + + The frame of the next Teleport action. + + + + + Stores the last UnityEngine.Time.time when interpolation was run for this component. Used to ensure interpolation is only run once per screen refresh. + + + + + Emulates the accuracy loss when writing and reading a Vector3 to/from a buffer, according to the given accuracies. + + + + + Emulates the accuracy loss when writing and reading a Quaternion to/from a buffer, according to the given accuracies. + + + + + Most recent unconsumed Teleport() command. + + + + + Teleport indicates that objects should not interpolate from the previous state to this new state. + + Sets the position or rigidbody.position to this value prior to state sync. + If not null, Sets the position or rigidbody.rotation to this value prior to state sync. + If not null, Sets the localScale to this value prior to state sync. + Accepts a Quaternion, Euler (Vector3), or 2d Z value (float) + Resets velocity, angular velocity, and controller states to default if applicable. + Typically set to true if teleport is used for a respawn. + Sets the velocity after teleport. Ignored if this is not a Rigidbody or CharacterController. Overrides reset. + Sets the angular velocity after teleport. Ignored if this is not a Rigidbody. Overrides reset. + + + + Captures the current state as the interpolation lerp TO target leading up to a teleport, and flags the next tick as a teleport. + Call this BEFORE making any TRS changes to the synced transform which represent the teleport. + For continuously moving rigidbodies, it is preferable to call the explicit Teleport() method. + + + + + Applies the latest unconsumed Teleport() call after the Forward simulation is complete, and before the TRS state is captured. + + + + + Base class for OrderBefore and OrderAfter attributes. + + + + + Fusion attribute used to indicate relative execution order of and derived scripts. + Any number of scripts can be specified. + Usage: + [OrderAfter(typeof(MySimulationBehaviour), typeof(MyNetworkBehaviour), etc)] + + + + + + The array of scripts the attributed script should run before. + + + + + Fusion attribute that defines execution order of this script relative to specified other scripts. + + The and scripts this attributed script should execute AFTER. + + + + Fusion attribute used to indicate relative execution order of and derived scripts. + Any number of scripts can be specified. + Usage: + [OrderBefore(typeof(MySimulationBehaviour), typeof(MyNetworkBehaviour), etc)] + + + + + + The array of scripts the attributed script should run before. + + + + + Fusion attribute that defines execution order of this script relative to specified other scripts. + + The and scripts this attributed script should execute BEFORE. + + + + This sorting class is meant to be released and garbage collected after use. + + + + + Returns sorted list of Types. + + + + + + Locate all SimulationBehaviours, and add them to this system. + + + + + Alphabetize the initial list to create determinism in all following steps. + + + + + Create the node without searching for attributes yet. + + + + + Merge Before and Afters based on the logic: If A is Before B, and B is Before C, then A is Before C... etc + This allows obvious conflicts to be quickly spotted. + + + + + + Converts linked OrderNode list to Type[] + + + + + + Flags a Unity component class as a RunnerVisibilityNodes recognized type. + Will be included in handling, and will be found by component finds. + + + + + Flag component which indicates a NetworkObject has already been factored into a Runner's VisibilityNode list. + + + + + Identifies visible/audible components (such as renderers, canvases, lights) that should be enabled/disabled by . + Automatically added to scene objects and spawned objects during play if running in . + Additionally this component can be added manually at development time to identify specific Behaviours or Renderers you would like to restrict to one enabled copy at a time. + + + + + Types that fusion.runtime isn't aware of, which need to be found using names instead. + + + + + The peer runner that will be used if more than one runner is visible, and this node was manually added by developer (indicating only one instance should be visible at a time). + + + + + The peer/runner with input authority will be used if visible. + + + + + The server peer/runner will be used if visible. + + + + + The first client peer/runner will be used if visible. + + + + + If more than one runner instance is visible, this indicates which peer's clone of this entity should be visible. + + + + + The associated component with this node. This Behaviour or Renderer will be enabled/disabled when its parent is changed. + + + + + Guid is used for common objects (user flagged nodes that should only run in one instance), to identify matching clones. + + + + + Set to false to indicate that this object should remain disabled even when is set to true. + + + + + Sets the visibility state of this node. + + + + + + Dictionary lookup for manually added visibility nodes (which indicates only one instance should be visible at a time), + which returns a list of nodes for a given LocalIdentifierInFile. + + + + + Find all component types that contribute to a scene rendering, and associate them with a component, + and add them to the runner's list of visibility nodes. + + + + + + + Force a complete visibility refresh on all runners. Typically used if a runner is destroyed/shutdown. + + + + + Reapplies a runner's IsVisibile setting to all of its registered visibility nodes. + + + + + + + Automatically adds a for each indicated component. + These indicated components will be limited to no more than one enabled instance when running in Multi-Peer mode. + + + + + If more than one runner instance is visible, this indicates which peer's clone of this entity should be visible. + + + + + Collection of components that will be marked for Multi-Peer mode as objects that should only have one enabled instance. + + + + + Prefix for the GUIDs of components which are added at runtime. + + + + + At runtime startup, this adds a for each component reference to this GameObject. + + + + + Finds visual/audio components on this GameObject, and adds them to the Components collection. + + + + + Finds visual/audio nested components on this GameObject and its children, and adds them to the Components collection. + + + + + Base class for a Fusion aware Behaviour (derived from UnityEngine.MonoBehavour). + Objects derived from this object can be associated with a and . + If a parent is found, this component will also be associated with that network entity. + + + + + The this component is associated with. + + + + + The this component is associated with. May be null if this GameObject does not have a . + + + + + Fusion FixedUpdate timing callback. + + + + + Post simulation frame rendering callback. Runs after all simulations have finished. Use in place of Unity's Update when Fusion is handling Physics. + + + + + Attribute for specifying which and this will execute in. + Can be used to limit execution to only Host, Server or Client peers, or to only execute on Resimulation or Forward ticks. + Usage: + + [SimulationBehaviour(Stages = SimulationStages.Forward, Modes = SimulationModes.Server | SimulationModes.Host)] + + + + + + Flag for which stages of the simulation loop this component will execute this script. + + + + + Flag for which indicated peers in will execute this script. + + + + + The default behaviour interfaces + + + + + Tools to replace GetComponent variants that respects nested objects. + These are used to find components of a NetworkedObjects without also finding components that belong to parent or child NetworkedObjects. + + + + + Find T on supplied transform or any parent. Unlike GetComponentInParent, GameObjects do not need to be active to be found. + + + + + Returns all T found between the child transform and its root. Order in List from child to parent, with the root/parent most being last. + + + + + Same as GetComponentInParent, but will always include inactive objects in search. + Will also stop recursing up the hierarchy when the StopOnT is found. + + + + + UNTESTED + + + + + Finds components of type T on supplied transform, and every parent above that node, inclusively stopping on node StopT component. + + + + + Same as GetComponentsInChildren, but will not recurse into children with component of the StopT type. + + + + + Same as GetComponentsInChildren, but will not recurse into children with any component of the types in the stopOn array. + + + + + Same as GetComponentsInChildren, but will not recurse into children with component of the StopT type. + + Cast found components to this type. Typically Component, but any other class/interface will work as long as they are assignable from SearchT. + Find components of this class or interface type. + When this component is found, no further recursing will be performed on that node. + + + + Find All instances of Component type in a scene. Attempts to respect the hierarchy of the scene objects to produce a more deterministic order. + This is a slower operation, and does produce garbage collection. + + + + + Find All instances of Component type in a scene. Attempts to respect the hierarchy of the scene objects to produce a more deterministic order. + This is a slower operation which should not be run every update. + + + + Supplied list that will be populated by this find. + Whether results should include inactive components. + + + + Find All instances of Component type in a scene. Attempts to respect the hierarchy of the scene objects to produce a more deterministic order. + This is a slow operation, and does produce garbage collection. + + The type being searched for. + Casts all found objects to this type, and returns collection of this type. Objects that fail cast are excluded. + + Whether results should include inactive components. + + + + Find All instances of Component type in a scene. Attempts to respect the hierarchy of the scene objects to produce a more deterministic order. + This is a slower operation and should not be run every update. + + + + + Supplied list that will be filled with found objects. + Whether results should include inactive components. + + + + Flag interface for custom NetworkInput structs. + + + + + Translates structs and represents them in Fusions's unsafe allocated memory. + + + + + Returns pointer to the struct data in memory. + + + + + Tries to export data as the indicated T struct. + + + + + Tries to import data from a struct. + + + + + Call this to batch-optimize any object-changes notified through + ssBVHNode.refit_ObjectChanged(..). For example, in a game-loop, + call this once per frame. + + + + + Call this when you wish to update an object. This does not update straight away, but marks it for update when Optimize() is called + + + + + + initializes a BVH with a given nodeAdaptor, and object list. + + + + + tryRotate looks at all candidate rotations, and executes the rotation with the best resulting SAH (if any) + + + + + + Represents a single lag-compensated collider. + Multiple component instances can be added anywhere in the hierarchy of a which includes a . + + + + + The collision geometry type for this . + + + + + When is set to , this defines the local-space geometry for narrow-phase checks. + + + + + When is set to , this defines the local-space geometry for narrow-phase checks. + + + + + This 's local-space offset from its GameObject position. + + + + + Reference to the top-level component for this . + + + + + The index of this hitbox in the array on . + The value is set by the root when initializing the nested hitboxes with . + + + + + Mask to access the state of this hitbox on the root. + + + + + Get or set the state of this Hitbox. + If a hitbox or its HitboxRoot are not active, it will not be hit by lag-compensated queries. + + + + + World-space position (includes Offset) of this . + + + + + Draws this hitbox gizmo on Unity editor. + + + + + Per-query options for lag compensation (both raycast and overlap). + + + + + Default, no extra options. + + + + + Add this to include checks against PhysX colliders. + + + + + Subtick accuracy query (exactly like seen by player). + + + + + Compute surface normal and distance of raycast hit. + + + + + Settings for lag compensation history. + + + + + Hitbox snapshot history size in ticks. + + + + + Broadphase BVH node expansion factor (default 20%) for leaf nodes, so updates are not too frequent. + + + + + Optional: tries to optimize broadphase BVH every update. May be removed in the future. + + + + + Enable gizmos for current BVH nodes. + + + + + Enable gizmos for hitbox history. + + + + + Server editor gizmo color for BVH nodes. + + + + + Client editor gizmo color for BVH nodes. + + + + + Editor gizmo color for history representations. + + + + + Entry point for lag compensated queries, which + maintains a history buffer, and provides lag compensated raycast and overlap methods. + Singleton instance is accessible through the property Runner.LagCompensation. + Usage - Call any of the following methods: + HitboxManager.Raycast()

+ HitboxManager.RaycastAll()

+ HitboxManager.PositionRotation()

+ HitboxManager.OverlapSphere()
+ These methods use the history buffer to perform a query against a state consistent with how the indicated perceived them locally. +
+
+ + + Debug data from Broadphase BVH (tree depth). + + + + + Debug data from Broadphase BVH (total nodes count). + + + + + Debug data from lag compensation history (registered count). + + + + + Performs a lag-compensated raycast query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Raycast origin, in world-space + Raycast direction, in world-space + Raycast length + Player who "owns" this raycast. Used by the server to find the exact hitbox snapshots to check against. + Raycast results will be filled in here. + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Trigger interaction behavior when also querying PhysX. + true is something is hit + + + + Performs a lag-compensated raycast query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Raycast origin, in world-space + Raycast direction, in world-space + Raycast length + Simulation tick number to use as the time reference for the lag compensation (use this for server AI, and similar). + + Simulation tick number to use as the time reference for the lag compensation. + If provided, must be combined with the parameter for interpolation between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded value of . + + + Interpolation value when querying between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded alpha value. + + Raycast results will be filled in here. + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Trigger interaction behavior when also querying PhysX. + true is something is hit + + + + Performs a lag-compensated raycast query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + Important: results are NOT sorted by distance. + + Raycast origin, in world-space + Raycast direction, in world-space + Raycast length + Player who "owns" this raycast. Used by the server to find the exact hitbox snapshots to check against. + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + total number of hits + + + + Performs a lag-compensated raycast query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + Important: results are NOT sorted by distance. + + Raycast origin, in world-space + Raycast direction, in world-space + Raycast length + Simulation tick number to use as the time reference for the lag compensation (use this for server AI, and similar). + + Simulation tick number to use as the time reference for the lag compensation. + If provided, must be combined with the parameter for interpolation between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded value of . + + + Interpolation value when querying between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded alpha value. + + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + total number of hits + + + + Performs a lag-compensated overlap sphere query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Sphere center, in world-space + Sphere radius + Player who "owns" this overlap. Used by the server to find the exact hitbox snapshots to check against. + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + total number of hits + + + + Performs a lag-compensated overlap sphere query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Sphere center, in world-space + Sphere radius + The tick to be queried + + Simulation tick number to use as the time reference for the lag compensation. + If provided, must be combined with the parameter for interpolation between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded value of . + + + Interpolation value when querying between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded alpha value. + + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + total number of hits + + + + Performs a lag-compensated box overlap query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Center of the box in world space. + Half of the size of the box in each dimension. + Rotation of the box. + Player who "owns" this overlap. Used by the server to find the exact hitbox snapshots to check against. + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + The total number of hits found. + + + + Performs a lag-compensated box overlap query against all registered hitboxes. + If the flag is indicated, query will also include PhysX colliders, + PhysX colliders are recommended for static geometry, rather than Hitboxes. + + Center of the box in world space. + Half of the size of the box in each dimension. + Rotation of the box. + The exact tick to be queried + + Simulation tick number to use as the time reference for the lag compensation. + If provided, must be combined with the parameter for interpolation between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded value of . + + + Interpolation value when querying between and . + If is included on , this query will be resolved against hitbox colliders interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded alpha value. + + List to be filled with hits (both hitboxes and/or PhysX colliders, if included). + Only objects with matching layers will be checked against. + Opt-in flags to compute with sub-tick accuracy () and/or to include PhysX (). + Clear list of hits before filling with new ones (defaults to true). + Trigger interaction behavior when also querying PhysX. + The total number of hits found. + + + + Performs a lag-compensated query for a specific Hitbox position and rotation. + + The target hitbox to be queried in the past + The tick to be queried + + Simulation tick number to use as the time reference for the lag compensation. + If provided, must be combined with the parameter for interpolation between and . + If is requested, the query will return the hitbox state interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded value of . + + + Interpolation value when querying between and . + If is requested, the query will return the hitbox state interpolated between the specified ticks. + Otherwise, only one of the two ticks will be considered, according to the rounded alpha value. + + Will be filled with the hitbox position at the time of the tick + Will be filled with the hitbox rotation at the time of the tick + If the query should interpolate between ticks to reflect exactly what was seen on the client. + + + + Performs a lag-compensated query for a specific Hitbox position and rotation. + + The target hitbox to be queried in the past + Player who "owns" this overlap. Used by the server to find the exact hitbox snapshots to check against. + Will be filled with the hitbox position at the time of the tick + Will be filled with the hitbox rotation at the time of the tick + If the query should interpolate between ticks to reflect exactly what was seen on the client. + + + + Internal use. Inserts (new ones) and updates all registered hitboxes into lag compensation history. + + + + + Unity Gizmos. + + + + + Root group container. Manages registering/unregistering hitboxes with the group, and defines the broadphase geometry for the group. + Broadphase is the initial rough query used by raycasts/overlaps/etc to find potential hit candidates, + which are then used in the final narrowphase query. + + + + + Get or set the state of this HitboxRoot. + For a hitbox to be hit by lag-compensated queries, both it and its HitboxRoot must be active. + + + + + The max number of hitboxes allowed under the same root. + + + + + The radius of the broadphase bounding sphere for this group. + Used by to insert/update lag compensated NetworkObjects into its BVH (bounding volume hierarchy) data structure. + Be sure this radius encompasses all children components (including their full ranges of animation motion). + We plan to offer an option to dynamically compute the bounding volume, but the performance trade of will still favor a hand-crafted radius. + Broadphase is the initial rough query used by raycasts/overlaps/etc to find potential hit candidates, + which are then used in the final narrowphase query. + + + + + Local-space offset of the broadphase bounding sphere from its transform position. + Adjust the and until the sphere gizmo (shown in the Unity Scene window) + encompasses all children components (including their full ranges of animation motion). + Broadphase is the initial rough query used by raycasts/overlaps/etc to find potential hit candidates, + which are then used in the final narrowphase query. + + + + + All Hitbox instances in hierarchy. Auto-filled at Spawned. + + + + + Reference to associated hitbox manager (from which lag compensated queries can be performed). + + + + + Finds child components, and adds them to the collection. + + + + + Sets to a rough value which encompasses all in their current positions. + + + + + Sets the state of a Hitbox instance under this root. + Both the hitbox and its root must be active in order for it to be hit by lag-compensated queries. + + A hitbox instance under the hierarchy of this root. + If the hitbox should be activated or deactivated. + If the of the is outside the valid range. + In Debug configuration, if the is not part of this root. + + + + Checks the state of a Hitbox instance under this root. + Both the hitbox and its root must be active in order for it to be hit by lag-compensated queries. + + A hitbox instance under the hierarchy of this root. + True if the is part of this root and is active. + If the of the is outside the valid range. + In Debug configuration, if the is not part of this root. + + + + Queries can hit either fusion's custom or Unity's standard Physx colliders. + + + + + Used when a raycast does not hit anything. Not used on overlaps. + + + + + is a Fusion . + + + + + is a Unity PhysX Collider. + + + + + Defines the collision geometry type of a . + + + + + [Future Use] to represent a disabled . + + + + + Geometry is a box, fill in Extents and (optional) Offset. + + + + + Geometry is a sphere, fill in Radius and (optional) Offset. + + + + + Defines a lag compensated query hit result. + + + + + Hit object source (PhysX or Fusion Hitboxes). + + + + + The Unity Game Object that was hit. Its data is not lag compensated. + This is either the 's or the 's gameObject, + depending on the object hit being a lag-compensated Hitbox or a regular Unity collider, respectively. + + + + + Surface normal (if requested) of the hit, at the lag compensated time. + + + + + Point of impact of the hit, at the lag compensated time. + + + + + Distance (if requested) to hit, at the lag compensated time. + + + + + Fusion's . Null in case the hit was on PhysX. + + + + + PhysX collider hit. Null in case hit is a Fusion . + + + + + HitboxBuffer will store all snapshots of the colliders into a circular buffer + + + + + HitboxContainer represents 1 snapshot of all containers in a given Tick + + + + + A Networked fusion type for degrees. This can be used with the , in RPCs, or in structs. + + + + + Clamps the current value to the supplied min-max range. + + + + + Returns the smaller of two supplied angles. + + + + + Returns the larger of two supplied angles. + + + + + Lerps between two angle values. + + + + + Returns a the value, clamped to the min-max range. + + + + + Alternative base class to Unity's MonoBehaviour. + This allows for components that work both in Unity, as well as the Photon relays. + + + + + Wrapper for Unity's GameObject.AddComponent() + + + + + Wrapper for Unity's GameObject.TryGetComponent() + + + + + Wrapper for Unity's GameObject.GetComponentInChildren() + + + + + Wrapper for Unity's GameObject.Destroy() + + + + + Delegate definition for changed callbacks. + + + + + Wrapping struct around changed behaviours which allows you to load old and new values. + + + + + The wrapped changed behaviour instance. The behaviour will have the new networked values. + Previous values can be obtained by calling changed.Behaviour.LoadOld(), then accessing the networked properties of changed.behaviour. + + + + + Tells the OnChanged callback system that this object needs to be re-scanned for changes after the current change scan is completed. + Call this is you are changing data inside an OnChange callback and can't wait for the next tick to execute until the callback is triggered. + + + + + Sets the changed behaviour to represent the previous state before the triggering change occurred. + To get previous values, call this before getting networked properties of . + + + + + Sets the to represent the changed state after the triggering change occurred. + This is the already default state of the behaviour when an OnChanged callback occurs, + and only needs to be called to undo a previous call in your callback handler. + + + + + Icon to be rendered on the component graphic header in the Unity inspector. + + + + + Color of the component graphic header in the Unity inspector. None indicates no header graphic should be used. + + + + + This may only be deterministic on 64 bit systems. + + + + + + + A set of parameters that tune the interpolated correction of prediction error on transform data. + + + + + + A factor with dimension of 1/s (Hz) that works as a lower limit for how much + of the accumulated prediction error is corrected every frame. + This factor affects both the position and the rotation correction. + Suggested values are greater than zero and smaller than MaxRate. + + + E.g.: MinRate = 3, rendering delta time = (1/60)s: at least 5% (3 * 1/60) of the accumulated error + will be corrected on this rendered frame. + + + This threshold might not be respected if the resultant correction magnitude is + below the PosMinCorrection + or above the PosTeleportDistance, for the position error, + or above the RotTeleportRadians, for the rotation error. + + + + + + + A factor with dimension of 1/s (Hz) that works as a upper limit for how much + of the accumulated prediction error is corrected every frame. + This factor affects both the position and the rotation correction. + Suggested values are greater than MinRate + and smaller than half of a target rendering rate. + + + E.g.: MaxRate = 15, rendering delta time = (1/60)s: at maximum 25% (15 * 1/60) of the accumulated error + will be corrected on this rendered frame. + + + This threshold might not be respected if the resultant correction magnitude is + below the PosMinCorrection or + above the PosTeleportDistance, for the position error, + or above the RotTeleportRadians, for the rotation error. + + + + + + + The reference for the magnitude of the accumulated position error, in meters, + at which the position error will be corrected at the MinRate. + Suggested values are greater than PosMinCorrection + and smaller than PosBlendEnd. + + + In other words, if the magnitude of the accumulated error is equal to or smaller than this threshold, + it will be corrected at the MinRate. + If, instead, the magnitude is between this threshold and PosBlendEnd, + the error is corrected at a rate between MinRate + and MaxRate, proportionally. + If it is equal to or greater than PosBlendEnd, + it will be corrected at the MaxRate. + + + Note: as the factor is expressed in distance units (meters), it might need to be scaled + proportionally to the overall scale of objects in the scene and speeds at which they move, + which are factors that affect the expected magnitude of prediction errors. + + + + + + + The reference for the magnitude of the accumulated position error, in meters, + at which the position error will be corrected at the MaxRate. + Suggested values are greater than PosBlendStart + and smaller than PosTeleportDistance. + + + In other words, if the magnitude of the accumulated error is equal to or greater than this threshold, + it will be corrected at the MaxRate. + If, instead, the magnitude is between PosBlendStart and this threshold, + the error is corrected at a rate between MinRate + and MaxRate, proportionally. + If it is equal to or smaller than PosBlendStart, + it will be corrected at the MinRate. + + + Note: as the factor is expressed in distance units (meters), it might need to be scaled + proportionally to the overall scale of objects in the scene and speeds at which they move, + which are factors that affect the expected magnitude of prediction errors. + + + + + + + The value, in meters, that represents the minimum magnitude of the accumulated position error + that will be corrected in a single frame, until it is fully corrected. + + + This setting has priority over the resultant correction rate, i.e. the restriction + will be respected even if it makes the effective correction rate be different than + the one computed according to the min/max rates and start/end blend values. + Suggested values are greater than zero and smaller than PosBlendStart. + + + Note: as the factor is expressed in distance units (meters), it might need to be scaled + proportionally to the overall scale of objects in the scene and speeds at which they move, + which are factors that affect the expected magnitude of prediction errors. + + + + + + + The value, in meters, that represents the magnitude of the accumulated + position error above which the error will be instantaneously corrected, + effectively teleporting the rendered object to its correct position. + Suggested values are greater than PosBlendEnd. + + + This setting has priority over the resultant correction rate, i.e. the restriction + will be respected even if it makes the effective correction rate be different than + the one computed according to the min/max rates and start/end blend values. + + + Note: as the factor is expressed in distance units (meters), it might need to be scaled + proportionally to the overall scale of objects in the scene and speeds at which they move, + which are factors that affect the expected magnitude of prediction errors. + + + + + + + The reference for the magnitude of the accumulated rotation error, in radians, + at which the rotation error will be corrected at the MinRate. + Suggested values are smaller than RotBlendEnd. + + + In other words, if the magnitude of the accumulated error is equal to or smaller than this threshold, + it will be corrected at the MinRate. + If, instead, the magnitude is between this threshold and RotBlendEnd, + the error is corrected at a rate between MinRate + and MaxRate, proportionally. + If it is equal to or greater than RotBlendEnd, + it will be corrected at the MaxRate. + + + + + + + The reference for the magnitude of the accumulated rotation error, in radians, + at which the rotation error will be corrected at the MaxRate. + Suggested values are greater than RotBlendStart + and smaller than RotTeleportRadians. + + + In other words, if the magnitude of the accumulated error is equal to or greater than this threshold, + it will be corrected at the MaxRate. + If, instead, the magnitude is between RotBlendStart and this threshold, + the error is corrected at a rate between MinRate + and MaxRate, proportionally. + If it is equal to or smaller than RotBlendStart, + it will be corrected at the MinRate. + + + + + + + The value, in radians, that represents the magnitude of the accumulated + rotation error above which the error will be instantaneously corrected, + effectively teleporting the rendered object to its correct orientation. + Suggested values are greater than RotBlendEnd. + + + This setting has priority over the resultant correction rate, i.e. the restriction + will be respected even if it makes the effective correction rate be different than + the one computed according to the min/max rates and start/end blend values. + + + + + + Fusion type for networking arrays. Capacity is fixed, and needs to be set the maximum possible number of entries.



+ Typical Usage: + + [Networked, Capacity(4)]

+ NetworkArray<float> syncedArray => default; +
+ Optional usage (for NetworkBehaviours ONLY - this is not legal in INetworkStructs): + + [Networked, Capacity(4)]

+ NetworkArray<int> syncedArray { get; } = MakeInitializer(new int[] { 1, 2, 3, 4 });

+
+ Usage for modifying data: + + var array = syncedArray; + array.Set(123); + array[0] = 456; + +
+ T can be a primitive, or an INetworkStruct. +
+ + + The fixed size of the array. + + + + + Indexer of array elements. + + + + + NetworkArray constructor. + + + + + Returns the array value at supplied index. + + + + + Sets the array value at the supplied index. + + + + + Allocates a new array and copies values from this array. For a non-alloc alternative use . + + + + + Adds each value to the supplied List. This does not clear the list, so values will be appended to the existing list. + + + + + Copies values to the supplied array. + + + If true, this method will throw an error if the supplied array is smaller than this . If false, will only copy as many elements as the target array can hold. + + + + Copies a range of values in from a supplied source array. + + Starting index of elements in source. + Number of sequential source elements to copy in. + + + + Copies a range of values in from a supplied source list. + + Starting index of elements in source. + Number of sequential source elements to copy in. + + + + Returns the elements of this array as a string, with value separated by \n characters. Specifically for use in the Unity inspector. + This is private and only is found by NetworkBehaviourEditor using reflection, so do not rename this method. + + + + + Fusion type for networking Dictionaries. Capacity is fixed, and needs to be set the maximum possible number of entries.



+ Typical Usage: + + [Networked, Capacity(10)]

+ NetworkDictionary<int, float> syncedDict => default;

+
+ Usage for modifying data: + + var dict = syncedDict; + dict.Add(5, 123); + dict[5] = 456; + dict.Remove(5); + +
+ Key can be a primitive, or an INetworkStruct. + Value can be a primitive, or an INetworkStruct. +
+ + + Current number of key/value entries in the Dictionary. + + + + + The maximum number of entries this dictionary may contain. + + + + + Key indexer. Gets/Sets value for specified key. + + + + + Remove all entries from the Dictionary, and clear backing memory. + + + + + Returns true if the Dictionary contains an entry for the given key. + + + + + Returns true if the Dictionary contains an entry value which compares as equal to given value. + + The value to compare against. + Specify custom IEqualityComparer to be used for compare. + + + + Returns the value for the given key. Will throw an error if the key is not found. + + + + + Sets the value for the given key. Will add a new key if the key does not already exist. + + + + + Adds a new key value pair to the Dictionary. If the key already exists, will return false. + + + + + Attempts to get the value for a given key. If found, returns true. + + The key to remove. + Returns value of removed item. Returns default value if key did not exist. + Returns true if key was found. + + + + Remove entry from Dictionary. + + + Returns true if key was found. + + + + Removes entry from Dictionary. If successful (key existed), returns true and the value of removed item. + + The key to remove. + Returns value of removed item. Returns default value if key did not exist. + Returns true if key was found. + + + + Fusion type for networking LinkedLists. Capacity is fixed, and needs to be set the maximum possible number of entries.



+ Typical Usage: + + [Networked, Capacity(10)]

+ NetworkLinkedList<int> syncedLinkedList => default; +
+ Optional usage (for NetworkBehaviours ONLY - this is not legal in INetworkStructs): + + [Networked, Capacity(4)]

+ NetworkLinkedList<int> syncedLinkedList { get; } = MakeInitializer(new int[] { 1, 2, 3, 4 });

+
+ Usage for modifying data: + + var list = syncedLinkedList; + list.Add(123); + list[0] = 456; + list.Remove(0); + +
+ T can be a primitive, or an INetworkStruct. +
+ + + Returns the current element count. + + + + + Returns the max element count. + + + + + Element indexer. + + + + + Removes and clears all list elements. + + + + + Returns true if the value already exists in the list. + + + + + Returns true if the value already exists in the list. + + + + + Sets the value at supplied index. + + + + + Returns the value at supplied index. + + + + + Returns the index with this value. Returns -1 if not found. + + + + + Returns the index with this value. Returns -1 if not found. + + Specify custom IEqualityComparer to be used for compare. + + + + Removes the first found element with indicated value. + + + + + Removes the first found element with indicated value. + + + + + Adds a value to the end of the list. + + + + + + PCG32 random generator, 16 bytes in size. + http://www.pcg-random.org + + + + + 0x1.00000001p-32 aka 0x3df0000000100000 + + + + + 0x1p-32 aka 0x3df0000000000000 + + + + + 0x1.000002p-24 aka 0x33800001 + + + + + 0x1p-24 aka 0x33800000 + + + + + Returns a random Double within [0, 1] (range is inclusive). + + + + + + Returns a random Double within [0, 1) (range is exclusive). + + + + + + Returns a random Single within [0, 1] (range is inclusive). + + + + + + [0, 1) + Returns a random Single within [0, 1) (range is exclusive). + + + + + + [int.MinValue, int.MaxValue] + + + + + + [0, uint.MaxValue] + + + + + + Returns a random Double within [minInclusive, maxInclusive] (range is inclusive). + If minInclusive is greater than maxInclusive, then the numbers are automatically swapped. + + + + + Returns a random Single within [minInclusive, maxInclusive] (range is inclusive). + If minInclusive is greater than maxInclusive, then the numbers are automatically swapped. + + + + + Returns a random Int32 within [minInclusive, maxExclusive) (range is exclusive). + If minInclusive and maxExclusive are equal, then the "exclusive rule" is ignored and minInclusive will be returned. + If minInclusive is greater than maxExclusive, then the numbers are automatically swapped. + + + + + Returns a random Int32 within [minInclusive, maxInclusive] (range is inclusive). + If minInclusive is greater than maxInclusive, then the numbers are automatically swapped. + + + + + Returns a random UInt32 within [minInclusive, maxExclusive) (range is exclusive). + If minInclusive and maxExclusive are equal, then the "exclusive rule" is ignored and minInclusive will be returned. + If minInclusive is greater than maxExclusive, then the numbers are automatically swapped. + + + + + Returns a random UInt32 within [minInclusive, maxInclusive] (range is inclusive). + If minInclusive is greater than maxInclusive, then the numbers are automatically swapped. + + + + + Fixed-size UTF32 string. All operations are alloc-free, except for converting to . + + + + + + Maximum UTF32 string length. + + + + + Converts to/from regular UTF16 string. Setter is alloc-free. Use + to get possibly alloc-free conversion. + + + + + Number of UTF32 scalars. It is equal or less than or the length + of , because those use UTF16 encoding, which needs two characters to encode + some values. + + + + + Returns UTF32 scalar at position. To iterate over characters, + use . + + + + + + + Checks if is equivalent and if not converts to UTF16 and + stores the result in . + + + + False if no conversion was performed, true otherwise. + + + + Converts to UTF32 string and stores it internally. + + + False if was too long to fit and had to be trimmed. + + + + Calculates the length of the equivalent UTF16 string. + + + + + + Represents a Fusion player. + + The PlayerRef, in contrast to the player index, is 1-based. The reason is that default(PlayerRef) will return a "null/invalid" player ref struct for convenience. There are automatic cast operators that can cast an int into a PlayerRef. + + default(PlayerRef), internally a 0, means NOBODY + PlayerRef, internally 1, is the same as player index 0 + PlayerRef, internally 2, is the same as player index 1 + + + + + None player + + + + + If this player index is valid + + + + + Raw encoded value + + + + + Player id this player ref represents + + + + + Note: You are passing the bitcount of the length, not the length. Normal values will be in the 5-8 range (32 - 256). + This RingArray is meant to be fast, so it only uses counts of 2^x. + + Element count will be 2 to this power. + + + + Indexer. Runs a fast mod on the frameId to make it conform to the ring array. + + + + + + Wrapper class which unifies rotation handling of Quaternion, Euler or Z-only (used by 2D). + Can store a Quaternion, Euler or Z-axis (for 2d rotation), and implicitly casts between those types. + + + + + Indicates of the stored rotation is a Quaternion or Euler type. + + + + + Returns the rotation as a Quaternion. If original type was a Euler or Rotation Z-axis only, that is converted to a Quaternion. + + + + + Returns the rotation as a Euler. If original type was Quaternion, that is converted to a Euler. + + + + + Returns the z-axis value of the rotation. If original type was Quaternion, first converts to a Euler then returns the z value of that Euler. + + + + + None scene + + + + + If this scene index is valid + + + + + The unique identifier for a network entity. + + + + + Offset for the bitflag that indicates of this tick is predicted or final. + + + + + + + + + + + + + + + String conversion specifically for use in prefixing names of GameObjects. + + + + + + The primary Fusion component for networked GameObject entities. + This stores the object's network identity and manages the object's state and input authority. + + + + + The unique identifier for this network entity. + + + + + Signal that this comes from a Resume Spawn + + + + + The this entity is associated with. + + + + + How Object Interest is determined for this object. + Only applicable to mode, and if is enabled. + + + + + All players will start with these Interest Groups explicitly enabled for this object. + Use on the server + to explicitly add or remove a player's interest in groups for this . + + + + + If the player with State Authority for this Object leaves the room, this Object will automatically be destroyed on the network. + Only applicable to Shared Mode. + + + + + When enabled, State Authority over this Object can be taken by a player, without the current State Authority player first releasing authority. + If disabled, will fail if another player currently has State Authority, and has not called . + Only applicable to Shared Mode. + + + + + Specifies the component to used to determine this players position for Area Of Interest checks. + If left null, will use the first found root/child instance of on the Object. + + + + + Last tick this object received an update. Only available for . + + + + + Flags used for network object prefabs and similar + + + + + The GUID for this prefab or scene object, which is set at development time. Used to reference this as a spawnable object. + All spawned instances of this object will retain this GUID. Use for the unique ID of network entries. + + + + + + + + + + Array of initial child nested entities, that are children of this Object. + + + + + Array of all s associated with this network entity. + + + + + Array of all s associated with this network entity. + This does not include derived classes, as they are stored in . + + + + + The ID + Unity GameObject name for this entity. + + + + + Returns if this network entity existed as part of a scene, rather than having been dynamically spawned. + + + + + Returns if this network entity is associated with its , and that runner is not null. + + + + + If this is a predicted spawned object + + + + + If this is a predicted spawned object + + + + + If this object is inserted into the simulation + + + + + Returns the that has Input Authority over this network entity. + PlayerRefs are assigned in order from 0 to MaxPlayers-1 and are re-used as players join and leave. + The only caveat is that the server player (if one exists), always gets the last index no matter how many clients are connected. + + + + + Returns the that has State Authority over this network entity. + PlayerRefs are assigned in order from 0 to MaxPlayers-1 and are re-used as players join and leave. + The only caveat is that the server player (if one exists), always gets the last index no matter how many clients are connected. + + + + + The layers this object exists in for area of interest queries. This is an integer bitmask not a layer index. + + + + + Returns if is the designated Input Authority for this network entity. + + + + + Returns if is the designated State Authority for this network entity. + + + + + Returns if is neither the Input nor State Authority for this network entity. + + + + + Toggles if this NetworkObject is included in the , which will include the prefab in builds as a Spawnable object. + + + + + Gets a bitmask of flags, representing the current local authority over this . + + + + + Gets a bitmask of flags, representing the supplied RPC authority for this . + + + + + Sets which has Input Authority for this Object. + + + + + Removes input authority from whichever player has it for this object. Only valid when called on a Host or Server peer. + + + + + Add or remove specific player interest in this NetworkObject. + + must be set to . + + + + + + + + Copies the entire State from another + + NetworkObject to copy the State from + + + + Add or remove Player's interest in a specific named Interest Group for this . + + must be set to . + + + + + + A decoupled prefab reference. Internally stored as a GUID. + + + + + Network meta information for a . + + + + + The id of the root most , if this is nested. + + + + + Represents a base class for "prefab assets" - assets that point to a prefab and provide a way to load them. + + + + + Actual Unity prefab guid. If out of sync with the prefab's, it will be updated at first opportunity. + + + + + Placeholder kept after a prefab is removed or made not spawnable. If the prefab is restored/made spawnable again, + the type of this asset will change to . + + + + + Header data for this object + + + + + If we have a header or not + + + + + Data pointer to the first word of this objects data block + + + + + Adds prefab to the table and assigns it an id. + + + The id assigned to the prefab (if returns true) or id of the prefab with the same guid that has already been added. + True if the prefab prefab had unique guid and was added. + + + + Flag constants for input and state authority. + + + + + Flags a method as being a networked Remote Procedure Call. + Only usable in a NetworkBehaviour. + Calls to this method (from the indicated allowed ) will generate a network message, + which will execute the method remotely on the indicated . + The RPC method can include an empty argument, that will include meta information about the RPC on the receiving peer. + Example: + + | [Rpc(RpcSources.All, RpcTargets.All, InvokeLocal = false, InvokeResim = false, Channel = RpcChannel.Reliable, TickAligned = true)]

+ | public void RPC_Configure(NetworkObject no, string name, Color color, RpcInfo info = default) { } +
+ To target a specific Player, use the : + + | [Rpc] + | public void RpcFoo([RpcTarget] PlayerRef targetPlayer) {} + + Use as a return value to access meta information about the RPC send attempt, such as failure to send reasons, message size, etc. + + Non-static RPCs are only valid on a . + Static RPCs can be implemented on s, and do not require a instance. + Static RPC require the first argument to be NetworkRunner. + + Static RPC Example: + + | [Rpc] + | public static void RPC_Configure(NetworkRunner runner) { } + +
+
+ + + The legal types that can trigger this Rpc. Cast to int. + + + + + The types that will receive and invoke this method. Cast to int. + + + + + Indicates if the method should be called locally (on the RPC caller). This happens immediately. + + + + + Indicates if this RPC will execute during resims, or only once on the first execution of the associated tick. + + + + + Specifies which RpcChannel to use. + + + + + Indicates if this RPC's execution will be postponed until the local simulation catches up with the sender's Tick number. + Even if set to false, the order of Rpcs is always preserved. Rpcs are deferred until all preceding Rpcs have + executed. + + + + + Options for when the game is run in mode and RPC is invoked by the host. + + + + The legal types that can trigger this Rpc + The types that will receive and invoke this method + + + + Rpc order preserved, delivery verified, resend in case of a failed delivery. + + + + + Rpc order preserved, delivery not verified, no resend attempts. + + + + + Options for when the game is run in mode and RPC is invoked by the host. + + + + + If host invokes RPC will be set to (default). + + + + + If host invokes RPC will be set to the host's local player. + + + + + May be used as an optional return value. Contains meta data about the RPC send, such as failure to send reasons, culling, message size, etc. + Example: + + | [Rpc] + | public RpcInvokeInfo RpcFoo(int value) { + | return default; + | } + | + | public override void FixedUpdateNetwork() { + | var info = RpcFoo(); + | Debug.Log(info); + | } + + + + + + Results for the local RPC Invocation of the RPC method. + + + + + RPC has been invoked locally. + + + + + Not invoked locally because is false. + + + + + Not invoked locally because is false and simulation stage is + + + + + Not invoked because source current authority does not match flags set in + + + + + Not invoked because target player is local and this current authority does not match flags set in + + + + + Not invoked because target player is not local. + + + + + Results for the RPC message send operation. Note: Some individual targets may be culled even if the send operation succeeds. + Information about culled targets can be found in . + + + + + RPC has been sent. Check for details. + + + + + Send culled because is false. + + + + + Send culled because source current authority does not match flags set in + + + + + Send culled because there are no active connections. + + + + + Send culled because target player does not exist. + + + + + Send culled because target player is local and is false. + + + + + RPC send operation result information. + + + + + Result flags for the RPC send operation. + + + + + The size of the RPC message. + + + + + Collection of the targets which were sent this RPC message. + + + + + Collection of the targets which were failed to be sent this RPC message. + Reason for failure can be found in the flags. + + + + + RPC attribute used to indicate a specific target player for an RPC when sending from one player to another. + RPC is sent to the server, and then is forwarded to the specified player. + Usage: + + | [Rpc] + | public void RpcFoo([RpcTarget] PlayerRef targetPlayer) { } + + + + + + Companion component for . Exposes as UnityEvents, + which can be wired up to other components in the inspector. + + + + + + The core Fusion config file that is shared with all peers at startup. + + + + + Options for running one or multiple peers in one Unity instance. + Multiple is useful for testing multiple players/clients inside of the Unity editor without needing to build executables. + Each peer is assigned its own independent physics scene and instance. + + + + + This is the normal use case, where every build and the editor run a single server, host or client peer. + + + + + This is the optional use case, which allows running multiple peers in the Unity editor, or in a build. + + + + + Options for which Physics engines Fusion will use for simulations. + + + + + The Unity PhysX simulation is used, but auto-simulation is disabled and simulations are controlled by Fusion. + + + + + The Unity PhysX2D simulation is used, but auto-simulation is disabled and simulations are controlled by Fusion. + + + + + Fusion preforms no simulation calls. + + + + + Options for how physics is simulated on clients. + + - Clients do not predict remote objects, and render in their remote time frame (relative past).
+ - Clients predict remote objects and have options for rendering them in local and/or remote time frames. +
+
+
+ + + The server simulates and sends states to all clients. Clients do not attempt prediction and will render remote objects in the server (relative past) time frame. + + + + + The server simulates and sends states to all clients. Clients simulate remote states forward into the local time frame (prediction). + + + + + Spawn Modes while Scene is being loaded + + + + + Block all Spawns + + + + + Allow all Spawns + + + + + Queue spawn for when Scene is fully loaded + + + + + Delta Compression Implementations + + + + + Default Implementation + + + + + Burst Implementation + + + + + Debug Implementation + + + + + Default file name for the asset + + + + + Reference for the default + + + + + Current Type ID + + + + + Current version + + + + + Current version + + + + + Current Type ID + + + + + Setting for whether multiple peers can run per Unity instance (typically to allow easy testing of multiple peers inside of the editor). + + + + + Setting for which physics engine Fusion will simulate with. + + + + + Setting for which Fusion will use in server or host mode. Not used in shared mode. + + + + + Use lag compensation. Not used in shared mode. + + + + + Advanced lag compensation buffer settings. + + + + + Current + + + + + Current type + + + + + Signal if the callbacks should be invoked in Batch Mode + + + + + Max number of allowed + + + + + Signal if the of the should be included on the name of the + + + + + Enable the Support for Host Migration + + + + + Delay in seconds between each time the Host sends a Snapshot + of the current Simulation State to be used in the Host Migration + + + + + Reference for the current + + + + + Simulation Settings + + + + + Interpolation Settings + + + + + Network Settings + + + + + Settings for simulating network conditions of latency and loss. + + + + + Heap Settings + + + + + The named global accuracies used by this project. + + + + + Names of assemblies Fusion is going to weave. Not case sensetive. + + + + + Use Fusion.SerializableDictionary to store [Networked] dictionary properties initial value. If unchecked, + the weaver will emit System.Generic.Dictionary instead - a type that's not Unity-serializable, but custom + serializers (e.g. Odin) may support it. + + + + + If set, the weaver will add a check to all [Networked] properties on each + to verify if owing has been attached to. + + + + + Make a copy of the + + + + + ToString + + + + + Serialize the a NetworkProjectConfig into a JSON + + NetworkProjectConfig reference + JSON String + + + + Deserialize the NetworkProjectConfig based on the JSON Serialized version sent by the Room's Creator + + JSON with a serialized NetworkProjectConfig + NetworkProjectConfig reference deserialized + + + + Remove un-unecessary data from serialized version of + + + + + Get the version information for the Fusion.Runntime.dll. + + + + + Convert between to + + + + + Manages and references the current instance of + + + + + Get the version information for the Fusion.Runntime.dll. + + + + + Path to asset (created on demand if non-existent), which will serve as a host + for lightweight assets. They can be used to reference prefabs + alternatively to and regular references, + offering the best of two worlds: decoupling and Find References support. +
+ Suggested path: Assets/Photon/Fusion/User/NetworkPrefabAssetCollection.asset +
+
+ + + An auto-generated list containing source information (e.g. Resource path, address, static reference) for all the prefabs that can be spawned, i.e. the ones with + component and enabled. +
+ Additional prefabs can registered at runtime with . +
+
+ + + Represents a Server or Client Simulation. + + + Host Migration related code in order to get a copy of the Simulation State + + + All Scene related API and fields + + + + + Enumeration of Fusion.Runtime.dll options. + + + + + Use the Debug version of the Fusion.Runntime.dll. + + + + + Use the Debug version of the Fusion.Runntime.dll. + + + + + Initialization stages of Fusion + + + + + Runner is about to start + + + + + Runner is running + + + + + Runner is shutdown + + + + + Get Fusion.Runtime.dll build type. + + + + + Delegate type for on before spawned callback + + + + + Stores the Shutdown parameters used when requesting a Shutdown from inside a Runner Callback + + + + + Get enumerator for the collection of all s. + + + + + + Completion Source for the startup Photon Cloud Operations + + + + + Change the visibility of this NetworkRunner when running in Multiple Peer Mode + + + + + Indicates if this is collecting . + + + + + The current topology used + + + + + Returns the for this . + + + + + Returns the flags for The type of network peer the associated represents. + + + + + Returns the current stage of this . + + + + + Returns the fixed tick time interval. Derived from the . + + + + + The time the current State represents (the most recent FixedUpdateNetwork simulation). + Use as an equivalent to Unity's Time.fixedTime. + Time is relative to Tick 0 (which represents Time 0f). + + + + + The current time (current State.Time + Simulation.DeltaTime) for predicted objects (objects in the local time frame). + Use as an equivalent to Unity's Time.time. + Time is relative to Tick 0 (which represents Time 0f). + + + + + The current time (current State.Time + Simulation.DeltaTime) for non-predicted objects (objects in a remote time frame). + Use as an equivalent to Unity's Time.time. + Time is relative to Tick 0 (which represents Time 0f). + + + + + Returns if this is valid and running. + + + + + If the runner is shutdown + + + + + Are we dealing with a regular, planned shutdown. + + + + + If the runner is pending to start + + + + + Returns if this represents a Client connection. + + + + + Returns if this Client is currently connected to a Remote Server + + + + + Returns if this represents a Server connection. + + + + + Returns true if this runner represents a Client or Host. Dedicated servers have no local player and will return false. + + + + + Returns true if this runner was started as single player (Started as with = 1). + + + + + If this is the last tick that is being executed this update + + + + + If this is the first tick that executes this update or re-simulation + + + + + If this is not a re-simulation but a new forward tick + + + + + If we are currently executing a client side prediction re-simulation. + + + + + The current state of the runner, if it's Starting, Running, Shutdown + + + + + Returns a for the local simulation. For a dedicated server will equal false. + PlayerRefs are assigned in order from 0 to MaxPlayers-1 and are re-used as players join and leave. + The only caveat is that the server player (if one exists), always gets the last index no matter how many clients are connected. + + + + + Returns the reference. + + + + + Returns how many ticks we executed last update. + + + + + Returns the collection of objects for this NetworkRunner's . + + + + + Returns the global instance of a lag compensation buffer . + + + + + Disconnect a player from the server + + Player to disconnect + + + + Connect this as a client to a Server. + + + + + Initiates a . + + + + + Starts using the supplied arguments. + + + + + Pauses the game in single player + + + + + Continues a paused game in single player + + + + + Sets the paused state in a single player + + + + + Add or remove Player's interest in a specific named Interest Group for a specific . + must be set to . + + + + + Gets Player's Actor Number (ID). Only usable in shared mode. + + PlayerRef to get the Actor Number (ID) + Actor Number associated with the PlayerRef, otherwise null. + + + + Gets Player's UserID. Only usable in shared mode. + + PlayerRef to get the UserID + UserID if valid player found, otherwise null. + + + + Sets the network object associated with this player + + + + + + + Gets the network object associated with a specific player + + + Network object if one is associated with the player + + + + Returns the player round trip time (ping) in seconds + + The player you want the round trip time for + + + + Sends RPC message. Not meant to be used directly, ILWeaver calls this. + + + + + + Sends RPC message. Not meant to be used directly, ILWeaver calls this. + + + + + + + + + + + + + + + + + + + + + Returns array of all registered with this . + + + + + + + Register an instance for callbacks from this . + + + + + + Unregister an instance for callbacks from this . + + + + + + Send an arbitrary data buffer to a target Player + + Player that should receive the buffer + Buffer to be sent + + + + Send an arbitrary data buffer to the Server + + Buffer to be sent + + + + Flags this player as always interested in this object. Means it does not have to be in a players area of interest to be replicated. + + The player + The object + If he's always interested, or not. + + + + Adds a area of interest radius for a specific player at a position. A player can have several areas of interests added. Needs to be called every frame. + + The player + Center position of the area of interest + Radius of the area of interest + + + + Returns the data from player, converted to the indicated . + + + + + Returns the unconverted unsafe for the indicated player. + + + + + Outputs the from player, translated to the indicated . + + + + + Get the instance for this from a . + + + null if object cannot be found. + + + + Get the instance for this from a . + + + + True if object was found. + + + + Get the instance for this from a . + + + + True if object was found. + + + + Gets the for the current stage. For use in the .Render() callback. + + + Indicate if the start and end ticks should represent predicted, or state snapshots. + + + + + + Tries to return the first instance of T found on the root of a . + + + + Returns the found component. Null if the cannot be found, or if T cannot be found on the GameObject. + + + + Returns if the contains a reference to a in the current State . + + + + + Returns if the contains a with given in the current State . + + + + + Returns if the contains a reference to a in the specified State . + + + + + Attempts to network instantiate a using a Component type that is part of a + + Must be a Type derived from + used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Attempts to network instantiate a using a prefab. + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Prefab used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Attempts to network instantiate a using a . + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Prefab Ref used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Attempts to network instantiate a using a + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Prefab Asset used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Attempts to network instantiate a using a + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Object Guid used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Attempts to network instantiate a using a + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Prefab ID used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Destroys a . + + + + + + Ensures that a specific component exists on this gameobject. + + + + + Removes a specific from this gameobject, if it exists. + + + + + Register a instance with the . + + + + + Unregister a instance from the callbacks. + + + + + Attaches a user created network object to the network + + The object to attach + If assigned who is the default input authority for this object + + + + Attempts to network instantiate a using a + Note: position and rotation values are only used locally for the instantiation of the object, and are not inherently networked. + Use , or any of its derived classes such as to replicate the initial transform state. + + Prefab ID used to spawn the + Spawn Position + Spawn Rotation + Player Input Authority + reference + reference + reference, or null if it was not able to spawn the object + + + + Signal if Fusion was initilized with Host Migration system enabled + + + + + if this instance is a resume (host migration) + + + + + Iterate over the old NetworkObjects from the Resume Snapshot + + Iterable list of + + + + Get the next NetworkID used while resuming the simulation + + + + + + Gets a temporary representation of a from the old Server Snapshot + + + + + Setup Host Migration information + + + + + + Start the Host Migration process + + Base Snapshot to be used when restarting the Host + + + + Recurring Service method that will check if the Job responsible for computing + the Server Snapshot has finished the process and will push the result for later use + + + + + Start the Snapshot build using an async process + This will return a Task that can be awaited witht the result of the operation + + Await Task that will complete with the Snapshot computation + + + + Creates a Job to build the Server Simulation Snapshot + + Ready to use + + + + Signal the Host Migration process has started + + + + + Unity Job responsible for computing a Server Snapshot + + + + + Signal if the Local Peer is connected to Photon Cloud and is able to Create/Join Room but also receive Lobby Updates + + + + + Photon Client UserID + + Returns null if Peer is not connected to Photon Cloud + + + + used by this Runner to Authenticate the local peer. + + + + + Current Game Mode active on the Fusion Simulation + + + + + Stores information about the current running session + + + + + Check the current Connection Type with the Remote Server + + + + + Exposes the current NAT Type from the local Peer + + + + + Signal if the Local Peer is in a Room and is the Room Master Client + + + + + Completion Source for the startup Photon Cloud Operations + + + + + Responsible to manage the Photon Cloud related Services + + + + + Join the Peer to a specific Lobby, either a prebuild or a custom one + + Lobby Type to Join + Lobby ID + Authentication Values used to authenticate this peer + Custom Photon Application Settings + Async Task to Join a Session Lobby. Can be used to wait for the process to be finished. + + + + Starts the local Fusion Runner and takes care of all major setup necessary + + Custom arguments used to setup the Fusion Simulation + Task that can be awaited to chain actions + + + + Connect the local peer to Photon Cloud using an async process. + + Authentication Values used to authenticate this peer + Custom Photon Application Settings + External Communicator that will be reused on restart + Async Task of the connect to Photon Cloud process. Can be used to wait for the process to be finished. + + + + Disconnect the Peer from Photon Cloud + + If the Cloud Services were not initialized, it just returns immediately + + + + + + Start Fusion in Single Player Mode + + Initialization Arguments + A running Task of the initialization process + + + + Start Fusion in one of the Cloud Game Modes + + Initialization Arguments + A running Task of the initialization process + + + + Shutdown the Fusion Runner based on a arbitrary Exception + + Exception used as base for the Shutdown procedure + Awaitable Task of the Shutdown procedure + + + + Signal an update on the list of + + New Session Info + + + + Signal an update on the list of + + Custom Authentication Response + + + + Returns the current loaded network scene. + + + + + Returns the Unity scene for this . With , multiple physics scenes may exist, and this getter should be used in place of SceneManager.GetActiveScene(). + + + + + A scene used when run in mode. + + + + + Signal if this Runner is already in a Temp Physics Scene + + + + + Update the current Active Scene + + to be activated + + + + Invoke on all implementations + + + + + Invoke on all implementations + + + + + Get the a GameObject instance belongs to. + + GameObject to check for a + reference, or null if not found + + + + Get the from a specific Scene + + Scene to check for a + reference, or null if not found + + + + Try to create a temp empty scene for PhysicsScene + + false if the runner was on the temp scene already, true otherwise. + + + + Get the 3D Physics scene being used by this Runner. + + + + + Get the 2D Physics scene being used by this Runner. + + + + + Instantiates an object in the scene of this runner + + + + + Instantiates an object in the scene of this runner + + + + + Instantiates an object in the scene of this runner + + + + + Instantiates an object in the scene of this runner + + + + + Moves an object to the scene of this runner + + + Component of object to move + + + + Moves an object to the scene of this runner + + Object to move + + + + If running in peer mode and the current active + scene is different than , switches active scene to , + stores the previous active scene in and returns true. + Returns false otherwise. + + + True if an actual change occurred. + + + + Retrieve the associated with an specific Scene + + Scene handler to check + reference, or null if not found + + + + Transitory Holder with all necessary information to restart the Fusion Runner + after the Host Migration has completed + + + + + New GameMode the local peer will assume after the Host Migration + + + + + Fusion Start Arguments, used to configure the + simulation mode and other settings + + + + + Game Mode in which this peer will start + + + + + Photon Cloud Session Name used either to Create or Join a Session + + + + + Binding Address + + + + + Object pool to use + + + + + Custom reference + + + + + Custom used to start the simulation + + + + + Number of players allowed to connect to this peer, when running in Server Mode + + + + + Scene that will be set as the starting Scene when running in Server Mode + + + + + Callback that is invoked when the Fusion has fully started + + + + + Flag to disable the NAT Punchthrough implementation and connect only via Relay + + + + + User defined callback interfaces we will provide O(1) constant time lookup for + + + + + Connection token sent by client to server. Not used in shared mode. + + + + + Custom Session Properties. + This dictionary can be used to either setup the initial Session Properties when creating a Session + but also to set the matchmaking filters when joining a Random Session. + + + + + Session Custom Lobby to be published in + + + + + Specify a Custom STUN Server used to Resolve the peer Reflexive Addresses + + + + + Custom Authentication Data + + + + + Custom Photon Application Settings + + + + + Disables the Session creation when starting a Client with an specific Session Name + + + + + Host Migration Token used when restarting the Fusion Simulation + + + + + Callback invoked when the new Host is migrating from the old Host state + + + + + Fusion Game Mode. + + Used to select how the local simulation will act. + + + + + Single Player Mode: it works very similar to Mode, but don't accept any connections. + + + + + Shared Mode: starts a Game Client, which will connect to a Game Server running in the Photon Cloud using the Fusion Plugin. + + + + + Server Mode: starts a Dedicated Game Server with no local player. + + + + + Host Mode: starts a Game Server and allows a local player. + + + + + Client Mode: starts a Game Client, which will connect to a peer in either or Modes. + + + + + Automatically start as Host or Client. The first peer to connect to a room will be started as a Host, all others will connect as clients. + + + + + Defines the type of the current connection with the Remote Peer, + either the Server or a Client + + + + + No connection is currently active + + + + + Connection was accomplished using the Photon Relay Services + + + + + Connection was accomplished directly with the remote peer + + + + + Session Lobby Type + + + + + Invalid Session Lobby Type + + + + + ClientServer Lobby + + + + + Shared Lobby + + + + + Custom Lobby - works in conjuction with a Lobby Name/ID + + + + + Holds information about the Game Session + + + + + Flag to signal if the is ready for use + + + + + Stores the current Room Name + + + + + Stores the current connected Region + + + + + Signal if the current connected Room is visible + + + + + Signal if the current connected Room is open + + + + + Room Custom Properties + + + + + Current number of peers inside this Session, this includes the Server/Host and Clients + + + + + Max number of peer that can join this Session, this value always include an extra slot for the Server/Host + + + + + Check if the reference is not Null and is Valid. + + + + + + Update or change the Custom Properties of the current joined Room + + New custom properties + + + + String representation of a + + Formatted + + + + Represents the result of starting the Fusion Simulation + + + + + Signal if the Start was OK + + + + + Start Game Shutdown Reason + + + + + StartGameResult to String + + + + + + Describe an Exception that Occurred while starting the Fusion Simulation + + + + + ShutdownReason that caused this exception + + + + + StartGameException to String + + + + + Convert arbitrary Exceptions into a StartGameException to public use + + Exception to be converted + Reference to a StartGameException holding a ShutdownReason + + + + Stores data types used on the interface + + + + + Data holder of a Connection Request from a remote client + + + + + Address of the remote client + + + + + Accepts the Request + + + + + Refuses the Request + + + + + Describes a list of Reason why the Fusion Runner was Shutdown + + + + + OK Reason means Fusion was Shutdown by request + + + + + Shutdown was caused by some internal error + + + + + Raised when the peer tries to Join a Room with a mismatching type between ClientServer Mode and Shared Mode. + + + + + Raised when the local peer started as a Server and tried to join a Room that already has a Server peer. + + + + + Raised when the Peer is disconnected or kicked by a Plugin Logic. + + + + + Raised when the Game the Peer is trying to Join is Closed + + + + + Raised when the Game the Peer is trying to Join does not exist + + + + + Raised when all CCU available for the Photon Application are in use + + + + + Raised when the peer is trying to connect to an unavailable or non-existent Region + + + + + Raised when a Session with the same name was already created + + + + + Raised when a peer is trying to join a Room with already the max capacity of players + + + + + Raised when the Authentication Values are invalid + + + + + Raised when the Custom Authentication has failed for some other reason + + + + + Raised when the Authentication Ticket has expired + + + + + Timeout on the Connection with the Photon Cloud + + + + + Raised when Fusion is already running and the StartGame is invoked again + + + + + Raised when any of the StartGame arguments does not meet the requirements + + + + + Signal this Runner is shutting down because of a Host Migration is about to happen + + + + + Interface for callbacks. + Register a class/struct instance which implements this interface with . + + + + + Callback from a when a new player has joined. + + + + + Callback from a when a player has disconnected. + + + + + Callback from that polls for user inputs. + The that is supplied expects: + + input.Set(new CustomINetworkInput() { /* your values */ }); + + + + + + + + + + + + + Called when the runner is shutdown + + The runner being shutdown + Describes the reason Fusion was Shutdown + + + + Callback when successfully connects to a server or host. + + + + + Callback when disconnects from a server or host. + + + + + Callback when receives a Connection Request from a Remote Client + + Local NetworkRunner + Request information + Request Token + + + + Callback when fails to connect to a server or host. + + + + + This callback is invoked when a manually dispatched simulation message is received from a remote peer + + The runner this message is for + The message pointer + + + + This callback is invoked when a new List of Sessions is received from Photon Cloud + + The runner this object exists on + Updated list of Session + + + + Callback is invoked when the Authentication procedure returns a response from the Authentication Server + + The runner this object exists on + Custom Authentication Reply Values + + + + Callback is invoked when the Host Migration process has started + + The runner this object exists on + Migration Token that stores all necessary information to restart the Fusion Runner + + + + Class that contains global accuracy information. + Built-in named defaults can be edited, or custom named defaults can be created. + These defaults allow you to change accuracy settings across an entire project. + These values can be changed in the . + + + + + The named built-in default for Uncompressed. This cannot be changed and will always have a value of 0. + + + + + The named built-in general default. + + + + + The named built-in default for Position. + This value is used by built-in components like and , + but may be used for your own components as well. + + + + + The named built-in default for Rotation. + This value is used by built-in components like and , + but may be used for your own components as well. + + + + + The named built-in default for NormalizedTime. + This value is used by built-in components like , + but may be used for your own components as well. + + + + + The standard default fall back value for Accuracy. + + + + + The fixed value that represents uncompressed. + + + + + Which hash should a hash value of zero (the struct default value) remap to. + + + + + Which tag name should a zero hash (the struct default value) remap to. + + + + + Which accuracy value should a zero hash (the struct default value) remap to. + + + + + Attempts to get an accuracy from a hash value. + + Returns false if the hash is invalid (hash 0 is considered valid, and remaps to ) + + + + Main simulation class + + + + + Use inside of FixedUpdateNetwork to determine if the tick currently being simulated has previously been simulated locally. + Resimulation occurs in client prediction when new states arrive from the StateAuthority. + Networked objects are set to the most current authority state tick, and simulations are repeated from that tick to the local current tick. + + + + + Use in conjunction with IsResimulation/IsForward inside of FixedUpdateNetwork to determine if the current tick being simulated + is the last tick of the resimulation or forward phase of the simulation loop. + + 'Resimulation' describes simulating a tick that has been previously been simulated.

+ 'Forward' describes simulating a tick that is being simulated for the first time locally.

+ 'Prediction' describes simulating ticks higher than the most current known StateAuthority snapshot tick. +
+
+
+ + + Use in conjunction with IsResimulation/IsForward inside of FixedUpdateNetwork to determine if the current tick being simulated + is the first tick of the resimulation or forward phase of the simulation loop. + + 'Resimulation' describes simulating a tick that has been previously been simulated.

+ 'Forward' describes simulating a tick that is being simulated for the first time locally.

+ 'Prediction' describes simulating ticks higher than the most current known StateAuthority snapshot tick. +
+
+
+ + + Use inside of FixedUpdateNetwork to determine if the tick currently being simulated has NOT previously been simulated locally. + + + + + True if the current stage of the simulation loop is Forward. False during resimulations. + + 'Resimulation' describes simulating a tick that has been previously been simulated.

+ 'Forward' describes simulating a tick that is being simulated for the first time locally.

+ 'Prediction' describes simulating ticks higher than the most current known StateAuthority snapshot tick. +
+
+
+ + + The tick associated with the current state of networked objects. + + + + + Indicates if a Server/Client or Shared Mode (relay server) topology is being used. + + + + + Gets the flags for The type of network peer this simulation represents. + + + + + Gets the current value. + + + + + The file used by this . + + + + + The file used by this . + + + + + Gets the fixed tick time interval. Derived from the . + + + + + If this peer is a client. True for client peers in Server/Client topologies, and true for all peers in Shared Mode. + + + + + If this peer is the server. True for the Server or Host peer in Server/Client topologies, and always false for all peers in Shared Mode (the relay is the server). + + + + + True for any peer that represents a human player. This is true for all peers except a dedicated server. + + + + + Indicates that this simulation is operating in Single Player mode, which is a Host that accepts no connections. + + + + + Only valid in Shared Mode. Indicates if this peer is flagged as the MasterClient, which means it is default StateAuthority + + + + + Signal if the Simulation is currently running + + + + + Bound Address of the internal socket + + + + + Add or remove specific player interest in a NetworkObject. + + must be set to . + + + + + + Forwards the Simulation based on the Delta Time + + Delta Time used to forward the simulation + How many Ticks executed on this Update + + + + Engine sources for statistics. + + + + + telemetry available for the sockets layer. + + + + + as Flags. + + + + + telemetry available for a . + + + + + as Flags. + + + + + telemetry available for . + + + + + as Flags. + + + + + Returns the requested associated with this . + + + + + Returns the requested associated with this . + + + + + Result flags for the RPC message send operation. + + + + + Client sent to the server, server will send to the target client. + + + + + Server sent to a specific client (a targeted message). + + + + + Server attempted to send to all the clients and at least one succeeded. + + + + + Target object not confirmed on the client. + + + + + Target object not in client's interest. Likely due to being outside of player's AOI region, or needs to be explicitly set as always interested. + + + + + Target client not connected (a targeted message). + + + + + Server attempted to send to all the clients, but none was connected. + + + + + Server attempted to send to all the clients, but the target object is not confirmed/not in Object Interest for all target clients. + + + + + Project configuration settings specific to how the Simulation class behaves. + + + + + + + + + + Classic server and client model + + + + + Relay based shared world model + + + + + Enumerates the types of state replication available. + + + + + Server Authoritative, Delta Snapshots send the complete world state from every network tick. This produces the highest accuracy simulation replication, + but may not be suitable for higher object and player counts. + + + + + Server Authoritative, Eventual Consistency allows for partial/culled state transmission, which may be necessary for implementing Area of Interest, + high networked object counts, or high player counts. + + + + + + + + + + The update rate of the simulation in Hz (ticks per second). + + + + + The default number of players allowed to join a game instance. Can also be changed in code when starting Fusion. + + + + + Determines which mode will be used. + + + + + The topology used + + + + + If, in host mode, we should allow host migration if the current host leaves. + + + + + Selects if Object Interest should be used when running in mode. + When enabled, clients will only receive updates for objects which they have interest in. + How interest is determined for each is based on its mode setting. + + + + + Uses area of interest queries to cull execution of Fixed Update Network for objects outside of any players area of interest. + + + + + + + + + + + + + + + Returns the inverse of TickRate. The time (in seconds) between simulation ticks. + + + + + Returns ( * ). + + + + + This value times TickRate determines the network send rate for the server. + A value of 1 instructs the server to send state updates to clients every simulation tick. + Values greater than 1 will send a input/state updates every N ticks. + Higher values reduce data usage, in exchange for increased latency and reduced state sync accuracy. + + + + + This value times TickRate determines the network send rate for clients. + A value of 1 instructs clients to send input/state updates to the server every simulation tick. + Values greater than 1 will send an update every N ticks. + Higher values reduce data usage, in exchange for increased latency and reduced state sync accuracy. + + + + + Returns ( * ). This is the effective time interval (in seconds) between server tick updates to clients. + + + + + Returns ( * ). This is the effective time interval (in seconds) between client input/state updates from the client. + + + + + + + + + + Memory Heap Settings + + + + + Default size of each Heap Page + + + + + Default number of Heap Pages + + + + + Heap Global Size + + + + + Initializes and creates a new based on the Global Size + + + + + ToString + + + + + How much the delta time is modulated up/down to adjust interpolation time + when the client has ended up behind or ahead of the server. Common values + are 1% to 3%, the lower the tick rate the higher this should be. + + Default value is 1%. + + + + + How much the actual interpolation time is allowed to drift up/down from the calculated optimal time + before adjustments to the interpolation time is made. Defined in % of optimal interpolation time. + For example with a 25% value and an optimal interpolation time of 0.1 seconds, the actual interpolation time + is allowed to very between 0.75-0.125 before adjustments are made to it. + + Default value is 25%. + + + + + How much actual interpolation time is allowed to drift up/down from calculated optimal time + before the time calculations are reset and snapped to the optimal value. + Defined in % of optimal time. Common values are 200% to 500%. + + Default value is 200%. + + + + + The minimum multiplier that is used to compensate for jitter and uncertainty when calculating optimal interpolation offset. + This multiplier is used to give some extra interpolation offset from the absolutely optimal time. + + + + + Same as the Multiplier Min, just the max value instead. + + + + + Main network configuration class. + + + + + Flag for allowed Reliable Data transfer modes. + + + + + Allow Client to Server. + + + + + Allow Client to Client using Server as Proxy. + + + + + Size in Kilobytes of the underlying socket send buffer. + + + + + Size in Kilobytes of the underlying socket receive buffer. + + + + + Max number of connection attempts that a Client will run when trying to connect to a remote Server. + + + + + Interval in seconds between each connection attempt from a Client. + + + + + Default assumed RTT in seconds for new connections (before actual RTT has been determined). The real RTT is calculated over time once the connection is established. + + + + + Max allowed time in seconds that the local peer can run without receiving any update from a remote peer. + If a client does not receive any update from the server within this period, it will disconnect itself. + If a server does not receive any update from a remote client within this period, it will disconnect that particular client. + + + + + Interval in seconds between PING messages sent to a remote connection, in order to keep that connection alive. + + Currently unused. + + + + Default delay between connection changes status to Shutdown (disconnected/invalid), and it actually being released (freeing all references to that particular connection). + + + + + Max number of bytes that can be used by Fusion to fill up a UDP package. + + + + + Current mode. + + + + + Initializes and creates a copy of this . + + + + + + Convert this into a using the as reference. + + + + + Configuration for network conditions simulation (induced latency and loss). + + + + + If adverse network conditions are being simulated. + + + + + The pattern used to oscillate between and values. + + + + + The lowest packet delay value returned from the oscillator. + + + + + The highest packet delay value returned from the oscillator. + + + + + The period of the oscillator (the rate at which delay oscillates between and ). + + + + + The oscillates between 0 and 1. Values below this threshold are reduced to zero, resulting in a value equal to . + + + + + After the delay value from the oscillator is determined, random 0 to this value of additional seconds be added to the packet latency. + + + + + The pattern used to oscillate between and values. + + + + + The lowest loss chance value the oscillator will produce. 0 = 0% chance of being lost. 1 = 100% chance of being lost. + + + + + The highest loss chance value the oscillator will produce. 0 = 0% chance of being lost. 1 = 100% chance of being lost. + + + + + The wave oscillates between 0 and 1. Values below this threshold are reduced to zero, resulting in a value equal to . + + + + + The period of the oscillator (the rate at which delay oscillates between and ). + + + + + After the oscillation loss calculated, an additional 0 to this (normalized) percentage of loss chance is added. + + + + + Stores the global state of a simulation + + + + + Total size of the Global State in 4 byte words + + + + + Total size of the Global State + + + + + Current Scene Ref + + + + + Current Simulation Mode + + + + + Current player count + + + + + + + + + + Flags for The type of network peer a simulation represents. + + + + + Simulation represents a server peer, with no local player. + + + + + Simulation represents a server peer, with a local player. + + + + + Simulation represents a client peer, with a local player. + + + + + Represents a snapshot of the simulation state + + + + + The absolute tick index this snapshot represents. + + + + + The time in seconds this represents. + Time is relative to 0, with Tick 0 representing Time 0f. + + + + + Flags for which stage the simulation currently running. Forward is when a tick is being simulated for the first time. + Resimulate is when a tick is being simulated again with corrections. + + + + + Currently simulating a tick for the first time. + + + + + Currently simulating a previously simulated tick again, with state corrections. + + +
+
diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml.meta new file mode 100644 index 0000000..384f280 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Runtime.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d461b7cc07b37504f94b4a901b69e494 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll new file mode 100644 index 0000000..e573d8d Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug new file mode 100644 index 0000000..e856a6a Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug.meta new file mode 100644 index 0000000..ac4c382 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 02ee5c5911f692e44a0cec1e51da1507 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.meta new file mode 100644 index 0000000..3aea0d0 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 850f5b8096bb4c64abfef35065cde6b1 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug new file mode 100644 index 0000000..5ea90d7 Binary files /dev/null and b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug differ diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug.meta new file mode 100644 index 0000000..d85a2db --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.pdb.debug.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4da11c4b9bd14f146984f58dd9a83d27 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml new file mode 100644 index 0000000..b4f95fa --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml @@ -0,0 +1,1202 @@ + + + + Fusion.Sockets + + + + + Represents a Network Address, which includes a IP and Port + This can contains either a IPv4 or a IPv6 address + + + + + Retrieves the Remote Actor ID which this Represents + + + + + Signal if the is a Relayed connection + + + + + Signal if the represents an IPv6 Address + + + + + Signal if this is not default/empty + + + + + Create a new NetAddress on the LocalHost address with the desired Port + + Port used to build the NetAddress + New NetAddress reference + + + + Create a new on the LocalHost IPv6 Address with the desired Port + + Port used to build the NetAddress + New NetAddress reference + + + + Create a new NetAddress using the "Any" IPv4 Address representation (0.0.0.0) + with the Port passed as argument + + Port used to build the NetAddress + New NetAddress reference + + + + Create a new NetAddress using the "Any" IPv6 Address representation (::) + with the Port passed as argument + + Port used to build the NetAddress + New NetAddress reference + + + + Create a new NetAddress based on the IP and Port passed as argument + + String representation of an IP, either IPv4 or IPv6 + Port used to build the NetAddress + New NetAddress reference + + + + IPv4 Subnet Mask Utilities + + + + + List of IPv4 Subnet Masks + + + + + Check if two IPv4 seems to be in the same Subnet. + + The addresses are checked against all subnet masks in . + + EndPoint A + EndPoint B + True if both addresses seems to be in the same subnet + + + + Extracts the Network Address of an IPv4 EndPoint based on a particular Subnet Mask + + EndPoint + Subnet Mask + Network Address based on the Subnet Mask + + + + Build and alloc a based on the number of packets + it need to keep track and the size of each packets. + + Number of blocks to allocate + Size of each block + Reference to a + + + + Release the that is part of this Block and + mark it as available again by including it on the list of buffers + + + + + + Tries to get a valid reference from the Buffer block + + A valid NetBitBuffer reference if it can be retrieved from this block, null otherwise + + + + Creates a new empty + + NetBitBuffer reference + + + + Tries to get a valid reference from the Buffer block + + Reference to a NetBitBuffer + True if a valid NetBitBuffer reference can be retrieved from this block, false otherwise + + + + Represents a linked list of + + + + + Add a at the beginning of the List + + NetBitBuffer to add to the list + + + + Add a at the end of the list. + + NetBitBuffer to add to the list + + + + Removes the first element of the list + + NetBitBuffer reference + + + + Remove a specific from the list + + NetBitBuffer to remove + + + + Check if a specific is in the list + + NetBitBuffer to check + True if the list contains the item, false otherwise + + + + Describe the Type of a Command Packet + + + + + Network Command Header + Describe its type and usual settings for all commands + + + + + Create a new NetCommandHeader based on a type + + Type of Command that should be created + New NetCommandHeader reference based on the Command Type + + + + Connect Command used to signal a remote server that a client is trying to connect to it + + + + + Accepted Command, sent by the server when a remote client connection is accepted + + + + + Refuse Command, sent by the server when the connection was refused. + This happens when the server has reached its max connection capacity. + + + + + Disconnect Command, it can be used by either side of the connection + + + + + General configuration used to drive the behavior of the Socket library + + + + + Pre-allocated number of data buffers used to send data + + + + + Number of Connection Groups supported by the local instance + + + + + Max Number of Connections supported by the local instance + + + + + Max number of Connection per Group based on the and + + + + + Size of the internal Socket send buffer + + + + + + Size of the internal Socket receive buffer + + + + + + default Maximum Transmission Unit + + + + + UDP Packet Size in Bytes + + + + + UDP Packet Size in Bits based on + + + + + Number of Connection Attempts tried by the peer before cancel the connection + + + + + Interval in Seconds between attempts to connect to a remote server + + + + + Max Allowed time for the Send and Receive operations, in milliseconds + + + + + Initial RTT + + + + + Connection Timeout in seconds + + + + + Interval in Seconds between ping being sent to a remote end + + + + + Timeout in Seconds to allow a disconnected Connection to be released from the Group Mapping + + + + + + + + + Network Address used to bind the internal Socket + + + + + Package acknowledgment system configuration + + + + + Network simulation system configuration + + + + + Builds a with the default values + + + + + Disconnect Reason Flag + + + + + Describe the type of a Networked Packet + + + + + IPv6 header: 40 bytes, UDP header: 8 bytes, Realtime Header: 96 bytes + + + + + MinimumTransferUnit bytes. + + + + + MinimumTransferUnit bits. + + + + + MaximumTransferUnit bytes. + + + + + MaximumTransferUnit bits. (ipv6 header: 40 bytes, udp header: 8 bytes) + + + + + if from is LARGER than to, result is positive + if from is LESSER than to, result is negative + if they are the same, result is zero + + + + + + + + Random ID of this socket + + + + + Reference to Current Communicator + + + + + Local Peer Address is based on the current Player Actor Number inside the Room + + + + + Specifies UDP network type. + + + + + Invalid NAT Type + + + + + UDP is always blocked. + + + + + No NAT, public IP, no firewall. + + + + + A full cone NAT is one where all requests from the same internal IP address and port are + mapped to the same external IP address and port. Furthermore, any external host can send + a packet to the internal host, by sending a packet to the mapped external address. + + + + + A symmetric NAT is one where all requests from the same internal IP address and port, + to a specific destination IP address and port, are mapped to the same external IP address and + port. If the same host sends a packet with the same source address and port, but to + a different destination, a different mapping is used. Furthermore, only the external host that + receives a packet can send a UDP packet back to the internal host. + + + + + This class implements STUN Client. Defined in RFC 8489 + + Session Traversal Utilities for NAT (STUN) + Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT(STUN) + Happy Eyeballs Version 2: Better Connectivity Using Concurrency + State of Peer-to-Peer (P2P) Communication across Network Address Translators(NATs) + + + + + + + + + + List of public DNS Servers + + + + + Run a STUN Binding Request against the Public STUN Server in order to discover peer reflexive addresses + + Network Peer reference + Network Socket reference + Running Task of the STUN Query Procedure + + + + Retrieve the Local IP Endpoint currently active + + + + + + This class implements STUN ERROR-CODE. Defined in RFC RFC 5389 15.6 + + + + + Gets or sets error code. + + + + + Gets reason text. + + + + + Default constructor. + + Error code. + Reason text. + + + + Implements STUN message. Defined in RFC 3489. + + + + + STUN Message Type + + + + + STUN Attribute Type + + + + + Global Stun Related defined values + + + + + IP Address Family + + + + + STUN Message Type + + + + + STUN Transaction ID + + + + + Gets transaction ID. + + + + + Gets or sets IP end point what was actually connected to STUN server. Returns null if not specified. + + + + + Gets or sets user name. Value null means not specified. + + + + + Gets or sets error info. Returns null if not specified. + + + + + Default constructor. + + + + + Parses STUN message from raw data packet. + + Raw STUN message. + + + + Converts this to raw STUN packet. + + Returns raw STUN packet. + + + + Parses attribute from data. + + SIP message data. + Offset in data. + + + + Pasrses IP endpoint attribute. + + STUN message data. + Offset in data. + Returns parsed IP end point. + + + + Stores ip end point attribute to buffer. + + Attribute type. + IP end point. + Buffer where to store. + Offset in buffer. + + + + This class holds the result of a STUN Query + + + + + Current NAT Type of the peer + + + + + Signal if Result is valid + + + + + Gets public IP end point. + + + + + Gets private IP end point. + + + + + Invalid StunResult Reference + + + + + Default constructor. + + Specifies UDP network type. + Public IP end point. + + + + List of public STUN Servers + + + + + + + + + + + + + Total size in BITS of the buffer + + + + + Current read/write position in BITS inside the Buffer + + + + + Size of written buffer in BYTES + Ammount of bytes required considering the total of written bytes + + + + + Total Size in BYTES of the Buffer + + + + + Signal if the buffer was completely written + + + + + Signal if the buffer is overflowing + + + + + Signal if the Buffer is in Write Mode + + + + + Signal if the Buffer is in Read Mode + + + + + Internal Byte Array + + + + + Handles Protocol Msgs sent by the Fusion Plugin + + It converts the byte buffer into usable Protocol Msgs + + Sender Actor of the Protocol Msg, generally the Plugin + Object that stores the buffer to be converted + + + + Convert the Data object into a usable Byte Buffer. + How the conversion happens depends on the the Type of Communicator + + + + + Base Protocol Message. + + This concentrates the basics for serialization and cloning + + + + + Max Lenght of the Custom Data + + + + + Stores the Current Protocol Message version + + + + + Stores the Current Fusion Serialization Version + + + + + Represents an Invalid Version + + + + + Custom data send along side any Protocol Message + + + + + Creates a copy of this Message + + + + + + Created a new Message with a certain version + + Protocol Message Version + + + + Serialize this Message into or from a . + + Buffer to read from or write into the data of the Message + + + + Used by the specialized versions of Message to serialize its data + + Buffer to read from or write into the data of the Message + + + + Check if this Message is compatible with target versions + + Target Protocol Message Version + Target Fusion Serialization Version + Enable the Serialization Version Check + True if message is compatible with versions + + + + Disconnect Protocol Message. + + Used to signal a peer that it will be disconnected from Photon Cloud + + + + + Disconnect Reason + + + + + Disconnect Protocol Message + + + + + Disconnect Protocol Message + + Disconnect Reason + + + + List all Disconnect reason used by the Plugin to remove an Actor from the Room + + + + + Abstract disconnect reason + + + + + Used when an event with other code other then the treated ones is received by the plugin + + + + + When the Join Message is not of the Request Type + + + + + When the Join Message does not contain a valid Game Mode + + + + + When any of the major settings of a message does not align with the current settings, + like GameMode, Protocol Version, Serialization Version and Peer Mode + + + + + When there is already a Server running on the current Room + + + + + Local Peer Mode + + + + + message Type + + + + + Sent by Peer to Request to Join on the Plugin + + + + + Sent by the Plugin to confirm the Join of a Peer + + + + + Type of Peer which the Peer is starting as + + + + + No Mode Selected, means Invalid + + + + + Server Mode + + + + + Client Mode + + + + + Join Requests sent by the Plugin to request data from the Peer + + + + + No request in the Join Message + + + + + Request the Network Config + + + + + Request for Reflexive Information + + + + + Request to Disable NAT Punch + + + + + Join Message + + It is used to join a Fusion Room Session with extra information about the Peer. + This is unrelated to the Join Operation into a Photon Room. + + + + + Join Message Type + + + + + Requested Plugin Game Mode + + + + + Local Peer Mode + + + + + Requests sent from Plugin + + + + + Network Config Msg Type + + + + + Request Network Config + + + + + Response to a Request + + + + + Override Signal for the Network Config + + + + + NetworkConfig Protocol Msgs + It is used to serialize the Fusion NetworkConfig and send to Photon Cloud Plugin + + + + + Network Config Type + + + + + JSON Serialized NetworkConfig + + + + + Reflexive Info Msgs + + Used to transport information about the Reflexive Addresses of a Peer + + + + + Actor ID to which this info is related + + + + + Peer Public Address + + + + + Peer Private Address + + + + + Peer NAT Type + + + + + Signal if this Reflexive Info is Valid or not + + + + + Snapshot Message Type + + + + + Invalid/Empty Type + + + + + Base Snapshot + + + + + Confirmation sent by the Plugin + + + + + State Snapshot Protocol Msgs + + Used to sync the current Server Game State with the Photon Cloud Plugin + in order to perform an eventual Host Migration + + + + + Tick to which this Snapshot represents + + + + + Last NetworkID from the Server + + + + + Snapshot Type + + + + + Snapshot Total number of bytes stored or expected to be stored on the Snapshot + + + + + Check if the Snapshot has a valid Data based on the expected CRC + + True if the Snapshot has a valid data + + + + Get Snapshot internal Data Buffer + + Internal Data Buffer + + + + CRC Hash based on the content of the internal data or the expected CRC after all fragments are computed + + + + + Actual Snapshot data + + + + + Snapshot Total Size + + + + + Computes the CRC64 of the current Buffer Data stored on the Snapshot + + + + + Merge the received Snapshot data on this own data + + Snapshot to be merged + + + + Create a clone with this Snapshot and reset reference + + + + + + Start Message Requests + + + + + No Requests + + + + + Peer should connect to Shared Server + + + + + Peer should wait for the Server Reflexive Info + + + + + Start Protocol Msgs + Used to signal that Fusion Simulation should start + + + + + Actor ID of the Remote Server + + + + + Start Requests + + + + + Defines the Mode the Plugin should run + + + + + No Game Mode set + + + + + Client Server Game Mode + + The Plugin will act just as a relay, exchanging data between the peers. + + + + + Shared Game Mode + + The Plugin will act as a Fusion Server and will accept remote connections + + + + + Photon Event Codes used by the Fusion to communicate with the Photon Cloud + + + + + Protocol Event Code + + + + + Data Event Code + + + + + Zero (0) means: if it should be the room itself (authorative event). + + + + + + + Default CustomData Key of Realtime Events + + + + + + + Protocol Messages Serializer + + + + + Serialize a Protocol Message into a BitStream + + Protocol Message to be serialized + BitStream containing the Protocol Message + True if the Protocol Message was serialized + + + + Deserialize a Protocol Message from a BitStream + + Stream containing a Protocol Message + Deserialized Protocol Message + True if a Protocol Message was deserialized + + + + Invalid Version + + + + + Initial Version + + + + + Added Support to Fusion Serialization Version + + + + + Added Custom Data to all Protocol Messages + + + + + Added NAT Type to Reflexive Info + + + + + Added Host Migration Support + + + + + Always points to the Latest version + + + + diff --git a/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml.meta b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml.meta new file mode 100644 index 0000000..ec1d311 --- /dev/null +++ b/Assets/Photon/Fusion/Assemblies/Fusion.Sockets.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 43a4edfa145535c4d80df040678f3746 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor.meta b/Assets/Photon/Fusion/Editor.meta new file mode 100644 index 0000000..ab19196 --- /dev/null +++ b/Assets/Photon/Fusion/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 16e8492301418144dac7bf2bafdaa982 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources.meta b/Assets/Photon/Fusion/Editor/Resources.meta new file mode 100644 index 0000000..8ab87e8 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db14742ab878fc64f817a77ca9d3a69d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml b/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml new file mode 100644 index 0000000..4d23714 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml @@ -0,0 +1,647 @@ + + + + Assembly-CSharp + + + + + Prototyping component for spawning Player avatars. + + + + + Which section of the Fusion engine is being monitored. In combination with StatId, this selects the stat being monitored. + + + + + The specific stat being monitored. + + + + + Available if AllowClientObjects is enabled in the NetworkProjectConfig, this allows players to be spawned with client StateAuthority. + + + + + The aspect ratio this element should maintain. 2 = twice as wide as tall. + + + + + If true, all messages will be prefixed with [Fusion] tag + + + + + If true, some parts of messages will be enclosed with <color> tags. + + + + + Color of the global prefix (see ). + + + + + + + + + + + + + + + Converts object to a color . By default works only for and uses and fields. + + + + + Collect the bounds of the indicated types (MeshRenderer and/or Collider) on the object and all of its children, and returns bounds that are a sum of all of those. + + GameObject to start search from. + The types of bounds to factor in. + Whether to search all children for bounds. + + + + + Sets the default teleport interpolation velocity to be the CC's current velocity. + For more details on how this field is used, see . + + + + + Sets the default teleport interpolation angular velocity to be the CC's rotation speed on the Z axis. + For more details on how this field is used, see . + + + + + Basic implementation of a jump impulse (immediately integrates a vertical component to Velocity). + Jump even if not in a grounded state. + Optional field to override the jump impulse. If null, is used. + + + + + Basic implementation of a character controller's movement function based on an intended direction. + Intended movement direction, subject to movement query, acceleration and max speed values. + + + + + Companion component for , which automatically faces this GameObject toward the supplied Camera. If Camera == null, will face towards Camera.main. + + + + + Force a particular camera to billboard this object toward. Leave null to use Camera.main. + + + + + Flag interface to identify GameObjects that should be used as markers for spawn points. + Used by to locate spawn points in a scene. + + + + + Values greater than 0 will limit the meter to a range of 0 to MeterMax. + Value of 0 will adjust the max to the largest value occurance. + + + + + Exposes the UI labels and controls of , so they may be modified if customizing this graph. + + + + + A simple example of Fusion input collection. This component should be on the same GameObject as the . + + + + + Example definition of an INetworkStruct. + + + + + Derive from this class for different types. + Derived manager will only find that spawn point type, allowing for separate handling of player spawn points from other spawn-able items such as AI. + + + + + + How spawn points will be selected from the collection. + + + + + LayerMask for which physics layers should be used for blocked spawn point checks. + + + + + The search radius used for detecting if a spawn point is blocked by an object. + + + + + Serialized collection of all of the type T found in the same scene as this component. + + + + + Find all instances in the same scene as this spawner. + This should only be done at development time if using the Photon relay for any spawn logic. + + + + + Select the next spawn point using the defined . Override this method to expand on this, such as detecting if a spawn point is blocked. + + + + + Handling for if all spawn points are blocked. + + + + + + Cycles through all remaining spawn points searching for unblocked. Will return null if all points return == true. + + The index of the first tried SpawnPoints[] element, which was blocked. + ( index, ) + + + + Override this method with any logic for checking if a spawn point is blocked. + + + + + + + Interface for behaviours. + + + + + Flag component to identify GameObjects that should be used as markers for spawn points. + + + + + Prototyping component for Fusion. Updates the Player's AOI every tick to be a radius around this object. + + + + + Radius around this GameObject that defines the Area Of Interest for the InputAuthority of the object. + The InputAuthority player of this , + will receive updates for any other within this radius. + + + + + Creates and controls a Canvas with one or multiple telemetry graphs. Can be created as a scene object or prefab, + or be created at runtime using the methods. If created as the child of a + then will automatically be set to true. + + + + + Options for displaying stats as screen overlays or world GameObjects. + + + + + Predefined layout default options. + + + + + Creates a new GameObject with a component, attaches it to any supplied parent, and generates Canvas/Graphs. + + + Generated FusionStats component and GameObject will be added as a child of this transform. + Uses a predefined position. + The network stats to be enabled. If left null, default statistics will be used. + The simulation stats to be enabled. If left null, default statistics will be used. + + + + + The gets the default SimStats. Some are only intended for Fusion internal development and aren't useful to users. + + + + + Interval (in seconds) between Graph redraws. Higher values (longer intervals) reduce CPU overhead, draw calls and garbage collection. + + + + + Selects between displaying Canvas as screen overlay, or a world GameObject. + + + + + Selects between displaying Canvas as screen overlay, or a world GameObject. + + + + + Enables text labels for the control buttons. + + + + + Enables text labels for the control buttons. + + + + + Height of button region at top of the stats panel. Values less than or equal to 0 hide the buttons, and reduce the header size. + + + + + Height of button region at top of the stats panel. Values less than or equal to 0 hide the buttons, and reduce the header size. + + + + + The size of the canvas when is set to . + + + + + The distance on the Z axis the canvas will be positioned. Allows moving the canvas in front of or behind the parent GameObject. + + + + + The Rect which defines the position of the stats canvas on a GameObject. Sizes are normalized percentages.(ranges of 0f-1f). + + + + + The Rect which defines the position of the stats canvas overlay on the screen. Sizes are normalized percentages.(ranges of 0f-1f). + + + + + value which all child components will use if their value is set to Auto. + + + + + UI Text on FusionGraphs can only overlay the bar graph if the canvas is perfectly facing the camera. + Any other angles will result in ZBuffer fighting between the text and the graph bar shader. + For uses where perfect camera billboarding is not possible (such as VR), this toggle prevents FusionGraph layouts being used where text and graphs overlap. + Normally leave this unchecked, unless you are experiencing corrupted text rendering. + + + + + Disables the bar graph in , and uses a text only layout. + Enable this if is not rendering correctly in VR. + + + + + Force graphs layout to use X number of columns. + + + + + If is set to zero, then columns will automatically be added as needed to limit graphs to this width or less. + + + + + If is set to zero, then columns will automatically be added as needed to limit graphs to this width or less. + + + + + Enables/Disables all NetworkObject related elements. + + + + + The source for any specific telemetry. + + + + + Height of Object title region at top of the stats panel. + + + + + Height of Object info region at top of the stats panel. + + + + + Height of Object info region at top of the stats panel. + + + + + The currently associated with this component and graphs. + + + + + Initializes a for all available stats, even if not initially included. + If disabled, graphs added after initialization will be added to the bottom of the interface stack. + + + + + When is null and no exists in the current scene, FusionStats will continuously attempt to find and connect to an active which matches these indicated modes. + + + + + Selects which NetworkObject stats should be displayed. + + + + + Selects which NetConnection stats should be displayed. + + + + + Selects which Simulation stats should be displayed. + + + + + Automatically destroys this GameObject if the associated runner is null or inactive. + Otherwise attempts will continuously be made to find an new active runner which is running in specified by , and connect to that. + + + + + Only one instance with the can exist. Will destroy any clones on Awake. + + + + + Identifier used to enforce single instances of when running in Multi-Peer mode. + When is enabled, only one instance of with this GUID will be active at any time, + regardless of the total number of peers running. + + + + + The color used for the telemetry graph data. + + + + + The color used for the telemetry graph data. + + + + + The color used for the telemetry graph data. + + + + + The color used for the telemetry graph data. + + + + + Companion component for . Automatically added as needed for rendering in-game networking IMGUI. + + + + + When enabled, the in-game user interface buttons can be activated with the keys H (Host), S (Server) and C (Client). + + + + + The GUISkin to use as the base for the scalable in-game UI. + + + + + A Fusion prototyping class for starting up basic networking. Add this component to your startup scene, and supply a . + Can be set to automatically startup the network, display an in-game menu, or allow simplified start calls like . + + + + + Selection for how will behave at startup. + + + + + The current stage of connection or shutdown. + + + + + Supply a Prefab or a scene object which has the component on it, + as well as any runner dependent components which implement , + such as or your own custom INetworkInput implementations. + + + + + Select how network startup will be triggered. Automatically, by in-game menu selection, or exclusively by script. + + + + + When is set to , this option selects if the + will be started as a dedicated server, or as a host (which is a server with a local player). + + + + + will not render GUI elements while == . + + + + + The number of client instances that will be created if running in Mulit-Peer Mode. + When using the Select start mode, this number will be the default value for the additional clients option box. + + + + + The port that server/host will use. + + + + + The default room name to use when connecting to photon cloud. + + + + + Will automatically enable once peers have finished connecting. + + + + + The Scene that will be loaded after network shutdown completes (all peers have disconnected). + If this field is null or invalid, will be set to the current scene when runs Awake(). + + + + + Indicates which step of the startup process is currently in. + + + + + The index number used for the last created peer. + + + + + The server mode that was used for initial startup. Used to inform UI which client modes should be available. + + + + + Start a single player instance. + + + + + Start a server instance. + + + + + Start a host instance. This is a server instance, with a local player. + + + + + Start a client instance. + + + + + Start a Fusion server instance, and the number of client instances indicated by . + InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + + + + + Start a Fusion host instance, and the number of client instances indicated by . + InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + + + + + Start a Fusion server instance, and the indicated number of client instances. + InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + + + + + Start a Fusion host instance (server with local player), and the indicated number of additional client instances. + InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + + + + + Start a Fusion host instance (server with local player), and the indicated number of additional client instances. + InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + + + + + Start as Room on the Photon cloud, and connects as one or more clients. + + + + + + In-Game IMGUI style used for the interface. + + + + + Get the custom scalable skin, already resized to the current screen. Provides the height, width, padding and margin used. + + + + + + Modifies a skin to make it scale with screen height. + + + Returns (height, width, padding, top-margin, left-box-margin) values applied to the GuiSkin + + + + Individual graph components generated and used by . + + + + + Select between automatic formating (based on size and aspect ratio of the graph) and manual selection of the various graph layouts available. + + + + + Controls if the graph shader will render. Currently the graph shader only works in Overlay mode, so if forced to show Always here, it will not render correctly in 3d space. + + + + + Padding added to text and other layout objects. + + + + + The graph shaded area (which is only visible in Overlay mode) will expand to the full extends of the component when this is enabled, regardless of size. + When false, the graph will automatically expand only as needed. + + + + + Exposes the UI labels and controls of , so they may be modified if customizing this graph. + + + + + Returns true if the graph rendered. False if the size was too small and the graph was hidden. + + + + + + Creates a new GameObject with and attaches it to the specified parent. + + + + + Generates the Graph UI for this . + + + + diff --git a/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml.meta b/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml.meta new file mode 100644 index 0000000..a831bc7 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/Assembly-CSharp.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 98ca5aa6754ce124689e0c452916e9d4 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics.meta b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics.meta new file mode 100644 index 0000000..a2adf5a --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1dca4e484cd4200468b640fdf0fe98df +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts.meta b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts.meta new file mode 100644 index 0000000..c2f1a80 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5ccd8b3061f54ad4fba6f9abc1a63ec6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt new file mode 100644 index 0000000..6485417 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt.meta b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt.meta new file mode 100644 index 0000000..ebf1b08 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/OFL.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9ab706fba9f8d8a4e8ecbd1b18087710 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf new file mode 100644 index 0000000..7601ddd Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf differ diff --git a/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf.meta b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf.meta new file mode 100644 index 0000000..33307e1 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/ComponentHeaderGraphics/Fonts/Oswald-Header.ttf.meta @@ -0,0 +1,22 @@ +fileFormatVersion: 2 +guid: bc4170141f0283e4c80ff0f668490e67 +TrueTypeFontImporter: + externalObjects: {} + serializedVersion: 4 + fontSize: 17 + forceTextureCase: -2 + characterSpacing: 0 + characterPadding: 1 + includeFontData: 1 + fontNames: + - Oswald + fallbackFontReferences: + - {fileID: 12800000, guid: a30f933bed2f6504bae572c0ef6aaeb7, type: 3} + customCharacters: + fontRenderingMode: 0 + ascentCalculationMode: 1 + useLegacyBoundsCalculation: 0 + shouldRoundAdvanceValue: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons.meta b/Assets/Photon/Fusion/Editor/Resources/icons.meta new file mode 100644 index 0000000..fa74251 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: caefff3f1d2f12f4d9356c622cdafb29 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png new file mode 100644 index 0000000..c07250f Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png.meta new file mode 100644 index 0000000..b686e33 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/correct-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 8958b450dee843f41a091a734ed43ccd +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png new file mode 100644 index 0000000..30200c1 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png.meta new file mode 100644 index 0000000..10975f1 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/input-disabled-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 9c44338dd0d7004459e02469c0074de2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png new file mode 100644 index 0000000..5c3d12a Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png.meta new file mode 100644 index 0000000..a150701 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/input-enabled-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 5a71cb696f8dd5042bc4c110df660dea +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 2 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png new file mode 100644 index 0000000..6d8dd58 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png.meta new file mode 100644 index 0000000..fe92167 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/networked-property-icon.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: 9d1a679741319604a85979784032907e +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png new file mode 100644 index 0000000..b0b6eb2 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png.meta new file mode 100644 index 0000000..d95e5b8 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/visible-disabled-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: d09bc19b7d97b4b43a1e646890703e08 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png b/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png new file mode 100644 index 0000000..73129f1 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png.meta new file mode 100644 index 0000000..4259314 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons/visible-enabled-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 536acaec6131e754883bd7ffae7dc885 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome.meta new file mode 100644 index 0000000..7e67b25 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 453785a293d1a574fa05852324ed5ac7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt new file mode 100644 index 0000000..55acb02 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt @@ -0,0 +1 @@ +Icons by Fat Cow Hosting (http://www.fatcow.com/free-icons). Licensed under a Creative Commons Attribution 3.0 License. \ No newline at end of file diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt.meta new file mode 100644 index 0000000..87545b4 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/ATTRIBUTION.txt.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 28883dc040320bb4ea69a449132b35d7 +TextScriptImporter: + userData: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png new file mode 100644 index 0000000..563c364 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png.meta new file mode 100644 index 0000000..d4e71f9 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bugtracker.png.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 1d949f6bc5812234bb28b6bd51e902f5 +timeCreated: 1500450410 +licenseType: Pro +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png new file mode 100644 index 0000000..d448caa Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png.meta new file mode 100644 index 0000000..ee8941f --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_black.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: ca583a4b5a2b2452c80aa11e55f19346 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 6 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png new file mode 100644 index 0000000..060af25 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png.meta new file mode 100644 index 0000000..0d3cb0b --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_down.png.meta @@ -0,0 +1,86 @@ +fileFormatVersion: 2 +guid: 06632fbfd33334c72a288bdbdf2922ea +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 6 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png new file mode 100644 index 0000000..f74a914 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png.meta new file mode 100644 index 0000000..f2e6b51 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/bullet_green.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: e12890dcb5a25439e8a36389cf70fce1 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 6 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png new file mode 100644 index 0000000..61ae64b Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png.meta new file mode 100644 index 0000000..9044c3f --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/comments.png.meta @@ -0,0 +1,76 @@ +fileFormatVersion: 2 +guid: 0d548c93fd2fd490db38298610780b39 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png new file mode 100644 index 0000000..e67fe63 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png.meta new file mode 100644 index 0000000..2ce92a5 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/community.png.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: fd7da1b41e02e2546a5b59597cbbedfd +timeCreated: 1500450532 +licenseType: Pro +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png new file mode 100644 index 0000000..1086fbe Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png.meta new file mode 100644 index 0000000..32cbd42 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/documentation.png.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: 7d6ce7da11bf9aa4f98abcc9eb237a08 +timeCreated: 1500450305 +licenseType: Pro +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png new file mode 100644 index 0000000..93c67f2 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png.meta new file mode 100644 index 0000000..3cbe4b6 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/information.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: fe64035b49c4e4a078ee42b857ae1519 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 6 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: 1 + mipBias: -100 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 32 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png new file mode 100644 index 0000000..669b150 Binary files /dev/null and b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png differ diff --git a/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png.meta b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png.meta new file mode 100644 index 0000000..674d3f7 --- /dev/null +++ b/Assets/Photon/Fusion/Editor/Resources/icons_welcome/samples.png.meta @@ -0,0 +1,84 @@ +fileFormatVersion: 2 +guid: d2b478a89610eb748a4b0b8040f43a01 +timeCreated: 1500450669 +licenseType: Pro +TextureImporter: + fileIDToRecycleName: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 0 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + filterMode: -1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Standalone + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + - buildTarget: Android + maxTextureSize: 32 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins.meta b/Assets/Photon/Fusion/Plugins.meta new file mode 100644 index 0000000..bd49102 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 88a369d26c2621f4b857e1099c7e4364 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets.meta b/Assets/Photon/Fusion/Plugins/NanoSockets.meta new file mode 100644 index 0000000..8d21e97 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7c0f804fc175b194a84166c696747f0b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android.meta new file mode 100644 index 0000000..6ef8bd3 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9007c321666fb65448aaf7502001c2b1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a.meta new file mode 100644 index 0000000..26ff8b4 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f3c69e69a588ff64294d4352f80d4762 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so new file mode 100644 index 0000000..384d104 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so.meta new file mode 100644 index 0000000..64a924e --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/arm64-v8a/libnanosockets.so.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: fd330aee9172d2c4087e7baffb8d9f5d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARM64 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a.meta new file mode 100644 index 0000000..4d4c500 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e49e89c8db6eb8346b3b480b38d620ce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so new file mode 100644 index 0000000..7167bd8 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so.meta new file mode 100644 index 0000000..fa2f7ba --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/armeabi-v7a/libnanosockets.so.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: 34d5b82ebfd43f840915715f4a3a133d +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86.meta new file mode 100644 index 0000000..047796d --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f2538901e4f33b94a83d1cf785b14a5d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so new file mode 100644 index 0000000..a3e3190 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so.meta new file mode 100644 index 0000000..50a6c12 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Android/x86/libnanosockets.so.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: abb420497d9a79343b4647172f1fa2ec +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: x86 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Linux.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux.meta new file mode 100644 index 0000000..e17ab63 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4128901ef174a1489d784676ce0a5aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so new file mode 100644 index 0000000..c0a2dbd Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so.meta new file mode 100644 index 0000000..f14f406 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Linux/libnanosockets.so.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: 1353bd98fd16b304f82bc4cb8ff7d03f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 0 + Exclude OSXUniversal: 1 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: Linux + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets.meta new file mode 100644 index 0000000..5e48db9 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 497a010bd4e8f2c4ea10903a5b29aa7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic.meta new file mode 100644 index 0000000..0cca44c --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8fc02f7d3703ecc409b5446020e1dc8f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll new file mode 100644 index 0000000..8fbe77b Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll.meta new file mode 100644 index 0000000..4e89bf0 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Dynamic/NanoSockets.dll.meta @@ -0,0 +1,89 @@ +fileFormatVersion: 2 +guid: 97dbb2b4c5d67894381958ef748f2a74 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 0 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude PS4: 1 + Exclude PS5: 1 + Exclude Switch: 1 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static.meta new file mode 100644 index 0000000..6e06585 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 380f693c255fff44981fcfbdffdbac8a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll new file mode 100644 index 0000000..6e441f2 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll.meta new file mode 100644 index 0000000..a1dccf2 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/NanoSockets/Static/NanoSockets.dll.meta @@ -0,0 +1,104 @@ +fileFormatVersion: 2 +guid: d42594000bed8de429fd22c181be00ab +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude PS4: 0 + Exclude PS5: 0 + Exclude Switch: 0 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Nintendo Switch: Switch + second: + enabled: 1 + settings: {} + - first: + PS4: PS4 + second: + enabled: 1 + settings: {} + - first: + PS5: PS5 + second: + enabled: 1 + settings: {} + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Windows.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows.meta new file mode 100644 index 0000000..091eeeb --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d310c8f675fbe94695191c753bcb583 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll new file mode 100644 index 0000000..1fc1e7f Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll.meta new file mode 100644 index 0000000..e566d03 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/Windows/nanosockets.dll.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: 7b3f94104f169cc4e84342c38a1da2e3 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/iOS.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS.meta new file mode 100644 index 0000000..4c1791e --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dcab16d8fc28b504cba47b74be7a55e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a new file mode 100644 index 0000000..59d62c9 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a.meta new file mode 100644 index 0000000..7942b50 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/iOS/libnanosockets.a.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: ce1995280bfb011488cdf6e584ba46bc +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 0 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: None + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE b/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE new file mode 100644 index 0000000..56ff400 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Stanislav Denisov (nxrighthere@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE.meta new file mode 100644 index 0000000..2d5897c --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/libnanosockets_LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2007cbe994ec1441bb4dc0783ee8d1f5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS.meta new file mode 100644 index 0000000..d0ce67b --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b007cf72c6354874e99e60a963804385 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon.meta new file mode 100644 index 0000000..a9ba180 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3e3e6364aaeb0d240920fab8da44dc6d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib new file mode 100644 index 0000000..ac51d11 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib.meta new file mode 100644 index 0000000..1feff7e --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/AppleSilicon/libnanosockets.dylib.meta @@ -0,0 +1,63 @@ +fileFormatVersion: 2 +guid: a2f5edea154374a25bfdffaf6f0d04f8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: ARM64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel.meta new file mode 100644 index 0000000..e150ffb --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 18dca813845fe194492e7f2294234b12 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib new file mode 100644 index 0000000..7a5a7f2 Binary files /dev/null and b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib differ diff --git a/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib.meta b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib.meta new file mode 100644 index 0000000..8422777 --- /dev/null +++ b/Assets/Photon/Fusion/Plugins/NanoSockets/macOS/Intel/libnanosockets.dylib.meta @@ -0,0 +1,80 @@ +fileFormatVersion: 2 +guid: 1c04cb8f7ee84ea46b2b6832cefd8819 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 0 + Exclude Linux64: 1 + Exclude OSXUniversal: 0 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: OSX + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: None + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Resources.meta b/Assets/Photon/Fusion/Resources.meta new file mode 100644 index 0000000..59ab9cd --- /dev/null +++ b/Assets/Photon/Fusion/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ffd898ee95fa1ca4abc86e2cfbe13fe9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt b/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt new file mode 100644 index 0000000..1760267 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt @@ -0,0 +1 @@ +This file exists to maintain a .meta file that is used to locate this config folder. \ No newline at end of file diff --git a/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt.meta b/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt.meta new file mode 100644 index 0000000..1d8b440 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/ConfigAssetsFolder.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 65cf5f43e8c20f941b0bb130b392ec89 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Resources/FusionGraphShader.shader b/Assets/Photon/Fusion/Resources/FusionGraphShader.shader new file mode 100644 index 0000000..4372253 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/FusionGraphShader.shader @@ -0,0 +1,164 @@ +Shader "Fusion Graph Shader" +{ + Properties + { + _MainTex ("Sprite Texture", 2D) = "white" {} + + _ColorMask("Color Mask", Float) = 15 + + _GoodColor("Good Color", Color) = (0,1,0,1) + _WarnColor("Warn Color", Color) = (.9,.6,0,1) + _BadColor("Bad Color", Color) = (1,0,0,1) + _FlagColor("Flag Color", Color) = (1,1,0,1) + _InvalidColor("Invalid Color", Color) = (0,1,1,1) + _NoneColor("Flag Color", Color) = (0,0,0,0) + _ZWrite("ZWrite", Int) = 0 + } + + SubShader + { + Tags + { + "Queue"="Geometry+1000" + "IgnoreProjector"="True" + "RenderType"="Transparent" + "PreviewType"="Plane" + } + + //Cull Off + Lighting Off + ZWrite [_ZWrite] + ZTest LEqual + Blend One OneMinusSrcAlpha + //Blend SrcAlpha OneMinusSrcAlpha + ColorMask [_ColorMask] + + Pass + { + + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + #include "UnityCG.cginc" + + struct appdata_t + { + float4 vertex : POSITION; + float2 texcoord : TEXCOORD0; + }; + + struct v2f + { + float4 vertex : SV_POSITION; + half2 texcoord : TEXCOORD0; + }; + + //fixed4 _Color; + fixed4 _GoodColor; + fixed4 _WarnColor; + fixed4 _BadColor; + fixed4 _FlagColor; + fixed4 _InvalidColor; + fixed4 _NoneColor; + + // data for bar graph + uniform float _Data[1024]; + uniform float _Intensity[1024]; + uniform float _Count; + uniform float _Height; + + uniform float _ZeroCenter; + + v2f vert(appdata_t IN) + { + v2f OUT; + OUT.vertex = UnityObjectToClipPos(IN.vertex); + OUT.texcoord = IN.texcoord; +#ifdef UNITY_HALF_TEXEL_OFFSET + OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1); +#endif + return OUT; + } + + fixed4 frag(v2f IN) : SV_Target + { + const float i = floor(IN.texcoord.x * _Count); + const float fv = _Intensity[i]; + const float v = max(_Data[i], 1 / _Count); + const float y = IN.texcoord.y; + + float a; + float dim; + + // All lines draw outward from zero. + if (_ZeroCenter > 0) { + if (v > _ZeroCenter) { + dim = 1; + + if (y > _ZeroCenter) { + a = !(y > v); + } + else { + a = 0; + } + } + else { + dim = .5; + if (y > _ZeroCenter) { + a = 0; + } + else { + if (y > v) { + a = .5; + } + else { + a = 0; + } + } + } + } + else { + dim = 1; + a = !(y > v); + } + + + // calculate alpha and rgb + float4 c; + if (_Count == 0) { + c.a = 0.1; + c.rgb = _NoneColor.rgb; + } + else { + if (fv == 0) { + c.rgb = _GoodColor.rgb * dim * a; + } + else if (fv == .5) { + c.rgb = _WarnColor.rgb * dim * a; + } + else if (fv == 1) { + c.rgb = _BadColor.rgb * dim * a; + } + else if (fv == -2) { + c.rgb = _NoneColor.rgb * .1; + } + else if (fv < 0) { + c.rgb = _InvalidColor.rgb * dim * a; + } + else if (fv > 1) { + c.rgb = _FlagColor.rgb; + } + else if (fv < 0.5) { + c.rgb = lerp(_GoodColor.rgb, _WarnColor.rgb, max(0, fv * 2)) * dim * a; + } + else { + c.rgb = lerp(_WarnColor.rgb, _BadColor.rgb, min(1, fv - .5f) * 2) * dim * a; + } + c.a = a; + } + return c; + } + ENDCG + } + } +} diff --git a/Assets/Photon/Fusion/Resources/FusionGraphShader.shader.meta b/Assets/Photon/Fusion/Resources/FusionGraphShader.shader.meta new file mode 100644 index 0000000..9a500b4 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/FusionGraphShader.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 191d95ce624abaa4ba0294fc7baa2845 +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion new file mode 100644 index 0000000..7417674 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion @@ -0,0 +1 @@ +{"Version":1,"TypeId":"NetworkProjectConfig","PeerMode":0,"PhysicsEngine":0,"ServerPhysicsMode":0,"UseLagCompensation":true,"LagCompensation":{"HitboxBufferSize":60,"ExpansionFactor":0.20000000298023225,"Optimize":false,"DebugBroadphase":false,"DebugHistory":false,"DebugColor":{"r":0.0,"g":1.0,"b":0.0,"a":0.5},"ClientDebugColor":{"r":0.0,"g":0.0,"b":1.0,"a":0.5},"HistoryDebugColor":{"r":0.0,"g":0.0,"b":1.0,"a":0.5}},"SceneLoadSpawnMode":0,"DeltaCompressor":0,"InvokeRenderInBatchMode":true,"MaxNetworkedObjectCount":8192,"NetworkIdIsObjectName":false,"EnableHostMigration":false,"HostMigrationSnapshotInterval":60,"Simulation":{"InputDataWordCount":0,"TickRate":60,"DefaultPlayers":10,"ReplicationMode":0,"ObjectInterest":false,"SimulationCulling":false,"ServerPacketInterval":1,"ClientPacketInterval":1},"Interpolation":{"DeltaAdjustment":1,"AllowedJitter":25,"SnapLimit":200,"MultiplierMin":1.25,"MultiplierMax":3.0},"Network":{"SocketSendBufferSize":256,"SocketRecvBufferSize":256,"ConnectAttempts":10,"ConnectInterval":0.5,"ConnectionDefaultRtt":0.1,"ConnectionTimeout":10.0,"ConnectionPingInterval":1.0,"ConnectionShutdownTime":1.0,"MtuDefault":1136,"ReliableDataTransferModes":3},"NetworkConditions":{"Enabled":false,"DelayShape":1,"DelayMin":0.01,"DelayMax":0.1,"DelayPeriod":3.0,"DelayThreshold":0.5,"AdditionalJitter":0.01,"LossChanceShape":1,"LossChanceMin":0.0,"LossChanceMax":0.02,"LossChanceThreshold":0.9,"LossChancePeriod":3.0,"AdditionalLoss":0.005},"Heap":{"PageShift":14,"PageCount":128,"GlobalsSize":0},"AccuracyDefaults":{"coreKeys":["Uncompressed","Default","Position","Rotation","NormalizedTime"],"coreDefs":[{"_value":0.0,"_inverse":Infinity,"_hash":-1382104104},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-814817977},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-194233199},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-258202764},{"_value":0.00009999999747378752,"_inverse":10000.0,"_hash":1061325578}],"coreVals":[{"_value":0.0,"_inverse":Infinity,"_hash":-1382104104},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-814817977},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-194233199},{"_value":0.0010000000474974514,"_inverse":999.9999389648438,"_hash":-258202764},{"_value":0.00009999999747378752,"_inverse":10000.0,"_hash":1061325578}],"tags":[],"values":[]},"AssembliesToWeave":["Assembly-CSharp","Assembly-CSharp-firstpass"],"UseSerializableDictionary":true,"NullChecksForNetworkedProperties":true} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta new file mode 100644 index 0000000..9fba001 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66160f7e98c87b64baf1de58ebb07f12 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 66a64a17d0b40f34f9224317a5a84bf2, type: 3} + PrefabAssetsContainerPath: diff --git a/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset new file mode 100644 index 0000000..10a6484 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1878438611, guid: 7de3b8b9e1263ad479e2d0c4261b7646, type: 3} + m_Name: PhotonAppSettings + m_EditorClassIdentifier: + AppSettings: + AppIdFusion: 9dc77937-8216-4732-b478-bebb2fd2ef86 + AppIdChat: + AppIdVoice: + AppVersion: + UseNameServer: 1 + FixedRegion: + Server: + Port: 0 + ProxyServer: + Protocol: 0 + EnableProtocolFallback: 1 + AuthMode: 0 + EnableLobbyStatistics: 0 + NetworkLogging: 1 diff --git a/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset.meta b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset.meta new file mode 100644 index 0000000..38146b2 --- /dev/null +++ b/Assets/Photon/Fusion/Resources/PhotonAppSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 272e58ac78140ee458df16cfb48052da +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts.meta b/Assets/Photon/Fusion/Scripts.meta new file mode 100644 index 0000000..7462f45 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1a5cbf852da42e409850f07dffecb92 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs b/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs new file mode 100644 index 0000000..558d09d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs @@ -0,0 +1 @@ +// deleted 28th may 2021 \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs.meta b/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs.meta new file mode 100644 index 0000000..a81ec3a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/BurstDeltaCompressor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95c90ab8ac9d71947a268a782c9523a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor.meta b/Assets/Photon/Fusion/Scripts/Editor.meta new file mode 100644 index 0000000..51f1db2 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b1d6621a2aef9d43ad2b1d47d5dc852 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef new file mode 100644 index 0000000..53f9f90 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef @@ -0,0 +1,41 @@ +{ + "name": "Fusion.Editor", + "rootNamespace": "", + "references": [ + "GUID:142cc54f3f73e4dabac1c30b19afb51d", + "GUID:19e39f4cea842594788e0c7be4c044c8" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "Mono.Cecil.dll", + "Mono.Cecil.Pdb.dll", + "Mono.Cecil.Mdb.dll", + "Mono.Cecil.Rocks.dll", + "Fusion.Common.dll", + "Fusion.Runtime.dll", + "Fusion.Realtime.dll", + "Sirenix.OdinInspector.Editor.dll", + "Sirenix.Utilities.Editor.dll", + "Sirenix.Utilities.dll" + ], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.nuget.mono-cecil", + "expression": "1", + "define": "FUSION_HAS_MONO_CECIL" + }, + { + "name": "nuget.mono-cecil", + "expression": "0.1", + "define": "FUSION_HAS_MONO_CECIL" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef.meta b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef.meta new file mode 100644 index 0000000..42ebe57 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4d449a4732e064d26a3069f2cfd88917 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs new file mode 100644 index 0000000..bc1ce40 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs @@ -0,0 +1,11056 @@ +#if !FUSION_DEV + +#region Assets/Photon/Fusion/Scripts/Editor/AssetObjectEditor.cs + +namespace Fusion.Editor { + using Fusion; + using UnityEditor; + + [CustomEditor(typeof(AssetObject), true)] + public class AssetObjectEditor : UnityEditor.Editor { + public override void OnInspectorGUI() { + base.OnInspectorGUI(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/AssetPipeline/INetworkPrefabSourceFactory.cs + +namespace Fusion.Editor { + using System; + + public interface INetworkPrefabSourceFactory { + int Order { get; } + Type SourceType { get; } + + NetworkPrefabSourceUnityBase TryCreate(string assetPath); + UnityEngine.GameObject EditorResolveSource(NetworkPrefabSourceUnityBase prefabAsset); + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/AssetPipeline/NetworkPrefabSourceFactory.cs + +namespace Fusion.Editor { + using System; + using System.Linq; + + internal static class NetworkPrefabSourceFactory { + + private static readonly Lazy _factories = new Lazy(() => { + return UnityEditor.TypeCache.GetTypesDerivedFrom() + .Select(x => (INetworkPrefabSourceFactory)Activator.CreateInstance(x)) + .OrderBy(x => x.Order) + .ToArray(); + }); + + public static NetworkPrefabSourceUnityBase Create(string assetPath) { + foreach (var factory in _factories.Value) { + var source = factory.TryCreate(assetPath); + if (source != null) { + if (source.GetType() != factory.SourceType) { + throw new InvalidOperationException($"Factory {factory} is expected to return {factory.SourceType}, returned {source.GetType()} instead"); + } + if (source is INetworkPrefabSource == false) { + throw new InvalidOperationException($"Type {source.GetType()} does not implement {nameof(INetworkPrefabSource)}"); + } + return source; + } + } + + throw new InvalidOperationException($"No factory could create info for prefab {assetPath}"); + } + + public static NetworkObject ResolveOrThrow(NetworkPrefabSourceUnityBase source) { + foreach (var factory in _factories.Value) { + if (factory.SourceType == source.GetType()) { + var prefab = factory.EditorResolveSource(source); + if (prefab == null) { + throw new InvalidOperationException($"Factory {factory} returned null for {source}"); + } + var networkObject = prefab.GetComponent(); + if (networkObject == null) { + throw new InvalidOperationException($"Prefab {prefab} does not contain {nameof(NetworkObject)} component"); + } + return networkObject; + } + } + + throw new InvalidOperationException($"No factory could resolve {source}"); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/AssetPipeline/NetworkPrefabSourceFactoryResource.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + public class NetworkPrefabAssetFactoryResource: INetworkPrefabSourceFactory { + + public const int DefaultOrder = 1000; + + Type INetworkPrefabSourceFactory.SourceType => typeof(NetworkPrefabSourceUnityResource); + + int INetworkPrefabSourceFactory.Order => DefaultOrder; + + NetworkPrefabSourceUnityBase INetworkPrefabSourceFactory.TryCreate(string assetPath) { + if (PathUtils.MakeRelativeToFolder(assetPath, "Resources", out var resourcesPath)) { + var result = ScriptableObject.CreateInstance(); + result.ResourcePath = PathUtils.GetPathWithoutExtension(resourcesPath); + return result; + } else { + return null; + } + } + + GameObject INetworkPrefabSourceFactory.EditorResolveSource(NetworkPrefabSourceUnityBase prefabAsset) { + var resource = (NetworkPrefabSourceUnityResource)prefabAsset; + return Resources.Load(resource.ResourcePath); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/AssetPipeline/NetworkPrefabSourceFactoryStatic.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + public class NetworkPrefabAssetFactoryStatic : INetworkPrefabSourceFactory { + + public const int DefaultOrder = 2000; + + Type INetworkPrefabSourceFactory.SourceType => typeof(NetworkPrefabSourceUnityStatic); + + int INetworkPrefabSourceFactory.Order => DefaultOrder; + + NetworkPrefabSourceUnityBase INetworkPrefabSourceFactory.TryCreate(string assetPath) { + var gameObject = AssetDatabase.LoadAssetAtPath(assetPath); + if (gameObject == null) { + throw new InvalidOperationException($"Unable to load {assetPath}"); + } + + var networkObject = gameObject.GetComponent(); + if (networkObject == null) { + throw new InvalidOperationException($"Unable to get {nameof(NetworkObject)} from {assetPath}"); + } + + var result = ScriptableObject.CreateInstance(); + result.PrefabReference = gameObject; + return result; + } + + GameObject INetworkPrefabSourceFactory.EditorResolveSource(NetworkPrefabSourceUnityBase prefabAsset) { + return ((NetworkPrefabSourceUnityStatic)prefabAsset).PrefabReference; + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/BehaviourEditor.cs + +namespace Fusion.Editor { + + using System; + using System.Collections.Generic; + using System.Linq; + using UnityEditor; + + [CustomEditor(typeof(Fusion.Behaviour), true)] + [CanEditMultipleObjects] + public partial class BehaviourEditor : +#if ODIN_INSPECTOR && !FUSION_ODIN_DISABLED + Sirenix.OdinInspector.Editor.OdinEditor +#else + UnityEditor.Editor +#endif + { + protected string _expandedHelpName; + protected BehaviourActionInfo[] behaviourActions; + + public override void OnInspectorGUI() { + PrepareInspectorGUI(); + base.DrawDefaultInspector(); + DrawBehaviourActions(); + } + + protected void PrepareInspectorGUI() { + FusionEditorGUI.InjectPropertyDrawers(serializedObject); + } + + protected void DrawBehaviourActions() { + // Draw any BehaviourActionAttributes for this Component + if (behaviourActions == null) { + behaviourActions = target.GetActionAttributes(); + } + + target.DrawAllBehaviourActionAttributes(behaviourActions, ref _expandedHelpName); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/BehaviourEditorUtils.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + public class BehaviourActionInfo { + public string MemberName; + public string Summary; + public Attribute Attribute; + public Action Action; + public MemberInfo MemberInfo; + public Func Condition; + public BehaviourActionInfo(string memberName, string summary, Attribute attribute, Action action, Func condition) { + Summary = summary; + MemberName = memberName; + Attribute = attribute; + Action = action; + Condition = condition; + } + } + + public static class BehaviourEditorUtils { + + // Getter to Delegate conversion magic for method that returns any kind of object and has no arguments + private static readonly MethodInfo CallPropertyDelegateMethod = typeof(BehaviourEditorUtils).GetMethod(nameof(CallPropertyDelegate), BindingFlags.NonPublic | BindingFlags.Static); + private static Func CallPropertyDelegate(Func deleg) => instance => deleg((TDeclared)instance); + + public static Dictionary>> GetValueDelegateLookups = new Dictionary>>(); + public static Dictionary> ActionDelegateLookups = new Dictionary>(); + + /// + /// Find member by name, and if compatible extracts a delegate for (object)value = Func(object targetInstance). + /// + public static Func GetDelegateFromMember(this Type type, string memberName) { + if (memberName == null || memberName == "") + return null; + + // See if we already have a cached delegate for this type and member name. + if (GetValueDelegateLookups.TryGetValue(type, out var delegateLookup)) { + if (delegateLookup.TryGetValue(memberName, out var getValueDelegate)) { + return getValueDelegate; + } + } else { + delegateLookup = new Dictionary>(); + GetValueDelegateLookups.Add(type, delegateLookup); + } + + // No delegate exists, brute force find one. + + var members = type.GetMember(memberName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + foreach (MemberInfo member in members) { + if (member is FieldInfo) { + var finfo = (FieldInfo)member; + var getValDelegate = (Func)((object targ) => finfo.GetValue(targ)); + delegateLookup.Add(memberName, getValDelegate); + return getValDelegate; + } + if (member is PropertyInfo) { + var p = (PropertyInfo)member; + var getMethod = p.GetMethod; + var declaring = p.DeclaringType; + var typeOfResult = p.PropertyType; + var getMethodDelegateType = typeof(Func<,>).MakeGenericType(declaring, typeOfResult); + var getMethodDelegate = getMethod.CreateDelegate(getMethodDelegateType); + var getMethodGeneric = CallPropertyDelegateMethod.MakeGenericMethod(declaring, typeOfResult); + var getValDelegate = (Func)getMethodGeneric.Invoke(null, new[] { getMethodDelegate }); + delegateLookup.Add(memberName, getValDelegate); + return getValDelegate; + } + if (member is MethodInfo) { + var m = member as MethodInfo; + var getMethod = (MethodInfo)member; + var declaring = member.DeclaringType; + var typeOfResult = m.ReturnType; + var getMethodDelegateType = typeof(Func<,>).MakeGenericType(declaring, typeOfResult); + var getMethodDelegate = getMethod.CreateDelegate(getMethodDelegateType); + var getMethodGeneric = CallPropertyDelegateMethod.MakeGenericMethod(declaring, typeOfResult); + var getValDelegate = (Func)getMethodGeneric.Invoke(null, new[] { getMethodDelegate }); + delegateLookup.Add(memberName, getValDelegate); + return getValDelegate; + } + } + + delegateLookup.Add(memberName, null); + return null; + } + + private static List reusableAttributeList = new List(); + + /// + /// Collect all BehaviourActionAttributes on a target's type, and cache as much of the heavy reflection out as possible. + /// + internal static BehaviourActionInfo[] GetActionAttributes(this UnityEngine.Object target) { + + var targetType = target.GetType(); + if (!typeof(Behaviour).IsAssignableFrom(targetType)) + return null; + + reusableAttributeList.Clear(); + + var methods = targetType.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + + foreach (var member in methods) { + // Rendering action attributes at the end of the inspector only if they are on for Methods and Property. + // Field attributes are rendered in-line elsewhere. + MethodInfo method; + if (member is MethodInfo minfo) + method = minfo; + else if (member is PropertyInfo pinfo) + method = null; + else + continue; + + var attrs = member.GetCustomAttributes(); + foreach (var attr in attrs) { + + if (attr is BehaviourWarnAttribute warnAttr) { + var condName = warnAttr.ConditionMember; + Func condMethod = targetType.GetDelegateFromMember(warnAttr.ConditionMember); + reusableAttributeList.Add(new BehaviourActionInfo(member.Name, method.GetXmlDocSummary(), attr, null, condMethod)); + continue; + } + + if (attr is BehaviourButtonActionAttribute buttonAttr) { + var condName = buttonAttr.ConditionMember; + Func condMethod = targetType.GetDelegateFromMember(buttonAttr.ConditionMember); + reusableAttributeList.Add(new BehaviourActionInfo(member.Name, method.GetXmlDocSummary(), attr, (Action)method.CreateDelegate(typeof(Action), target), condMethod)); + continue; + } + + if (attr is BehaviourToggleAttribute toggleAttr) { + Func condMethod = targetType.GetDelegateFromMember(toggleAttr.ConditionMember); + var actioninfo = new BehaviourActionInfo(member.Name, (member as PropertyInfo).GetXmlDocSummary(), attr, null, condMethod); + actioninfo.MemberInfo = member; + reusableAttributeList.Add(actioninfo); + continue; + } + + if (attr is BehaviourActionAttribute actionAttr) { + Func condMethod = targetType.GetDelegateFromMember(actionAttr.ConditionMember); + reusableAttributeList.Add(new BehaviourActionInfo(member.Name, method.GetXmlDocSummary(), attr, (Action)method.CreateDelegate(typeof(Action), target), condMethod)); + continue; + } + } + } + + return reusableAttributeList.ToArray(); + } + + /// + /// Draw all special editor method attributes specific to Fusion.Behaviour rendering. + /// + /// + internal static void DrawAllBehaviourActionAttributes(this UnityEngine.Object target, BehaviourActionInfo[] behaviourActions, ref string expandedHelpName) { + + if (behaviourActions == null) + return; + + foreach (var ba in behaviourActions) { + + var attr = ba.Attribute; + var action = ba.Action; + var condition = ba.Condition; + + if (attr is BehaviourWarnAttribute warnAttr) { + warnAttr.DrawEditorWarnAttribute(target, condition); + continue; + } + + if (attr is BehaviourButtonActionAttribute buttonAttr) { + buttonAttr.DrawEditorButtonAttribute(ba, target, ref expandedHelpName); + continue; + } + + if (attr is BehaviourToggleAttribute toggleAttr) { + toggleAttr.DrawEditorToggleAttribute(ba, target, ref expandedHelpName); + continue; + } + + // Action is the base class, so this needs to be last always if more derived classes are added + if (attr is BehaviourActionAttribute actionAttr) { + action.Invoke(); + continue; + } + } + } + + internal static Action GetActionDelegate(this Type targetType, string actionMethodName, UnityEngine.Object target) { + + if (ActionDelegateLookups.TryGetValue(targetType, out var lookup)) { + if (lookup.TryGetValue(actionMethodName, out var found)) + return found; + } else { + lookup = new Dictionary(); + ActionDelegateLookups.Add(targetType, lookup); + } + var executeMethod = target.GetType().GetMethod(actionMethodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + var action = (Action)executeMethod.CreateDelegate(typeof(Action), target); + lookup.Add(actionMethodName, action); + return action; + } + + internal static void NoActionWarning() { + Debug.LogWarning($"No action defined for {nameof(BehaviourButtonActionAttribute)}. Be sure to either add one to the attribute arguments, or place the attribute on a method."); + } + + internal static void DrawEditorToggleAttribute(this BehaviourToggleAttribute toggleAttr, BehaviourActionInfo actionInfo, UnityEngine.Object target, ref string expandedPropertyName) { + // If a condition member exists for this attribute, check it. + if (actionInfo.Condition != null) { + object valObj = actionInfo.Condition(target); + if (valObj == null || valObj.GetObjectValueAsDouble() == 0) { + return; + } + } + + DrawEditorToggleAttributeFinal(toggleAttr, target, actionInfo, ref expandedPropertyName); + } + + internal static void DrawEditorButtonAttribute(this BehaviourButtonActionAttribute buttonAttr, BehaviourActionInfo actionInfo, UnityEngine.Object target, ref string expandedPropertyName) { + + // If a condition member exists for this attribute, check it. + if (actionInfo.Condition != null) { + object valObj = actionInfo.Condition(target); + if (valObj == null || valObj.GetObjectValueAsDouble() == 0) { + return; + } + } + + DrawEditorButtonAttributeFinal(buttonAttr, target, actionInfo, ref expandedPropertyName); + } + + internal static void DrawEditorWarnAttribute(this BehaviourWarnAttribute buttonAttr, UnityEngine.Object target) { + if (buttonAttr.ConditionMember != null) { + + var getValDelegate = target.GetType().GetDelegateFromMember(buttonAttr.ConditionMember); + if (getValDelegate == null) + return; + + object valObj = getValDelegate(target); + + if (valObj == null || valObj.GetObjectValueAsDouble() == 0) { + return; + } + } + + DrawEditorWarnAttributeFinal(buttonAttr); + } + + internal static void DrawEditorWarnAttribute(this BehaviourWarnAttribute buttonAttr, UnityEngine.Object target, Func condition) { + + // If a condition member exists for this attribute, check it. + if (condition != null) { + object valObj = condition(target); + if (valObj == null || valObj.GetObjectValueAsDouble() == 0) { + return; + } + } + DrawEditorWarnAttributeFinal(buttonAttr); + } + + + static void DrawEditorButtonAttributeFinal(this BehaviourButtonActionAttribute buttonAttr, UnityEngine.Object target, BehaviourActionInfo actionInfo, ref string expandedPropertyName) { + var flags = buttonAttr.ConditionFlags; + + if (ShouldShow(flags)) { + GUILayout.Space(4); + Rect rect = EditorGUILayout.GetControlRect(); + + if (!string.IsNullOrEmpty(actionInfo.Summary)) { + var wasExpanded = FusionEditorGUI.IsHelpExpanded(target, actionInfo.MemberName); + var buttonRect = FusionEditorGUI.GetInlineHelpButtonRect(InlineHelpButtonPlacement.BeforeLabel, rect, GUIContent.none, expectFoldout: false); + + if (wasExpanded) { + var content = new GUIContent(actionInfo.Summary); + + var contentRect = FusionEditorGUI.GetInlineHelpRect(content); + var totalRect = new Rect(rect.x, rect.y, rect.width, rect.height + contentRect.height); + + EditorGUILayout.GetControlRect(false, contentRect.height, EditorStyles.helpBox); + FusionEditorGUI.DrawInlineHelp(content, totalRect, contentRect); + } + + if (FusionEditorGUI.DrawInlineHelpButton(buttonRect, wasExpanded)) { + FusionEditorGUI.SetHelpExpanded(target, actionInfo.MemberName, !wasExpanded); + if (!wasExpanded) { + expandedPropertyName = actionInfo.MemberName; + } + } + } + + if (GUI.Button(rect, buttonAttr.ButtonName)) { + actionInfo.Action.Invoke(); + if ((flags & BehaviourActionAttribute.ActionFlags.DirtyAfterButton) == BehaviourActionAttribute.ActionFlags.DirtyAfterButton) { + EditorUtility.SetDirty(target); + } + } + } + } + + static void DrawEditorToggleAttributeFinal(this BehaviourToggleAttribute toggleAttr, UnityEngine.Object target, BehaviourActionInfo actionInfo, ref string expandedPropertyName) { + var flags = toggleAttr.ConditionFlags; + + if (ShouldShow(flags)) { + GUILayout.Space(4); + Rect rect = EditorGUILayout.GetControlRect(); + + if (!string.IsNullOrEmpty(actionInfo.Summary)) { + var wasExpanded = FusionEditorGUI.IsHelpExpanded(target, actionInfo.MemberName); + var buttonRect = FusionEditorGUI.GetInlineHelpButtonRect(InlineHelpButtonPlacement.BeforeLabel, rect, GUIContent.none, expectFoldout: false); + + if (wasExpanded) { + var content = new GUIContent(actionInfo.Summary); + + var contentRect = FusionEditorGUI.GetInlineHelpRect(content); + var totalRect = new Rect(rect.x, rect.y, rect.width, rect.height + contentRect.height); + + EditorGUILayout.GetControlRect(false, contentRect.height, EditorStyles.helpBox); + FusionEditorGUI.DrawInlineHelp(content, totalRect, contentRect); + } + + if (FusionEditorGUI.DrawInlineHelpButton(buttonRect, wasExpanded)) { + FusionEditorGUI.SetHelpExpanded(target, actionInfo.MemberName, !wasExpanded); + if (!wasExpanded) { + expandedPropertyName = actionInfo.MemberName; + } + } + } + + var pinfo = actionInfo.MemberInfo as PropertyInfo; + + var oldval = (bool)pinfo.GetValue(target); + var newval = EditorGUI.Toggle(rect, ObjectNames.NicifyVariableName(pinfo.Name), oldval); + if (newval != oldval) { + pinfo.SetValue(target, newval); + if ((flags & BehaviourActionAttribute.ActionFlags.DirtyAfterButton) == BehaviourActionAttribute.ActionFlags.DirtyAfterButton) { + EditorUtility.SetDirty(target); + } + } + } + } + + static void DrawEditorWarnAttributeFinal(this BehaviourWarnAttribute warnAttr) { + + if (ShouldShow(warnAttr.ConditionFlags)) { + GUILayout.Space(4); + DrawWarnBox(warnAttr.WarnText, MessageType.Warning); + } + } + + public static bool DrawWarnButton(GUIContent buttonText, MessageType icon = MessageType.None) { + + var rect = EditorGUILayout.GetControlRect(false, 24); + var clicked = GUI.Button(rect, buttonText); + + if (icon != MessageType.None) { + Texture2D icontexture; + switch (icon) { + case MessageType.Info: + icontexture = FusionGUIStyles.InfoIcon; + break; + case MessageType.Warning: + icontexture = FusionGUIStyles.WarnIcon; + break; + case MessageType.Error: + icontexture = FusionGUIStyles.ErrorIcon; + break; + default: + icontexture = FusionGUIStyles.InfoIcon; + break; + } + GUI.Label(new Rect(rect) { xMin = rect.xMin + 4, yMin = rect.yMin + 2, yMax = rect.yMax - 2 }, icontexture); + } + return clicked; + } + + public static void DrawWarnBox(string message, MessageType msgtype = MessageType.Warning, FusionGUIStyles.GroupBoxType? groupBoxType = null) { + + var style = groupBoxType.HasValue ? groupBoxType.Value.GetStyle() : ((FusionGUIStyles.GroupBoxType)msgtype).GetStyle(); + EditorGUILayout.BeginHorizontal(style/*, GUILayout.ExpandHeight(true)*/); + { + + // TODO: Cache these icons in a utility + if (msgtype != MessageType.None) { + Texture icon = + msgtype == MessageType.Warning ? FusionGUIStyles.WarnIcon : + msgtype == MessageType.Error ? FusionGUIStyles.ErrorIcon : + FusionGUIStyles.InfoIcon; + + GUI.DrawTexture(EditorGUILayout.GetControlRect(GUILayout.Width(32), GUILayout.Height(32)), icon, ScaleMode.ScaleAndCrop); + } + + EditorGUILayout.LabelField(message, FusionGUIStyles.WarnLabelStyle); + } + EditorGUILayout.EndHorizontal(); + } + + static bool ShouldShow(BehaviourActionAttribute.ActionFlags flags) { + bool isPlaying = Application.isPlaying; + return ( + (isPlaying && (flags & BehaviourActionAttribute.ActionFlags.ShowAtRuntime) == BehaviourActionAttribute.ActionFlags.ShowAtRuntime) || + (!isPlaying && (flags & BehaviourActionAttribute.ActionFlags.ShowAtNotRuntime) == BehaviourActionAttribute.ActionFlags.ShowAtNotRuntime)); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/ChildLookupEditor.cs + +// removed July 12 2021 + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AccuracyDefaultsDrawer.cs + +namespace Fusion.Editor { + + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(AccuracyDefaults))] + public class AccuracyDefaultsDrawer : PropertyDrawer { + + private static string[] _tagNames; + public static string[] TagNames { + get { + if (_tagNames == null) { + RefreshCache(); + } + return _tagNames; + } + } + + private static Dictionary _tagLookup = new Dictionary(); + public static Dictionary TagLookup { + get { + if (_tagLookup.Count == 0) { + RefreshCache(); + } + return _tagLookup; + } + } + + // Invalidates the static caches of AccuracyDefault names, used in Accuracy droplist. + private static void ClearTagCache() { + _tagLookup.Clear(); + _tagNames = null; + } + + public static void RefreshCache() { + + var settings = NetworkProjectConfig.Global.AccuracyDefaults; + settings.RebuildLookup(); + + List names = new List(settings.coreKeys); + names.AddRange(settings.tags); + _tagNames = names.ToArray(); + + _tagLookup.Clear(); + for (int i = 0, cnt = _tagNames.Length; i < cnt; ++i) { + string t = _tagNames[i]; + _tagLookup.Add(t.GetHashDeterministic(), (i, t)); + } + } + + private static void SetValue(SerializedProperty array, int index, Accuracy value) { + array.GetArrayElementAtIndex(index).FindPropertyRelativeOrThrow("_value").floatValue = value._value; + array.GetArrayElementAtIndex(index).FindPropertyRelativeOrThrow("_inverse").floatValue = value._inverse; + } + + private static string GetKey(SerializedProperty array, int index) { + return array.GetArrayElementAtIndex(index).stringValue; + } + + private static string SetKeyEnsureUnique(SerializedProperty array, int index, string value) { + HashSet keys = new HashSet(); + for (int i = 0; i < array.arraySize; ++i) { + if (i == index) + continue; + keys.Add(GetKey(array, i)); + } + while (keys.Contains(value)) { + value += "X"; + } + array.GetArrayElementAtIndex(index).stringValue = value; + return value; + } + + private static Accuracy GetValue(SerializedProperty array, int index) { + var elem = array.GetArrayElementAtIndex(index); + return new Accuracy() { + _value = elem.FindPropertyRelativeOrThrow("_value").floatValue, + _inverse = elem.FindPropertyRelativeOrThrow("_inverse").floatValue, + }; + } + + const int ELEMENT_HEIGHT = 20; + const int ELEMENT_INNER = 18; + const int BOX_PADDING = 14; + const int BUTTON_TOPPAD = 2; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + var coreKeys = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.coreKeys)); + var tags = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.tags)); + if (property.isExpanded) { + return ELEMENT_HEIGHT + ELEMENT_HEIGHT + ((tags.arraySize + coreKeys.arraySize) * ELEMENT_HEIGHT) + (BOX_PADDING * 2) + BUTTON_TOPPAD + ELEMENT_HEIGHT; + } else { + return base.GetPropertyHeight(property, label); + } + } + + public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { + + EditorGUI.BeginProperty(r, label, property); + // Indented to make room for the inline help icon + property.isExpanded = EditorGUI.Foldout(new Rect(r) { xMin = r.xMin, height = ELEMENT_INNER }, property.isExpanded, label); + + if (property.isExpanded) { + + float y = r.yMin + ELEMENT_HEIGHT; + + GUI.Box(new Rect(r) { yMin = y }, "", EditorStyles.helpBox); // FusionGUIStyles.GroupBoxType.Sand.GetStyle()); + + r = new Rect(r) { xMin = r.xMin + BOX_PADDING, xMax = r.xMax - BOX_PADDING, yMin = y + BOX_PADDING, height = ELEMENT_INNER }; + + const float LABEL_WIDTH = 120; + float labelleft = r.xMin; + float resetbtnwidth = 24; + float valueleft = r.xMin + LABEL_WIDTH; + float valuewidth = r.width - LABEL_WIDTH - resetbtnwidth - 2; + + EditorGUI.BeginChangeCheck(); + { + + GUI.Label(r, "Tag:"); + GUI.Label(new Rect(r) { x = valueleft }, "Accuracy:"); + + r.y += ELEMENT_HEIGHT; + + var settings = NetworkProjectConfig.Global.AccuracyDefaults; + + var serializedObject = property.serializedObject; + + const float MIN = .0000008f; + const float MAX = 10f; + const float ZERO = .000001f; + + var coreDefs = settings.coreDefs; + + var coreKeys = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.coreKeys)); + var coreVals = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.coreVals)); + + var tags = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.tags)); + var values = property.FindPropertyRelativeOrThrow(nameof(AccuracyDefaults.values)); + + // Fixed named items (Core) + for (int i = 0; i < AccuracyDefaults.CORE_COUNT; ++i) { + + string key = GetKey(coreKeys, i); + Accuracy val = GetValue(coreVals, i); + + string tooltip = "hash: " + key.GetHashDeterministic(); + GUI.Label(new Rect(r) { width = LABEL_WIDTH - 2 }, new GUIContent(key, tooltip), new GUIStyle("Label") { fontStyle = FontStyle.Italic }); + + EditorGUI.BeginDisabledGroup(i == 0); + { + float newVal = CustomSliders.Log10Slider(new Rect(r) { x = valueleft, width = valuewidth }, val.Value, null, MIN, MAX, ZERO, 1); + + // Button - Reset to default + if (GUI.Button(new Rect(r) { xMin = r.xMax - resetbtnwidth}, EditorGUIUtility.FindTexture("d_RotateTool"))) { + SetValue(coreVals, i, coreDefs[i]._value); + } + if (val._value != newVal) { + SetValue(coreVals, i, newVal); + } + } + EditorGUI.EndDisabledGroup(); + + r.y += ELEMENT_HEIGHT; + } + + //User Editable list items + for (int i = 0, cnt = tags.arraySize; i < cnt; ++i) { + + string key = GetKey(tags, i); + float val = GetValue(values, i)._value; + + string tooltip = "hash: " + key.GetHashDeterministic(); + GUI.Label(r, new GUIContent(key, tooltip)); + string newKey = GUI.TextField(new Rect(r) { width = LABEL_WIDTH - 4 }, key); + + float newVal = CustomSliders.Log10Slider(new Rect(r) { x = valueleft, width = valuewidth }, val, null, MIN, MAX, ZERO, 1); + + if (GUI.Button(new Rect(r) { xMin = r.xMax - resetbtnwidth }, "X")) { + values.DeleteArrayElementAtIndex(i); + tags.DeleteArrayElementAtIndex(i); + ClearTagCache(); + break; + } + + if (key != newKey) { + SetKeyEnsureUnique(tags, i, newKey); + ClearTagCache(); + } + + if (val != newVal) { + SetValue(values, i, newVal); + } + r.y += ELEMENT_HEIGHT; + } + + r.y += BUTTON_TOPPAD; + + if (GUI.Button(new Rect(r) { xMin = r.xMin}, "Add New")) { + tags.InsertArrayElementAtIndex(tags.arraySize); + values.InsertArrayElementAtIndex(values.arraySize); + + SetKeyEnsureUnique(tags, tags.arraySize - 1, "UserDefined"); + SetValue(values, values.arraySize - 1, AccuracyDefaults.DEFAULT_ACCURACY); + ClearTagCache(); + } + } + + if (EditorGUI.EndChangeCheck()) { + property.serializedObject.ApplyModifiedProperties(); + property.serializedObject.Update(); + } + } + + EditorGUI.EndProperty(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AccuracyDefaultsDrawGUI.cs + +// deleted + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AccuracyDrawer.cs + +namespace Fusion.Editor { + + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(Accuracy), true)] + public class AccuracyDrawer : PropertyDrawer, IFoldablePropertyDrawer { + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + OnGUIExtended(position, property, label, new AccuracyRangeAttribute(AccuracyRangePreset.Defaults)); + } + + /// + /// OnGui handling for custom fusion types that use a custom AccuracyRangeAttribute slider. + /// + internal static void OnGUIExtended(Rect r, SerializedProperty property, GUIContent label, AccuracyRangeAttribute range) { + + EditorGUI.BeginProperty(r, label, property); + + var value = property.FindPropertyRelative(nameof(Accuracy._value)); + var inverse = property.FindPropertyRelative(nameof(Accuracy._inverse)); + var hash = property.FindPropertyRelative(nameof(Accuracy._hash)); + + if (value == null || inverse == null) { + EditorGUI.PropertyField(r, property, label); + + Debug.LogWarning($"AccuracyAttribute and AccuracyRangeAttribute can only be used on Accuracy types. {property.serializedObject.targetObject.name}:{property.name} is a {property.type}"); + return; + } + + float min = range.min; + float max = range.max; + float labelWidth = EditorGUIUtility.labelWidth; + const int CHECK_WIDTH = 18; + + GUI.Label(r, label); + + Rect toggleRect = new Rect(r) { xMin = r.xMin + labelWidth + 2, width = 16 }; + const string globalsTooltip = "Toggles between custom accuracy value, and using a defined Global Accuracy (found in " + nameof(NetworkProjectConfig) + ")."; + EditorGUI.LabelField(r /*toggleRect*/, new GUIContent("", globalsTooltip)); + bool useGlobals = GUI.Toggle(toggleRect, inverse.floatValue == 0, GUIContent.none); + + // To spare some memory, a toggle bool isn't used. Instead the Inverse value being zero indicates usage of the hash/global setting. + if (useGlobals != (inverse.floatValue == 0)) { + if (useGlobals) { + inverse.floatValue = 0; + + if (hash.intValue == 0) { + hash.intValue = AccuracyDefaults.ZeroHashRemap; + property.serializedObject.ApplyModifiedProperties(); + } + } else { + inverse.floatValue = 1f / value.floatValue; + } + } + + // Slider and Field + + if (useGlobals) { + var newval = DrawDroplist(new Rect(r) { xMin = r.xMin + labelWidth + CHECK_WIDTH }, hash.intValue, property.serializedObject.targetObject); + + if (hash.intValue != newval) { + hash.intValue = newval; + property.serializedObject.ApplyModifiedProperties(); + } + } + else { + + // if linear, convert base10, and hand draw the slider (since its internal values should never be seen) + if (range.logarithmic) { + EditorGUI.BeginChangeCheck(); + value.floatValue = CustomSliders.Log10Slider(r, value.floatValue, GUIContent.none, (min * .9f), max, min, range.places, CHECK_WIDTH); + if (EditorGUI.EndChangeCheck()) { + inverse.floatValue = 1 / value.floatValue; + property.serializedObject.ApplyModifiedProperties(); + } + } + // Non-linear, just keep this simple. + else { + + EditorGUI.BeginChangeCheck(); + + EditorGUI.Slider(new Rect(r) { xMin = r.xMin + labelWidth + 4 + CHECK_WIDTH }, value, max, 0, GUIContent.none); + + if (EditorGUI.EndChangeCheck()) { + + if (value.floatValue < min) { + value.floatValue = 0; + inverse.floatValue = float.PositiveInfinity; + } else { + value.floatValue = CustomSliders.RoundAndClamp(value.floatValue, min, max, range.places); + inverse.floatValue = 1 / value.floatValue; + } + + property.serializedObject.ApplyModifiedProperties(); + } + } + } + EditorGUI.EndProperty(); + } + + + public static int DrawDroplist(Rect r, int hash, UnityEngine.Object target) { + + const float VAL_WIDTH = 50; + + if (hash == 0) { + hash = AccuracyDefaults.ZeroHashRemap; + } + + bool success = AccuracyDefaultsDrawer.TagLookup.TryGetValue(hash, out (int popupindex, string) tag); + + var hold = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + var selected = EditorGUI.Popup(new Rect(r) { xMax = r.xMax - VAL_WIDTH }, success ? tag.popupindex : -1, AccuracyDefaultsDrawer.TagNames); + EditorGUI.indentLevel = hold; + + GUIStyle valStyle = new GUIStyle("MiniLabel") { alignment = TextAnchor.MiddleLeft, fontStyle = FontStyle.Italic }; + + var accuracy = GetAccuracyFromHash(hash, target); + + float val = accuracy.Value; + // Round the value to fit the label + val = (val > 1) ? (float)System.Math.Round(val, 3) : (float)System.Math.Round(val, 4); + + if (GUI.Button(new Rect(r) { xMin = r.xMax - VAL_WIDTH }, val.ToString(), valStyle)) { + NetworkProjectConfigUtilities.PingGlobalConfigAsset(); + } + + if (selected == -1) { + GUI.Label(new Rect(r) { width = 16 }, FusionGUIStyles.ErrorIcon); + EditorGUI.BeginDisabledGroup(true); + GUI.Label(new Rect(r) { xMin = r.xMin + 20, xMax = r.xMax - VAL_WIDTH - 24 }, new GUIContent("Missing Tag: " + hash.ToString(), "The previously selected tag no longer exists in Accuracy Defaults"), EditorStyles.miniLabel); + EditorGUI.EndDisabledGroup(); + } + return selected == -1 ? hash : AccuracyDefaultsDrawer.TagNames[selected].GetHashDeterministic(); + } + + static System.Collections.Generic.HashSet _beenWarnedOnce; + + static Accuracy GetAccuracyFromHash(int hash, UnityEngine.Object target) { + bool found = NetworkProjectConfig.Global.AccuracyDefaults.TryGetAccuracy(hash, out Accuracy accuracy); + if (found == false) { + // Warn of an invalid hash, but only once to avoid log spam. + if (_beenWarnedOnce == null) { + _beenWarnedOnce = new System.Collections.Generic.HashSet(); + } + if (_beenWarnedOnce.Contains(hash) == false) { + _beenWarnedOnce.Add(hash); + Debug.LogWarning($"GameObject: '{target.name}' - Accuracy for hash '{hash}' was not found in {nameof(AccuracyDefaults)}.{nameof(AccuracyDefaults.Lookup)}. " + + $"Make sure a matching entry exists in {nameof(AccuracyDefaults)} in the {nameof(NetworkProjectConfig)}. " + + $"A user defined entry in {nameof(NetworkProjectConfig)} > Accuracy Defaults may have been deleted, or was lost due to a {nameof(NetworkProjectConfig)} reset. " + + $"Default value will be used until this is corrected."); + } + } + + return accuracy; + } + + bool IFoldablePropertyDrawer.HasFoldout(SerializedProperty property) { + return false; + } + } +} + + + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AccuracyRangeDrawer.cs + +namespace Fusion.Editor { + + using System; + using UnityEngine; + using UnityEditor; + + [CustomPropertyDrawer(typeof(AccuracyRangeAttribute))] + public class AccuracyRangeDrawer : PropertyDrawer { + + public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { + + EditorGUI.BeginProperty(r, label, property); + + AccuracyRangeAttribute range = (AccuracyRangeAttribute)attribute; + + AccuracyDrawer.OnGUIExtended(r, property, label, range); + + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AssemblyNameAttributeDrawer.cs + +// --------------------------------------------------------------------------------------------- +// PhotonNetwork Framework for Unity - Copyright (C) 2020 Exit Games GmbH +// developer@exitgames.com +// --------------------------------------------------------------------------------------------- + +namespace Fusion.Editor { + using UnityEngine; + using UnityEditor; + using UnityEditor.SceneManagement; + using System.Linq; + using System.IO; + using System; + using System.Collections.Generic; + + [CanEditMultipleObjects] + [CustomPropertyDrawer(typeof(AssemblyNameAttribute))] + public class AssemblyNameAttributeDrawer : PropertyDrawerWithErrorHandling { + + const float DropdownWidth = 20.0f; + + static GUIContent DropdownContent = new GUIContent(""); + + string _lastCheckedAssemblyName; + + [Flags] + enum AsmDefType { + Predefined = 1 << 0, + InPackages = 1 << 1, + InAssets = 1 << 2, + Editor = 1 << 3, + Runtime = 1 << 4, + All = Predefined | InPackages | InAssets | Editor | Runtime, + } + + HashSet _allAssemblies; + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var assemblyName = property.stringValue; + if (!string.IsNullOrEmpty(assemblyName)) { + if (_allAssemblies == null) { + _allAssemblies = new HashSet(GetAssemblies(AsmDefType.All), StringComparer.OrdinalIgnoreCase); + } + if (!_allAssemblies.Contains(assemblyName, StringComparer.OrdinalIgnoreCase)) { + SetError($"Assembly not found: {assemblyName}"); + } + } + + using (new FusionEditorGUI.PropertyScope(position, label, property)) { + EditorGUI.BeginChangeCheck(); + + assemblyName = EditorGUI.TextField(new Rect(position) { xMax = position.xMax - DropdownWidth }, label, assemblyName); + + var dropdownRect = EditorGUI.IndentedRect(new Rect(position) { xMin = position.xMax - DropdownWidth }); + + if (EditorGUI.DropdownButton(dropdownRect, DropdownContent, FocusType.Passive)) { + + GenericMenu.MenuFunction2 onClicked = (userData) => { + property.stringValue = (string)userData; + property.serializedObject.ApplyModifiedProperties(); + UnityInternal.EditorGUI.EndEditingActiveTextField(); + ClearError(property); + }; + + var menu = new GenericMenu(); + + foreach (var (flag, prefix) in new[] { (AsmDefType.Editor, "Editor/"), (AsmDefType.Runtime, "") }) { + + if (menu.GetItemCount() != 0) { + menu.AddSeparator(prefix); + } + + foreach (var name in GetAssemblies(flag | AsmDefType.InPackages)) { + menu.AddItem(new GUIContent($"{prefix}Packages/{name}"), string.Equals(name, assemblyName, StringComparison.OrdinalIgnoreCase), onClicked, name); + } + + menu.AddSeparator(prefix); + + foreach (var name in GetAssemblies(flag | AsmDefType.InAssets | AsmDefType.Predefined)) { + menu.AddItem(new GUIContent($"{prefix}{name}"), string.Equals(name, assemblyName, StringComparison.OrdinalIgnoreCase), onClicked, name); + } + } + + menu.DropDown(dropdownRect); + } + + if (EditorGUI.EndChangeCheck()) { + property.stringValue = assemblyName; + property.serializedObject.ApplyModifiedProperties(); + base.ClearError(); + } + } + } + + static IEnumerable GetAssemblies(AsmDefType types) { + IEnumerable query = Enumerable.Empty(); + + if (types.HasFlag(AsmDefType.Predefined)) { + if (types.HasFlag(AsmDefType.Runtime)) { + query = query.Concat(new[] { "Assembly-CSharp-firstpass", "Assembly-CSharp" }); + } + if (types.HasFlag(AsmDefType.Editor)) { + query = query.Concat(new[] { "Assembly-CSharp-Editor-firstpass", "Assembly-CSharp-Editor" }); + } + } + + if (types.HasFlag(AsmDefType.InAssets) || types.HasFlag(AsmDefType.InPackages)) { + query = query.Concat( + AssetDatabase.FindAssets("t:asmdef") + .Select(x => AssetDatabase.GUIDToAssetPath(x)) + .Where(x => { + if (types.HasFlag(AsmDefType.InAssets) && x.StartsWith("Assets/")) { + return true; + } else if (types.HasFlag(AsmDefType.InPackages) && x.StartsWith("Packages/")) { + return true; + } else { + return false; + } + }) + .Select(x => JsonUtility.FromJson(File.ReadAllText(x))) + .Where(x => { + bool editorOnly = x.includePlatforms.Length == 1 && x.includePlatforms[0] == "Editor"; + if (types.HasFlag(AsmDefType.Runtime) && !editorOnly) { + return true; + } else if (types.HasFlag(AsmDefType.Editor) && editorOnly) { + return true; + } else { + return false; + } + }) + .Select(x => x.name) + .Distinct() + ); + } + + return query; + } + + [Serializable] + private class AsmDefData { + public string[] includePlatforms = Array.Empty(); + public string name = string.Empty; + } + } + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/AutoGUIAttributeDrawer.cs + +// Removed May 22 2021 (Alpha 3) + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/CastEnumAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(CastEnumAttribute))] + public class CastEnumAttributeDrawer : PropertyDrawer { + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + EditorGUI.BeginProperty(position, label, property); + + var attr = attribute as CastEnumAttribute; + var castToType = attr.CastToType; + + // get cast type using property if a type was not given. + if (castToType == null) { + var methodname = attr.GetTypeMethodName; + var instance = property.serializedObject.targetObject; + var compType = instance.GetType(); + var propInfo = compType.GetProperty(methodname, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + if (propInfo == null) { + Debug.LogWarning($"Invalid property supplied to {nameof(CastEnumAttribute)} in class '{compType.Name}' on field {property.name}"); + } else { + if (propInfo.PropertyType == typeof(Type)) { + castToType = (Type)propInfo.GetValue(instance); + } else { + Debug.LogWarning($"Property supplied to {nameof(CastEnumAttribute)} in class '{compType.Name}' on field '{property.name}' does not evaluate to an Enum type."); + } + } + } + + if (castToType != null){ + + // Only cast if the Type supplied is an enum type. + if (castToType.IsEnum) { + var converted = (Enum)Enum.ToObject(castToType, property.intValue); + EditorGUI.BeginChangeCheck(); + { + property.intValue = Convert.ToInt32(EditorGUI.EnumPopup(position, label, converted)); + } + if (EditorGUI.EndChangeCheck()) { + property.serializedObject.ApplyModifiedProperties(); + } + + } else { + Debug.LogWarning($"Type supplied to {nameof(CastEnumAttribute)} in class '{property.serializedObject.targetObject.GetType().Name}' on field '{property.name}' does not evaluate to an Enum type."); + EditorGUI.PropertyField(position, property, label); + } + + + } else { + EditorGUI.PropertyField(position, property, label); + } + + EditorGUI.EndProperty(); + } + } + +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/DecoratingPropertyAttributeDrawerBase.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + public abstract class DecoratingPropertyAttributeDrawerBase : PropertyDrawer, IFoldablePropertyDrawer { + private PropertyDrawer _actualDrawer; + private bool _initialized; + + protected PropertyDrawer NextPropertyDrawer { + get; private set; + } + + protected DecoratingPropertyAttributeDrawerBase() { + } + + protected DecoratingPropertyAttributeDrawerBase(PropertyDrawer actualDrawer) { + NextPropertyDrawer = _actualDrawer = actualDrawer; + _initialized = true; + } + + public override bool CanCacheInspectorGUI(SerializedProperty property) { + EnsureInitialized(property); + return base.CanCacheInspectorGUI(property); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + EnsureInitialized(property); + return (_actualDrawer?.GetPropertyHeight(property, label) ?? EditorGUI.GetPropertyHeight(property, label)); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + EnsureInitialized(property); + if (_actualDrawer != null) { + _actualDrawer.OnGUI(position, property, label); + } else { + FusionEditorGUI.ForwardPropertyField(position, property, label, property.IsArrayProperty() ? true : property.isExpanded); + } + } + private void EnsureInitialized(SerializedProperty property) { + if (_initialized) { + return; + } + + _initialized = true; + + if (fieldInfo == null) { + return; + } + NextPropertyDrawer = _actualDrawer = GetNextPropertyDrawer(fieldInfo, UnityInternal.ScriptAttributeUtility.GetFieldAttributes(fieldInfo), attribute); + } + + internal static PropertyDrawer GetNextPropertyDrawer(FieldInfo targetField, List fieldAttributes, PropertyAttribute currentAttribute) { + + // now check if we're hiding the default type drawer + int index = fieldAttributes?.IndexOf(currentAttribute) ?? -1; + + PropertyDrawer result = null; + + // the last one needs to redirect to the default drawer + if (index < 0 || index == fieldAttributes.Count - 1) { + var drawerType = UnityInternal.ScriptAttributeUtility.GetDrawerTypeForType(targetField.FieldType); + if (drawerType != null) { + FusionEditorLog.TraceInspector($"Chained type drawer ({drawerType}) for {targetField.DeclaringType.FullName}.{targetField.Name}"); + result = (PropertyDrawer)Activator.CreateInstance(drawerType); + UnityInternal.PropertyDrawer.SetFieldInfo(result, targetField); + } + } else { +#if !UNITY_2021_1_OR_NEWER + // need to chain with the next drawer + for (int i = index + 1; i < fieldAttributes.Count; ++i) { + var drawerType = UnityInternal.ScriptAttributeUtility.GetDrawerTypeForType(fieldAttributes[i].GetType()); + if (drawerType?.IsSubclassOf(typeof(PropertyDrawer)) != true) { + continue; + } + + FusionEditorLog.TraceInspector($"Chained attribute drawer ({drawerType}) for {targetField.DeclaringType.FullName}.{targetField.Name}"); + result = (PropertyDrawer)Activator.CreateInstance(drawerType); + UnityInternal.PropertyDrawer.SetFieldInfo(result, targetField); + UnityInternal.PropertyDrawer.SetAttribute(result, fieldAttributes[i]); + break; + } +#endif + } + + return result; + } + + public void Chain(PropertyDrawer injectedDrawer) { + if (_actualDrawer != null) { + if (injectedDrawer is DecoratingPropertyAttributeDrawerBase decorating) { + decorating.Chain(_actualDrawer); + } else { + throw new InvalidOperationException(); + } + } else { + _actualDrawer = injectedDrawer; + } + _initialized = true; + } + + public bool HasFoldout(SerializedProperty property) { + if (NextPropertyDrawer is IFoldablePropertyDrawer next) { + return next.HasFoldout(property); + } + if (property.IsArrayProperty()) { + return true; + } + if (property.propertyType == SerializedPropertyType.Generic) { + return true; + } + return false; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/DoIfAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEngine; + + public abstract class DoIfAttributeDrawer : DecoratingPropertyAttributeDrawerBase { + + protected object realTargetObject; + + protected double GetCompareValue(SerializedProperty property, string conditionMember, FieldInfo finfo) { + + if (conditionMember == null || conditionMember == "") { + Debug.LogWarning("Invalid Condition."); + } + + var condDelegate = finfo.DeclaringType.GetDelegateFromMember(conditionMember); + if (condDelegate == null) + return 0; + + if (realTargetObject == null) + realTargetObject = property.GetParent(); + + var valObj = condDelegate(realTargetObject); + return valObj == null ? 0 : valObj.GetObjectValueAsDouble(); + } + + public static bool CheckDraw(DoIfAttribute warnIf, double referenceValue) { + + switch (warnIf.Compare) { + case DoIfCompareOperator.Equal: return referenceValue.Equals(warnIf.CompareToValue); + case DoIfCompareOperator.NotEqual: return !referenceValue.Equals(warnIf.CompareToValue); + case DoIfCompareOperator.Less: return referenceValue < warnIf.CompareToValue; + case DoIfCompareOperator.LessOrEqual: return referenceValue <= warnIf.CompareToValue; + case DoIfCompareOperator.GreaterOrEqual: return referenceValue >= warnIf.CompareToValue; + case DoIfCompareOperator.Greater: return referenceValue > warnIf.CompareToValue; + } + return false; + } + + public static bool CheckDraw(WarnIfAttribute warnIf, double referenceValue, double compareToValue) { + + switch (warnIf.Compare) { + case DoIfCompareOperator.Equal: return referenceValue.Equals(compareToValue); + case DoIfCompareOperator.NotEqual: return !referenceValue.Equals(compareToValue); + case DoIfCompareOperator.Less: return referenceValue < compareToValue; + case DoIfCompareOperator.LessOrEqual: return referenceValue <= compareToValue; + case DoIfCompareOperator.GreaterOrEqual: return referenceValue >= compareToValue; + case DoIfCompareOperator.Greater: return referenceValue > compareToValue; + } + return false; + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/DrawIfAttributeDrawer.cs + +namespace Fusion.Editor { + + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(DrawIfAttribute))] + public class DrawIfAttributeDrawer : DoIfAttributeDrawer { + public DrawIfAttribute Attribute => (DrawIfAttribute)attribute; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + + double otherValue = GetCompareValue(property, Attribute.ConditionMember, fieldInfo); + + if (Attribute.Hide == DrawIfHideType.ReadOnly || CheckDraw(Attribute, otherValue)) { + return base.GetPropertyHeight(property, label); + } + + // -2 is required rather than zero, otherwise a space is added for hidden fields. + return -2; + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + double otherValue = GetCompareValue(property, Attribute.ConditionMember, fieldInfo); + + var readOnly = Attribute.Hide == DrawIfHideType.ReadOnly; + var draw = CheckDraw(Attribute, otherValue); + + if (readOnly || draw) { + EditorGUI.BeginDisabledGroup(!draw); + + base.OnGUI(position, property, label); + + EditorGUI.EndDisabledGroup(); + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/EditorDisabledAttributeDrawer.cs + +namespace Fusion.Editor { + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(EditorDisabledAttribute))] + public class EditorDisabledDecoratorDrawer : PropertyDrawer { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + var attr = attribute as EditorDisabledAttribute; + if (attr.HideInRelease && NetworkRunner.BuildType == NetworkRunner.BuildTypes.Release) + return; + + try { + GUI.enabled = false; + EditorGUI.PropertyField(position, property, label, true); + } finally { + GUI.enabled = true; + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + + // If we are hiding this property, need to use negative height to erase the margin with a -2 + var attr = attribute as EditorDisabledAttribute; + if (attr.HideInRelease && NetworkRunner.BuildType == NetworkRunner.BuildTypes.Release) + return -2; + + return EditorGUI.GetPropertyHeight(property); + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/EnumMaskAttributeDrawer.cs + +namespace Fusion.Editor { + + using UnityEngine; + using System; + using UnityEditor; + + [CustomPropertyDrawer(typeof(EnumMaskAttribute))] + public class EnumMaskAttributeDrawer : PropertyDrawer { + public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { + string[] names; + var maskattr = attribute as EnumMaskAttribute; + if (maskattr.castTo != null) + names = Enum.GetNames(maskattr.castTo); + else + names = property.enumDisplayNames; + + if (maskattr.definesZero) { + string[] truncated = new string[names.Length - 1]; + Array.Copy(names, 1, truncated, 0, truncated.Length); + names = truncated; + } + + //_property.intValue = System.Convert.ToInt32(EditorGUI.EnumMaskPopup(_position, _label, (SendCullMask)_property.intValue)); + property.intValue = EditorGUI.MaskField(r, label, property.intValue, names); + + } + } +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/FixedBufferPropertyAttributeDrawer.cs + +namespace Fusion.Editor { + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using Fusion.Internal; + using Unity.Collections.LowLevel.Unsafe; + using UnityEditor; + using UnityEditor.Compilation; + using UnityEngine; + + [CustomPropertyDrawer(typeof(FixedBufferPropertyAttribute))] + public unsafe class FixedBufferPropertyAttributeDrawer : PropertyDrawerWithErrorHandling { + public const string FixedBufferFieldName = "Data"; + public const string WrapperSurrogateDataPath = "Surrogate.Data"; + + private const float SpacingSubLabel = 2; + private static readonly int _multiFieldPrefixId = "MultiFieldPrefixId".GetHashCode(); + private static int[] _buffer = Array.Empty(); + + private static SurrogatePool _pool = new SurrogatePool(); + private static GUIContent[] _vectorProperties = new[] { + new GUIContent("X"), + new GUIContent("Y"), + new GUIContent("Z"), + new GUIContent("W"), + }; + + private Dictionary _needsSurrogateCache = new Dictionary(); + private Dictionary _optimisedReaderWriters = new Dictionary(); + + private Type ActualFieldType => ((FixedBufferPropertyAttribute)attribute).Type; + private int Capacity => ((FixedBufferPropertyAttribute)attribute).Capacity; + private Type SurrogateType => ((FixedBufferPropertyAttribute)attribute).SurrogateType; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + if (SurrogateType == null) { + return EditorGUIUtility.singleLineHeight; + } + + if (NeedsSurrogate(property)) { + var fixedBufferProperty = GetFixedBufferProperty(property); + var firstElement = fixedBufferProperty.GetFixedBufferElementAtIndex(0); + if (!firstElement.IsArrayElement()) { + // it seems that with multiple seclection child elements are not accessible + Debug.Assert(property.serializedObject.targetObjects.Length > 1); + return EditorGUIUtility.singleLineHeight; + } + + var wrapper = _pool.Acquire(fieldInfo, attribute, property, SurrogateType); + + if (wrapper.Drawer != null) { + return wrapper.Drawer.GetPropertyHeight(wrapper.Property, label); + } else { + return EditorGUI.GetPropertyHeight(wrapper.Property); + } + } else { + int count = 1; + if (!EditorGUIUtility.wideMode) { + count++; + } + return count * (EditorGUIUtility.singleLineHeight) + (count - 1) * EditorGUIUtility.standardVerticalSpacing; + } + } + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + if (NeedsSurrogate(property)) { + if (SurrogateType == null) { + this.SetInfo($"[Networked] properties of type {ActualFieldType.FullName} in structs are not yet supported"); + EditorGUI.LabelField(position, label, GUIContent.none); + } else { + int capacity = Capacity; + var fixedBufferProperty = GetFixedBufferProperty(property); + + Array.Resize(ref _buffer, Math.Max(_buffer.Length, fixedBufferProperty.fixedBufferSize)); + + var firstElement = fixedBufferProperty.GetFixedBufferElementAtIndex(0); + if (!firstElement.IsArrayElement()) { + Debug.Assert(property.serializedObject.targetObjects.Length > 1); + SetInfo($"Type does not support multi-edit"); + EditorGUI.LabelField(position, label); + } else { + var wrapper = _pool.Acquire(fieldInfo, attribute, property, SurrogateType); + + { + bool surrogateOutdated = false; + var targetObjects = property.serializedObject.targetObjects; + if (targetObjects.Length > 1) { + for (int i = 0; i < targetObjects.Length; ++i) { + using (var so = new SerializedObject(targetObjects[i])) { + using (var sp = so.FindPropertyOrThrow($"{property.propertyPath}.Data")) { + if (UpdateSurrogateFromFixedBuffer(sp, wrapper.Surrogates[i], false, _pool.Flush)) { + surrogateOutdated = true; + } + } + } + } + + if (surrogateOutdated) { + // it seems that a mere Update won't do here + wrapper.Property = new SerializedObject(wrapper.Wrappers).FindPropertyOrThrow(WrapperSurrogateDataPath); + } + } else { + // an optimised path, no alloc needed + Debug.Assert(wrapper.Surrogates.Length == 1); + if (UpdateSurrogateFromFixedBuffer(fixedBufferProperty, wrapper.Surrogates[0], false, _pool.Flush)) { + wrapper.Property.serializedObject.Update(); + } + } + } + + // check if there has been any chagnes + EditorGUI.BeginChangeCheck(); + EditorGUI.BeginProperty(position, label, property); + + if (wrapper.Drawer != null) { + wrapper.Drawer.OnGUI(position, wrapper.Property, label); + } else { + EditorGUI.PropertyField(position, wrapper.Property, label, true); + } + + EditorGUI.EndProperty(); + if (EditorGUI.EndChangeCheck()) { + wrapper.Property.serializedObject.ApplyModifiedProperties(); + + // when not having multiple different values, just write the whole thing + if (UpdateSurrogateFromFixedBuffer(fixedBufferProperty, wrapper.Surrogates[0], true, !fixedBufferProperty.hasMultipleDifferentValues)) { + fixedBufferProperty.serializedObject.ApplyModifiedProperties(); + + // refresh? + wrapper.Property.serializedObject.Update(); + } + } + } + } + } else { + if (!_optimisedReaderWriters.TryGetValue(SurrogateType, out var surrogate)) { + surrogate = (UnitySurrogateBase)Activator.CreateInstance(SurrogateType); + _optimisedReaderWriters.Add(SurrogateType, surrogate); + } + + if (ActualFieldType == typeof(float)) { + DoFloatField(position, property, label, (IUnityValueSurrogate)surrogate); + } else if (ActualFieldType == typeof(Vector2)) { + DoFloatVectorProperty(position, property, label, 2, (IUnityValueSurrogate)surrogate); + } else if (ActualFieldType == typeof(Vector3)) { + DoFloatVectorProperty(position, property, label, 3, (IUnityValueSurrogate)surrogate); + } else if (ActualFieldType == typeof(Vector4)) { + DoFloatVectorProperty(position, property, label, 4, (IUnityValueSurrogate)surrogate); + } + } + } + + private void DoFloatField(Rect position, SerializedProperty property, GUIContent label, IUnityValueSurrogate surrogate) { + var fixedBuffer = GetFixedBufferProperty(property); + Debug.Assert(1 == fixedBuffer.fixedBufferSize); + + var valueProp = fixedBuffer.GetFixedBufferElementAtIndex(0); + int value = valueProp.intValue; + surrogate.Read(&value, 1); + + EditorGUI.BeginProperty(position, label, property); + EditorGUI.BeginChangeCheck(); + surrogate.Data = EditorGUI.FloatField(position, label, surrogate.Data); + if (EditorGUI.EndChangeCheck()) { + surrogate.Write(&value, 1); + valueProp.intValue = value; + property.serializedObject.ApplyModifiedProperties(); + } + EditorGUI.EndProperty(); + } + + private unsafe void DoFloatVectorProperty(Rect position, SerializedProperty property, GUIContent label, int count, IUnityValueSurrogate readerWriter) where T : unmanaged { + EditorGUI.BeginProperty(position, label, property); + try { + var fixedBuffer = GetFixedBufferProperty(property); + Debug.Assert(count == fixedBuffer.fixedBufferSize); + + int* raw = stackalloc int[count]; + for (int i = 0; i < count; ++i) { + raw[i] = fixedBuffer.GetFixedBufferElementAtIndex(i).intValue; + } + + readerWriter.Read(raw, 1); + + int changed = 0; + + var data = readerWriter.Data; + float* pdata = (float*)&data; + + int id = GUIUtility.GetControlID(_multiFieldPrefixId, FocusType.Keyboard, position); + position = UnityInternal.EditorGUI.MultiFieldPrefixLabel(position, id, label, count); + if (position.width > 1) { + using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) { + float w = (position.width - (count - 1) * SpacingSubLabel) / count; + var nestedPosition = new Rect(position) { width = w }; + + for (int i = 0; i < count; ++i) { + var propLabel = _vectorProperties[i]; + float prefixWidth = EditorStyles.label.CalcSize(propLabel).x; + using (new FusionEditorGUI.LabelWidthScope(prefixWidth)) { + EditorGUI.BeginChangeCheck(); + var newValue = propLabel == null ? EditorGUI.FloatField(nestedPosition, pdata[i]) : EditorGUI.FloatField(nestedPosition, propLabel, pdata[i]); + if (EditorGUI.EndChangeCheck()) { + changed |= (1 << i); + pdata[i] = newValue; + } + } + nestedPosition.x += w + SpacingSubLabel; + } + } + } + + if (changed != 0) { + readerWriter.Data = data; + readerWriter.Write(raw, 1); + + for (int i = 0; i < count; ++i) { + if ((changed & (1 << i)) != 0) { + fixedBuffer.GetFixedBufferElementAtIndex(i).intValue = raw[i]; + } + } + property.serializedObject.ApplyModifiedProperties(); + } + } finally { + EditorGUI.EndProperty(); + } + } + + private SerializedProperty GetFixedBufferProperty(SerializedProperty prop) { + var result = prop.FindPropertyRelativeOrThrow(FixedBufferFieldName); + Debug.Assert(result.isFixedBuffer); + return result; + } + + private bool NeedsSurrogate(SerializedProperty property) { + if (_needsSurrogateCache.TryGetValue(property.propertyPath, out var result)) { + return result; + } + + result = true; + if (ActualFieldType == typeof(float) || ActualFieldType == typeof(Vector2) || ActualFieldType == typeof(Vector3) || ActualFieldType == typeof(Vector4)) { + if (UnityInternal.ScriptAttributeUtility.GetFieldAttributes(fieldInfo).Count == 0) { + // fast drawers do not support any additional attributes + result = false; + } + } + + _needsSurrogateCache.Add(property.propertyPath, result); + return result; + } + + private bool UpdateSurrogateFromFixedBuffer(SerializedProperty sp, UnitySurrogateBase surrogate, bool write, bool force) { + int count = sp.fixedBufferSize; + Array.Resize(ref _buffer, Math.Max(_buffer.Length, count)); + + // need to get to the first property... `GetFixedBufferElementAtIndex` is slow and allocs + + var element = sp.Copy(); + element.Next(true); // .Array + element.Next(true); // .Array.size + element.Next(true); // .Array.data[0] + + fixed (int* p = _buffer) { + UnsafeUtility.MemClear(p, count * sizeof(int)); + surrogate.Write(p, Capacity); + + int i = 0; + if (!force) { + // find first difference + for (; i < count; ++i, element.Next(true)) { + Debug.Assert(element.propertyType == SerializedPropertyType.Integer); + if (element.intValue != p[i]) { + break; + } + } + } + + if (i < count) { + // update data + if (write) { + for (; i < count; ++i, element.Next(true)) { + element.intValue = p[i]; + } + } else { + for (; i < count; ++i, element.Next(true)) { + p[i] = element.intValue; + } + } + // update surrogate + surrogate.Read(p, Capacity); + return true; + } else { + return false; + } + } + } + + private class SurrogatePool { + + private const int MaxTTL = 10; + + private FieldInfo _surrogateField = typeof(UnitySurrogateBaseWrapper).GetField(nameof(UnitySurrogateBaseWrapper.Surrogate)); + private Dictionary<(Type, string, int), PropertyEntry> _used = new Dictionary<(Type, string, int), PropertyEntry>(); + private Dictionary> _wrappersPool = new Dictionary>(); + + public SurrogatePool() { + Undo.undoRedoPerformed += () => Flush = true; + + EditorApplication.update += () => { + Flush = false; + if (!WasUsed) { + return; + } + WasUsed = false; + + var keysToRemove = new List<(Type, string, int)>(); + + foreach (var kv in _used) { + var entry = kv.Value; + if (--entry.TTL < 0) { + // return to pool + keysToRemove.Add(kv.Key); + foreach (var wrapper in entry.Wrappers) { + _wrappersPool[wrapper.Surrogate.GetType()].Push(wrapper); + } + } + } + + // make all the wrappers available again + foreach (var key in keysToRemove) { + FusionEditorLog.TraceInspector($"Cleaning up {key}"); + _used.Remove(key); + } + }; + + CompilationPipeline.compilationFinished += obj => { + // destroy SO's, we don't want them to hold on to the surrogates + + var wrappers = _wrappersPool.Values.SelectMany(x => x) + .Concat(_used.Values.SelectMany(x => x.Wrappers)); + + foreach (var wrapper in wrappers) { + UnityEngine.Object.DestroyImmediate(wrapper); + } + }; + } + + public bool Flush { get; private set; } + + public bool WasUsed { get; private set; } + + public PropertyEntry Acquire(FieldInfo field, PropertyAttribute attribute, SerializedProperty property, Type type) { + WasUsed = true; + + var key = (type, property.propertyPath, property.serializedObject.targetObjects.Length); + if (_used.TryGetValue(key, out var entry)) { + entry.TTL = MaxTTL; + return entry; + } + + // acquire new entry + var wrappers = new UnitySurrogateBaseWrapper[key.Item3]; + if (!_wrappersPool.TryGetValue(type, out var pool)) { + pool = new Stack(); + _wrappersPool.Add(type, pool); + } + + for (int i = 0; i < wrappers.Length; ++i) { + if (pool.Count > 0) { + wrappers[i] = pool.Pop(); + } else { + FusionEditorLog.TraceInspector($"Allocating surrogate {type}"); + wrappers[i] = ScriptableObject.CreateInstance(); + } + + if (wrappers[i].SurrogateType != type) { + FusionEditorLog.TraceInspector($"Replacing type {wrappers[i].Surrogate?.GetType()} with {type}"); + wrappers[i].Surrogate = (UnitySurrogateBase)Activator.CreateInstance(type); + wrappers[i].SurrogateType = type; + } + } + + FusionEditorLog.TraceInspector($"Created entry for {property.propertyPath}"); + var attributes = UnityInternal.ScriptAttributeUtility.GetFieldAttributes(field); + entry = new PropertyEntry() { + Drawer = DecoratingPropertyAttributeDrawerBase.GetNextPropertyDrawer(_surrogateField, attributes, attribute), + Property = new SerializedObject(wrappers).FindPropertyOrThrow(WrapperSurrogateDataPath), + Surrogates = wrappers.Select(x => x.Surrogate).ToArray(), + TTL = MaxTTL, + Wrappers = wrappers + }; + + _used.Add(key, entry); + + return entry; + } + + public class PropertyEntry { + public PropertyDrawer Drawer; + public SerializedProperty Property; + public UnitySurrogateBase[] Surrogates; + public int TTL; + public UnitySurrogateBaseWrapper[] Wrappers; + } + } + + private class UnitySurrogateBaseWrapper : ScriptableObject { + [NonSerialized] + public PropertyDrawer Drawer; + [SerializeReference] + public UnitySurrogateBase Surrogate; + [NonSerialized] + public SerializedProperty SurrogateProperty; + [NonSerialized] + public Type SurrogateType; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/HitboxRootEditor.cs + +// Deleted Aug 5 2021 + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/IFoldablePropertyDrawer.cs + +namespace Fusion { + public interface IFoldablePropertyDrawer { + bool HasFoldout(UnityEditor.SerializedProperty property); + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/InlineEditorAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEditorInternal; + using UnityEngine; + + [CustomPropertyDrawer(typeof(InlineEditorAttribute))] + public class InlineEditorAttributeDrawer : PropertyDrawer { + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + bool enterChildren = true; + int parentDepth = property.depth; + + using (new FusionEditorGUI.PropertyScope(position, label, property)) { + for (var prop = property; property.NextVisible(enterChildren) && property.depth > parentDepth; enterChildren = false) { + position.height = EditorGUI.GetPropertyHeight(prop); + EditorGUI.PropertyField(position, prop); + position.y += position.height + EditorGUIUtility.standardVerticalSpacing; + } + } + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + + property.isExpanded = true; + + float result = -EditorGUIUtility.standardVerticalSpacing; + bool enterChildren = true; + int parentDepth = property.depth; + + for (var prop = property; property.NextVisible(enterChildren) && property.depth > parentDepth; enterChildren = false) { + result += EditorGUI.GetPropertyHeight(prop) + EditorGUIUtility.standardVerticalSpacing; + } + + return result; + } + } + + //[CustomPropertyDrawer(typeof(DictionaryAdapter), true)] + //public class DictionaryAdapterDrawer : PropertyDrawerWithErrorHandling { + + // private Dictionary reorderables = new Dictionary(); + + // protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + // GetOrCreateList(property).DoList(position); + // } + + // public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + // return GetOrCreateList(property).GetHeight(); + // } + + // private ReorderableList GetOrCreateList(SerializedProperty property) { + // if (reorderables.TryGetValue(property.propertyPath, out var reorderable)) { + // return reorderable; + // } + + // var entries = property.FindPropertyRelativeOrThrow("Entries"); + + // reorderable = new ReorderableList(property.serializedObject, entries); + + // reorderable.headerHeight = 0.0f; + + // reorderable.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { + // var entry = entries.GetArrayElementAtIndex(index); + // EditorGUI.PropertyField(rect, entry, true); + // }; + + // reorderable.elementHeightCallback = (int index) => { + // var entry = entries.GetArrayElementAtIndex(index); + // return EditorGUI.GetPropertyHeight(entry); + // }; + + + // reorderables.Add(property.propertyPath, reorderable); + // return reorderable; + // } + //} +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/InlineHelpAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(InlineHelpAttribute))] + internal class InlineHelpAttributeDrawer : DecoratingPropertyAttributeDrawerBase { + + internal ReserveArrayPropertyHeightDecorator ArrayHeightDecorator; + + private FusionEditorGUI.PropertyInlineHelpInfo _helpInfo; + private bool _initialized; + + public InlineHelpAttributeDrawer() { + } + + public InlineHelpAttributeDrawer(PropertyDrawer redirectedDrawer) : base(redirectedDrawer) { + } + + private new InlineHelpAttribute attribute => (InlineHelpAttribute)base.attribute; + + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + if (property.IsArrayProperty()) { + return base.GetPropertyHeight(property, label); + } + + EnsureInitialized(property); + + float height = base.GetPropertyHeight(property, label); + if (height <= 0) { + return height; + } + + if (FusionEditorGUI.IsHelpExpanded(this, property.propertyPath)) { + height += FusionEditorGUI.GetInlineHelpRect(_helpInfo.Summary).height; + } + + return height; + } + + private void SetRequiredHeight(float value) { + if (ArrayHeightDecorator != null) { + ArrayHeightDecorator.Height = value; + } + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + EnsureInitialized(property); + + if (position.height <= 0 || _helpInfo == null) { + // ignore + base.OnGUI(position, property, label); + return; + } + + if (_helpInfo.Summary == null) { + // just the tooltip + ReplaceTooltip(ref label); + base.OnGUI(position, property, label); + return; + } + + bool hasFoldout = HasFoldout(property); + if (hasFoldout && EditorGUI.indentLevel == 0 && attribute.ButtonPlacement == InlineHelpButtonPlacement.BeforeLabel) { + // help button won't fit + ReplaceTooltip(ref label); + base.OnGUI(position, property, label); + return; + } + + Rect buttonRect = FusionEditorGUI.GetInlineHelpButtonRect(attribute.ButtonPlacement, position, label, hasFoldout); + bool wasExpanded = FusionEditorGUI.IsHelpExpanded(this, property.propertyPath); + if (wasExpanded) { + var helpRect = FusionEditorGUI.GetInlineHelpRect(_helpInfo.Summary); + FusionEditorGUI.DrawInlineHelp(_helpInfo.Summary, position, helpRect); + label = new GUIContent(label); + position.height = position.height - helpRect.height; + SetRequiredHeight(helpRect.height); + } else { + SetRequiredHeight(0.0f); + } + + if (FusionEditorGUI.DrawInlineHelpButton(buttonRect, wasExpanded, doButton: true, doIcon: false)) { + FusionEditorGUI.SetHelpExpanded(this, property.propertyPath, !wasExpanded); + + if (wasExpanded) { + SetRequiredHeight(0.0f); + } else { + SetRequiredHeight(FusionEditorGUI.GetInlineHelpRect(_helpInfo.Summary).y); + } + } + + ReplaceTooltip(ref label); + base.OnGUI(position, property, label); + + // paint over what the inspector has drawn + FusionEditorGUI.DrawInlineHelpButton(buttonRect, wasExpanded, doButton: false, doIcon: true); + + // a temporary fix for icons + if (NextPropertyDrawer is PropertyDrawerWithErrorHandling next) { + next.IconOffset = buttonRect.width + 2; + } + } + + + private void EnsureInitialized(SerializedProperty property) { + if (_initialized) { + return; + } + + _initialized = true; + + if (property.IsArrayElement()) { + _helpInfo = null; + return; + } + + var rootType = property.serializedObject.targetObject.GetType(); + var type = fieldInfo?.DeclaringType ?? rootType; + + _helpInfo = type.GetInlineHelpInfo(property.name); + } + + private void ReplaceTooltip(ref GUIContent label) { + if (!string.IsNullOrEmpty(_helpInfo.Label.tooltip)) { + label = new GUIContent(label); + if (string.IsNullOrEmpty(label.tooltip)) { + label.tooltip = _helpInfo.Label.tooltip; + } else { + label.tooltip += "\n" + _helpInfo.Label.tooltip; + } + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/KeyValuePairAttributeDrawer.cs + +namespace Fusion.Editor { + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(KeyValuePairAttribute))] + public class KeyValuePairAttributeDrawer : PropertyDrawer { + + const string KeyPropertyName = "Key"; + const string ValuePropertyName = "Value"; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + var origLabel = new GUIContent(label); + var keyProperty = property.FindPropertyRelativeOrThrow(KeyPropertyName); + var keyHeight = Mathf.Max(EditorGUIUtility.singleLineHeight, EditorGUI.GetPropertyHeight(keyProperty)); + + var elementRect = position; + elementRect.height = EditorGUIUtility.singleLineHeight; + + using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) { + var keyRect = position; + keyRect.height = keyHeight; + keyRect.xMin += EditorGUIUtility.labelWidth; + EditorGUI.PropertyField(keyRect, keyProperty, GUIContent.none, true); + } + + if (EditorGUI.PropertyField(elementRect, property, origLabel, false)) { + var valueProperty = property.FindPropertyRelativeOrThrow(ValuePropertyName); + using (new EditorGUI.IndentLevelScope()) { + var valueHeight = Mathf.Max(EditorGUIUtility.singleLineHeight, EditorGUI.GetPropertyHeight(valueProperty)); + var valueRect = position; + valueRect.yMin += keyHeight + EditorGUIUtility.standardVerticalSpacing; + valueRect.height = valueHeight; + EditorGUI.PropertyField(valueRect, valueProperty, true); + } + } + + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + var keyProperty = property.FindPropertyRelativeOrThrow(KeyPropertyName); + var keyHeight = Mathf.Max(EditorGUIUtility.singleLineHeight, EditorGUI.GetPropertyHeight(keyProperty)); + + var result = keyHeight; + + if (property.isExpanded) { + var valueProperty = property.FindPropertyRelativeOrThrow(ValuePropertyName); + result += EditorGUIUtility.standardVerticalSpacing; + result += Mathf.Max(EditorGUIUtility.singleLineHeight, EditorGUI.GetPropertyHeight(valueProperty, true)); + } + + return result; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkBoolDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkBool))] + public class NetworkBoolDrawer : PropertyDrawer { + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + using (new FusionEditorGUI.PropertyScope(position, label, property)) { + var valueProperty = property.FindPropertyRelativeOrThrow("_value"); + EditorGUI.BeginChangeCheck(); + bool isChecked = EditorGUI.Toggle(position, label, valueProperty.intValue > 0); + if (EditorGUI.EndChangeCheck()) { + valueProperty.intValue = isChecked ? 1 : 0; + valueProperty.serializedObject.ApplyModifiedProperties(); + } + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkObjectGuidDrawer.cs + +namespace Fusion.Editor { + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkObjectGuid))] + public class NetworkObjectGuidDrawer : PropertyDrawerWithErrorHandling, IFoldablePropertyDrawer { + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + var guid = GetValue(property); + + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + if (!GUI.enabled) { + GUI.enabled = true; + EditorGUI.SelectableLabel(position, $"{(System.Guid)guid}"); + GUI.enabled = false; + } else { + EditorGUI.BeginChangeCheck(); + + var text = EditorGUI.TextField(position, ((System.Guid)guid).ToString()); + ClearErrorIfLostFocus(); + + if (EditorGUI.EndChangeCheck()) { + if (NetworkObjectGuid.TryParse(text, out guid)) { + SetValue(property, guid); + property.serializedObject.ApplyModifiedProperties(); + } else { + SetError($"Unable to parse {text}"); + } + } + } + } + } + + public static unsafe NetworkObjectGuid GetValue(SerializedProperty property) { + var guid = new NetworkObjectGuid(); + var prop = property.FindPropertyRelativeOrThrow(nameof(NetworkObjectGuid.RawGuidValue)); + guid.RawGuidValue[0] = prop.GetFixedBufferElementAtIndex(0).longValue; + guid.RawGuidValue[1] = prop.GetFixedBufferElementAtIndex(1).longValue; + return guid; + } + + public static unsafe void SetValue(SerializedProperty property, NetworkObjectGuid guid) { + var prop = property.FindPropertyRelativeOrThrow(nameof(NetworkObjectGuid.RawGuidValue)); + prop.GetFixedBufferElementAtIndex(0).longValue = guid.RawGuidValue[0]; + prop.GetFixedBufferElementAtIndex(1).longValue = guid.RawGuidValue[1]; + } + + bool IFoldablePropertyDrawer.HasFoldout(SerializedProperty property) { + return false; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkPrefabAssetDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkPrefabAsset))] + public class NetworkPrefabAssetDrawer : PropertyDrawerWithErrorHandling { + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + + position.width -= 40; + + // handle dragging of NetworkObjects + NetworkObject draggedObject = null; + + if (Event.current.type == EventType.DragPerform || Event.current.type == EventType.DragUpdated) { + + if (position.Contains(Event.current.mousePosition) && GUI.enabled) { + + draggedObject = DragAndDrop.objectReferences + .OfType() + .FirstOrDefault(x => EditorUtility.IsPersistent(x)); + + if (draggedObject == null) { + draggedObject = DragAndDrop.objectReferences + .OfType() + .Where(x => EditorUtility.IsPersistent(x)) + .Select(x => x.GetComponent()) + .FirstOrDefault(x => x != null); + } + + if (draggedObject != null) { + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + if (Event.current.type == EventType.DragPerform) { + if (NetworkProjectConfigUtilities.TryGetPrefabAsset(draggedObject.NetworkGuid, out NetworkPrefabAsset prefabAsset)) { + property.objectReferenceValue = prefabAsset; + property.serializedObject.ApplyModifiedProperties(); + } + } + + Event.current.Use(); + } + } + } + + EditorGUI.PropertyField(position, property, GUIContent.none, false); + + using (new EditorGUI.DisabledScope(property.objectReferenceValue == null)) { + position.x += position.width; + position.width = 40; + if (GUI.Button(position, "Ping")) { + // ping the main asset + var info = (NetworkPrefabAsset)property.objectReferenceValue; + if (NetworkProjectConfigUtilities.TryResolvePrefab(info.AssetGuid, out var prefab)) { + EditorGUIUtility.PingObject(prefab.gameObject); + } + } + } + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkPrefabAssetEditor.cs + +namespace Fusion.Editor { + using System; + using System.Linq; + using UnityEditor; + using UnityEngine; + + [CustomEditor(typeof(NetworkPrefabAsset), true)] + [CanEditMultipleObjects] + public class NetworkPrefabAssetEditor : UnityEditor.Editor { + + private UnityEditor.Editor[] _gameObjectEditors = new UnityEditor.Editor[1]; + private bool multiSelection = false; + + public override void OnInspectorGUI() { + + FusionEditorGUI.InjectPropertyDrawers(serializedObject); + FusionEditorGUI.DrawDefaultInspector(serializedObject); + + if (targets.Length > 1) { + EditorGUILayout.LabelField("Prefabs"); + } + + foreach (var prefab in targets + .Cast() + .OrderBy(x => x.name) + .Select(x => { + NetworkProjectConfigUtilities.TryResolvePrefab(x.AssetGuid, out var prefab); + return prefab; + })) { + + if (targets.Length > 1) { + EditorGUILayout.ObjectField(prefab, typeof(NetworkObject), false); + } else { + EditorGUILayout.ObjectField("Prefab", prefab, typeof(NetworkObject), false); + } + } + + if (targets.OfType().Any()) { + EditorGUILayout.Space(); + EditorGUILayout.HelpBox($"Prefab assets have their type changed to {"MISSING"} in case a prefab is removed or is set as not spawnable. " + + $"If a prefab is restored/made spawnable again, all the references will once again point to the same prefab. Having such placeholders also makes it trivial " + + $"to find any assets referencing missing prefabs.", MessageType.Info); + + if (GUILayout.Button("Destroy Selected Missing Prefab Placeholders")) { + foreach (var asset in targets.OfType().ToList()) { + DestroyImmediate(asset, true); + } + GUIUtility.ExitGUI(); + } + } + + RefreshEditors(); + } + + private void RefreshEditors() { + Array.Resize(ref _gameObjectEditors, targets.Length); + + int i = 0; + foreach (NetworkPrefabAsset info in targets) { + var prefab = AssetDatabase.LoadMainAssetAtPath(AssetDatabase.GUIDToAssetPath(info.AssetGuid.ToString("N"))) as GameObject; + if (prefab == null) { + _gameObjectEditors[i] = null; + } else if (_gameObjectEditors[i]?.target != prefab) { + _gameObjectEditors[i] = UnityEditor.Editor.CreateEditor(prefab); + } + ++i; + } + } + + private void OnDisable() { + for (int i = 0; i < _gameObjectEditors.Length; ++i) { + if (_gameObjectEditors[i]) { + DestroyImmediate(_gameObjectEditors[i]); + _gameObjectEditors[i] = null; + } + } + } + + public override bool HasPreviewGUI() { + // GameObject preview is messed + multiSelection = targets.Length > 1; + return true; + } + + public override GUIContent GetPreviewTitle() { + if (NetworkProjectConfigUtilities.TryGetPrefabSource(target.AssetGuid, out INetworkPrefabSource entry)) { + return new GUIContent(entry.EditorSummary); + } else { + return new GUIContent("null"); + } + + } + + public override void OnPreviewGUI(Rect r, GUIStyle background) { + var assetPath = AssetDatabase.GUIDToAssetPath(target.AssetGuid.ToString("N")); + var prefab = AssetDatabase.LoadMainAssetAtPath(assetPath) as GameObject; + if (prefab == null) { + EditorGUI.HelpBox(r, $"Prefab not found!\nGuid: {target.AssetGuid}\nPath: {assetPath}", MessageType.Error); + } else { + + float pickerHeight = EditorGUIUtility.singleLineHeight; + float marginBottom = 2.0f; + + + var pathRect = new Rect(r); + pathRect.height = EditorGUIUtility.singleLineHeight; + EditorGUI.LabelField(pathRect, assetPath); + + r.yMin = pathRect.yMax; + r.height = Mathf.Max(1, r.height - pickerHeight - marginBottom); + + if (!multiSelection) { + RefreshEditors(); + } + + var editor = _gameObjectEditors?.FirstOrDefault(x => x?.target == prefab); + if (editor != null) { + editor.OnPreviewGUI(r, background); + } + + + var pickerRect = new Rect(r); + pickerRect.y = r.yMax; + pickerRect.height = pickerHeight; + EditorGUI.ObjectField(pickerRect, prefab, typeof(GameObject), false); + } + } + + private new NetworkPrefabAsset target => (NetworkPrefabAsset)base.target; + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkPrefabAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkPrefabAttribute))] + public class NetworkPrefabAttributeDrawer : PropertyDrawerWithErrorHandling, IFoldablePropertyDrawer { + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var leafType = fieldInfo.FieldType.GetUnityLeafType(); + if (leafType != typeof(GameObject) && leafType != typeof(NetworkObject) && !leafType.IsSubclassOf(typeof(NetworkObject))) { + SetError($"{nameof(NetworkPrefabAttribute)} only works for {typeof(GameObject)} and {typeof(NetworkObject)} fields"); + return; + } + + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + + GameObject prefab; + if (leafType == typeof(GameObject)) { + prefab = (GameObject)property.objectReferenceValue; + } else { + var component = (NetworkObject)property.objectReferenceValue; + prefab = component != null ? component.gameObject : null; + } + + EditorGUI.BeginChangeCheck(); + + prefab = (GameObject)EditorGUI.ObjectField(position, prefab, typeof(GameObject), false); + + // ensure the results are filtered + if (UnityInternal.ObjectSelector.isVisible) { + var selector = UnityInternal.ObjectSelector.get; + if (UnityInternal.EditorGUIUtility.LastControlID == selector.objectSelectorID) { + var filter = selector.searchFilter; + if (!filter.Contains(NetworkProjectConfigImporter.FusionPrefabTagSearchTerm)) { + if (string.IsNullOrEmpty(filter)) { + filter = NetworkProjectConfigImporter.FusionPrefabTagSearchTerm; + } else { + filter = NetworkProjectConfigImporter.FusionPrefabTagSearchTerm + " " + filter; + } + selector.searchFilter = filter; + } + } + } + + if (EditorGUI.EndChangeCheck()) { + UnityEngine.Object result; + if (!prefab) { + result = null; + } else { + if (leafType == typeof(GameObject)) { + result = prefab; + } else { + result = prefab.GetComponent(leafType); + if (!result) { + SetError($"Prefab {prefab} does not have a {leafType} component"); + return; + } + } + } + + property.objectReferenceValue = prefab; + property.serializedObject.ApplyModifiedProperties(); + } + + if (prefab) { + var no = prefab.GetComponent(); + if (no == null) { + SetError($"Prefab {prefab} does not have a {nameof(NetworkObject)} component"); + } else if (!no.NetworkGuid.IsValid) { + SetError($"Prefab {prefab} needs to be reimported."); + } else if (!NetworkProjectConfigUtilities.TryResolvePrefab(no.NetworkGuid, out var resolved)) { + SetError($"Prefab {prefab} with guid {no.NetworkGuid} not found in the config. Try reimporting."); + } else if (resolved != no) { + SetError($"Prefab {prefab} with guid {no.NetworkGuid} resolved to a different prefab: {resolved}."); + } + } + } + } + + bool IFoldablePropertyDrawer.HasFoldout(SerializedProperty property) { + return false; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkPrefabRefDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkPrefabRef))] + public class NetworkPrefabRefDrawer : PropertyDrawerWithErrorHandling, IFoldablePropertyDrawer { + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var prefabRef = NetworkObjectGuidDrawer.GetValue(property); + + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + NetworkObject prefab = null; + + if (prefabRef.IsValid && !NetworkProjectConfigUtilities.TryResolvePrefab(prefabRef, out prefab)) { + var prefabActualPath = AssetDatabase.GUIDToAssetPath(prefabRef.ToString("N")); + if (!string.IsNullOrEmpty(prefabActualPath)) { + var go = AssetDatabase.LoadMainAssetAtPath(prefabActualPath) as GameObject; + if ( go != null ) { + prefab = go.GetComponent(); + } + } + + if (!prefab) { + SetError($"Prefab with guid {prefabRef} not found."); + } + } + + EditorGUI.BeginChangeCheck(); + + var prefabGo = (GameObject)EditorGUI.ObjectField(position, prefab != null ? prefab.gameObject : null, typeof(GameObject), false); + + // ensure the results are filtered + if (UnityInternal.ObjectSelector.isVisible) { + var selector = UnityInternal.ObjectSelector.get; + if (UnityInternal.EditorGUIUtility.LastControlID == selector.objectSelectorID) { + var filter = selector.searchFilter; + if (!filter.Contains(NetworkProjectConfigImporter.FusionPrefabTagSearchTerm)) { + if (string.IsNullOrEmpty(filter)) { + filter = NetworkProjectConfigImporter.FusionPrefabTagSearchTerm; + } else { + filter = NetworkProjectConfigImporter.FusionPrefabTagSearchTerm + " " + filter; + } + selector.searchFilter = filter; + } + } + } + + if (EditorGUI.EndChangeCheck()) { + if (prefabGo) { + prefab = prefabGo.GetComponent(); + if (!prefab) { + SetError($"Prefab {prefabGo} does not have a {nameof(NetworkObject)} component"); + return; + } + } else { + prefab = null; + } + + if (prefab) { + prefabRef = prefab.NetworkGuid; + } else { + prefabRef = default; + } + NetworkObjectGuidDrawer.SetValue(property, prefabRef); + property.serializedObject.ApplyModifiedProperties(); + } + + SetInfo($"{prefabRef}"); + + + if (prefab) { + var expectedPrefabRef = prefab.NetworkGuid; + if (!prefabRef.Equals(expectedPrefabRef)) { + SetError($"Resolved {prefab} has a different guid ({expectedPrefabRef}) than expected ({prefabRef}). " + + $"This can happen if prefabs are incorrectly resolved, e.g. when there are multiple resources of the same name."); + } else if (!expectedPrefabRef.IsValid) { + SetError($"Prefab {prefab} needs to be reimported."); + } else if (!NetworkProjectConfigUtilities.TryResolvePrefab(expectedPrefabRef, out _)) { + SetError($"Prefab {prefab} with guid {prefab.NetworkGuid} not found in the config. Try reimporting."); + } else { + // ClearError(); + } + } + } + } + + bool IFoldablePropertyDrawer.HasFoldout(SerializedProperty property) { + return false; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NetworkStringDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Reflection; + using System.Text; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(NetworkString<>))] + public class NetworkStringDrawer : PropertyDrawerWithErrorHandling, IFoldablePropertyDrawer { + + private string _str = ""; + private Action _write; + private Action _read; + + public NetworkStringDrawer() { + _write = (buffer, count) => { + unsafe { + fixed (int* p = buffer) { + _str = new string((sbyte*)p, 0, count * 4, Encoding.UTF32); + } + } + }; + + _read = (buffer, count) => { + unsafe { + fixed (int* p = buffer) { + var charCount = UTF32Tools.Convert(_str, (uint*)p, count).CharacterCount; + if (charCount < _str.Length) { + _str = _str.Substring(0, charCount); + } + } + } + }; + } + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var length = property.FindPropertyRelativeOrThrow(nameof(NetworkString<_2>._length)); + var data = property.FindPropertyRelativeOrThrow($"{nameof(NetworkString<_2>._data)}.Data"); + + data.UpdateFixedBuffer(_read, _write, false); + + EditorGUI.BeginChangeCheck(); + + using (new FusionEditorGUI.ShowMixedValueScope(data.hasMultipleDifferentValues)) { + _str = EditorGUI.TextField(position, label, _str); + } + + if (EditorGUI.EndChangeCheck()) { + if (data.UpdateFixedBuffer(_read, _write, true, data.hasMultipleDifferentValues)) { + length.intValue = Encoding.UTF32.GetByteCount(_str) / 4; + data.serializedObject.ApplyModifiedProperties(); + } + } + } + + bool IFoldablePropertyDrawer.HasFoldout(SerializedProperty property) { + return false; + } + + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/NormalizedRectAttributeDrawer.cs + + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + +#if UNITY_EDITOR + [CustomPropertyDrawer(typeof(NormalizedRectAttribute))] + public class NormalizedRectAttributeDrawer : PropertyDrawer { + + bool isDragNewRect; + bool isDragXMin, isDragXMax, isDragYMin, isDragYMax, isDragAll; + MouseCursor lockCursorStyle; + + Vector2 mouseDownStart; + static GUIStyle _compactLabelStyle; + static GUIStyle _compactValueStyle; + + const float EXPANDED_HEIGHT = 140; + const float COLLAPSE_HEIGHT = 48; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + if (property.propertyType == SerializedPropertyType.Rect) { + return property.isExpanded ? EXPANDED_HEIGHT : COLLAPSE_HEIGHT; + } else { + return base.GetPropertyHeight(property, label); + } + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + EditorGUI.BeginProperty(position, label, property); + + bool hasChanged = false; + + EditorGUI.LabelField(new Rect(position) { height = 17 }, label); + + var value = property.rectValue; + + if (property.propertyType == SerializedPropertyType.Rect) { + + var dragarea = new Rect(position) { + yMin = position.yMin + 16 + 3, + yMax = position.yMax - 2, + //xMin = position.xMin + 16, + //xMax = position.xMax - 4 + }; + + // lower foldout box + GUI.Box(dragarea, GUIContent.none, EditorStyles.helpBox); + + property.isExpanded = GUI.Toggle(new Rect(position) { xMin = dragarea.xMin + 2, yMin = dragarea.yMin + 2, width = 12, height = 16 }, property.isExpanded, GUIContent.none, EditorStyles.foldout); + bool isExpanded = property.isExpanded; + + float border = isExpanded ? 4 : 2; + dragarea.xMin += 18; + dragarea.yMin += border; + dragarea.xMax -= border; + dragarea.yMax -= border; + + // Reshape the inner box to the correct aspect ratio + if (isExpanded) { + var ratio = (attribute as NormalizedRectAttribute).AspectRatio; + if (ratio == 0) { + var currentRes = UnityEditor.Handles.GetMainGameViewSize(); + ratio = currentRes.x / currentRes.y; + } + + // Don't go any wider than the inspector box. + var width = (dragarea.height * ratio); + if (width < dragarea.width) { + var x = (dragarea.width - width) / 2; + dragarea.x = dragarea.xMin + (int)x; + dragarea.width = (int)(width); + } + } + + + // Simulated desktop rect + GUI.Box(dragarea, GUIContent.none, EditorStyles.helpBox); + + var invertY = (attribute as NormalizedRectAttribute).InvertY; + + Event e = Event.current; + + const int HANDLE_SIZE = 8; + + var normmin = new Vector2(value.xMin, invertY ? 1f - value.yMin : value.yMin); + var normmax = new Vector2(value.xMax, invertY ? 1f - value.yMax : value.yMax); + var minreal = Rect.NormalizedToPoint(dragarea, normmin); + var maxreal = Rect.NormalizedToPoint(dragarea, normmax); + var lowerleftrect = new Rect(minreal.x , minreal.y - (invertY ? HANDLE_SIZE : 0), HANDLE_SIZE, HANDLE_SIZE); + var upperrghtrect = new Rect(maxreal.x - HANDLE_SIZE, maxreal.y - (invertY ? 0 : HANDLE_SIZE), HANDLE_SIZE, HANDLE_SIZE); + var upperleftrect = new Rect(minreal.x , maxreal.y - (invertY ? 0 : HANDLE_SIZE), HANDLE_SIZE, HANDLE_SIZE); + var lowerrghtrect = new Rect(maxreal.x - HANDLE_SIZE, minreal.y - (invertY ? HANDLE_SIZE : 0), HANDLE_SIZE, HANDLE_SIZE); + + var currentrect = Rect.MinMaxRect(minreal.x, invertY ? maxreal.y : minreal.y, maxreal.x, invertY ? minreal.y : maxreal.y); + + if (lockCursorStyle == MouseCursor.Arrow) { + if (isExpanded) { + EditorGUIUtility.AddCursorRect(lowerleftrect, MouseCursor.Link); + EditorGUIUtility.AddCursorRect(upperrghtrect, MouseCursor.Link); + EditorGUIUtility.AddCursorRect(upperleftrect, MouseCursor.Link); + EditorGUIUtility.AddCursorRect(lowerrghtrect, MouseCursor.Link); + } + EditorGUIUtility.AddCursorRect(currentrect, MouseCursor.MoveArrow); + } else { + // Lock cursor to a style while dragging, otherwise the slow inspector update causes rapid mouse icon changes. + EditorGUIUtility.AddCursorRect(dragarea, lockCursorStyle); + } + + EditorGUI.DrawRect(lowerleftrect, Color.yellow); + EditorGUI.DrawRect(upperrghtrect, Color.yellow); + EditorGUI.DrawRect(upperleftrect, Color.yellow); + EditorGUI.DrawRect(lowerrghtrect, Color.yellow); + + var mousepos = e.mousePosition; + if (e.button == 0) { + if (e.type == EventType.MouseUp) { + isDragXMin = false; + isDragYMin = false; + isDragXMax = false; + isDragYMax = false; + isDragAll = false; + lockCursorStyle = MouseCursor.Arrow; + isDragNewRect = false; + + hasChanged = true; + } + + if (e.type == EventType.MouseDown ) { + if (isExpanded && lowerleftrect.Contains(mousepos)) { + isDragXMin = true; + isDragYMin = true; + lockCursorStyle = MouseCursor.Link; + } else if (isExpanded && upperrghtrect.Contains(mousepos)) { + isDragXMax = true; + isDragYMax = true; + lockCursorStyle = MouseCursor.Link; + } else if (isExpanded && upperleftrect.Contains(mousepos)) { + isDragXMin = true; + isDragYMax = true; + lockCursorStyle = MouseCursor.Link; + } else if (isExpanded && lowerrghtrect.Contains(mousepos)) { + isDragXMax = true; + isDragYMin = true; + lockCursorStyle = MouseCursor.Link; + } else if (currentrect.Contains(mousepos)) { + isDragAll = true; + // mouse start is stored as a normalized offset from the Min values. + mouseDownStart = Rect.PointToNormalized(dragarea, mousepos) - normmin; + lockCursorStyle = MouseCursor.MoveArrow; + } else if (isExpanded && dragarea.Contains(mousepos)) { + mouseDownStart = mousepos; + isDragNewRect = true; + } + } + } + + if (e.type == EventType.MouseDrag) { + + Rect rect; + if (isDragNewRect) { + var start = Rect.PointToNormalized(dragarea, mouseDownStart); + var end = Rect.PointToNormalized(dragarea, e.mousePosition); + + if (invertY) { + rect = Rect.MinMaxRect( + Math.Max(0f, Math.Min(start.x, end.x)), + Math.Max(0f, 1f - Math.Max(start.y, end.y)), + Math.Min(1f, Math.Max(start.x, end.x)), + Math.Min(1f, 1f - Math.Min(start.y, end.y)) + ); + } else { + rect = Rect.MinMaxRect( + Math.Max(0f, Math.Min(start.x, end.x)), + Math.Max(0f, Math.Min(start.y, end.y)), + Math.Min(1f, Math.Max(start.x, end.x)), + Math.Min(1f, Math.Max(start.y, end.y)) + ); + } + property.rectValue = rect; + hasChanged = true; + + + } else if (isDragAll){ + var normmouse = Rect.PointToNormalized(dragarea, e.mousePosition); + rect = new Rect(value) { + x = Math.Max(normmouse.x - mouseDownStart.x, 0), + y = Math.Max(invertY ? (1 - normmouse.y + mouseDownStart.y) : (normmouse.y - mouseDownStart.y), 0) + }; + + if (rect.xMax > 1) { + rect = new Rect(rect) { x = rect.x + (1f - rect.xMax)}; + } + if (rect.yMax > 1) { + rect = new Rect(rect) { y = rect.y + (1f - rect.yMax) }; + } + + property.rectValue = rect; + hasChanged = true; + + } else if (isDragXMin || isDragXMax || isDragYMin || isDragYMax) { + + const float VERT_HANDLE_MIN_DIST = .2f; + const float HORZ_HANDLE_MIN_DIST = .05f; + var normmouse = Rect.PointToNormalized(dragarea, e.mousePosition); + if (invertY) { + rect = Rect.MinMaxRect( + isDragXMin ? Math.Min( normmouse.x, value.xMax - HORZ_HANDLE_MIN_DIST) : value.xMin, + isDragYMin ? Math.Min(1f - normmouse.y, value.yMax - VERT_HANDLE_MIN_DIST) : value.yMin, + isDragXMax ? Math.Max( normmouse.x, value.xMin + HORZ_HANDLE_MIN_DIST) : value.xMax, + isDragYMax ? Math.Max(1f - normmouse.y, value.yMin + VERT_HANDLE_MIN_DIST) : value.yMax + ); + } else { + rect = Rect.MinMaxRect( + isDragXMin ? Math.Min(normmouse.x, value.xMax - HORZ_HANDLE_MIN_DIST) : value.xMin, + isDragYMin ? Math.Min(normmouse.y, value.yMax - VERT_HANDLE_MIN_DIST) : value.yMin, + isDragXMax ? Math.Max(normmouse.x, value.xMin + HORZ_HANDLE_MIN_DIST) : value.xMax, + isDragYMax ? Math.Max(normmouse.y, value.yMin + VERT_HANDLE_MIN_DIST) : value.yMax + ); + } + + property.rectValue = rect; + hasChanged = true; + } + } + + const float SPACING = 4f; + const int LABELS_WIDTH = 16; + const float COMPACT_THRESHOLD = 340f; + + bool useCompact = position.width < COMPACT_THRESHOLD; + + var labelwidth = EditorGUIUtility.labelWidth; + var fieldwidth = (position.width - labelwidth- 3 * SPACING) * 0.25f ; + var fieldbase = new Rect(position) { xMin = position.xMin + labelwidth, height = 16, width = fieldwidth - (useCompact ? 0 : LABELS_WIDTH) }; + + if (_compactValueStyle == null) { + _compactLabelStyle = new GUIStyle(EditorStyles.miniLabel) { fontSize = 9, alignment = TextAnchor.MiddleLeft, padding = new RectOffset(2, 0, 1, 0) }; + _compactValueStyle = new GUIStyle(EditorStyles.miniTextField) { fontSize = 9, alignment = TextAnchor.MiddleLeft, padding = new RectOffset(2, 0, 1, 0) }; + } + GUIStyle valueStyle = _compactValueStyle; + + //if (useCompact) { + // if (_compactStyle == null) { + // _compactStyle = new GUIStyle(EditorStyles.miniTextField) { fontSize = 9, alignment = TextAnchor.MiddleLeft, padding = new RectOffset(2, 0, 1, 0) }; + // } + // valueStyle = _compactStyle; + //} else { + // valueStyle = EditorStyles.textField; + //} + + // Only draw labels when not in compact + if (!useCompact) { + Rect l1 = new Rect(fieldbase) { x = fieldbase.xMin }; + Rect l2 = new Rect(fieldbase) { x = fieldbase.xMin + 1 * (fieldwidth + SPACING) }; + Rect l3 = new Rect(fieldbase) { x = fieldbase.xMin + 2 * (fieldwidth + SPACING) }; + Rect l4 = new Rect(fieldbase) { x = fieldbase.xMin + 3 * (fieldwidth + SPACING) }; + GUI.Label(l1, "L:", _compactLabelStyle); + GUI.Label(l2, "R:", _compactLabelStyle); + GUI.Label(l3, "T:", _compactLabelStyle); + GUI.Label(l4, "B:", _compactLabelStyle); + } + + // Draw value fields + Rect f1 = new Rect(fieldbase) { x = fieldbase.xMin + 0 * fieldwidth + (useCompact ? 0 : LABELS_WIDTH) }; + Rect f2 = new Rect(fieldbase) { x = fieldbase.xMin + 1 * fieldwidth + (useCompact ? 0 : LABELS_WIDTH) + 1 * SPACING }; + Rect f3 = new Rect(fieldbase) { x = fieldbase.xMin + 2 * fieldwidth + (useCompact ? 0 : LABELS_WIDTH) + 2 * SPACING }; + Rect f4 = new Rect(fieldbase) { x = fieldbase.xMin + 3 * fieldwidth + (useCompact ? 0 : LABELS_WIDTH) + 3 * SPACING }; + + using (var check = new EditorGUI.ChangeCheckScope()) { + float newxmin, newxmax, newymin, newymax; + if (invertY) { + newxmin = EditorGUI.DelayedFloatField(f1, (float)Math.Round(value.xMin, useCompact ? 2 : 3), valueStyle); + newxmax = EditorGUI.DelayedFloatField(f2, (float)Math.Round(value.xMax, useCompact ? 2 : 3), valueStyle); + newymax = EditorGUI.DelayedFloatField(f3, (float)Math.Round(value.yMax, useCompact ? 2 : 3), valueStyle); + newymin = EditorGUI.DelayedFloatField(f4, (float)Math.Round(value.yMin, useCompact ? 2 : 3), valueStyle); + } else { + newxmin = EditorGUI.DelayedFloatField(f1, (float)Math.Round(value.xMin, useCompact ? 2 : 3), valueStyle); + newxmax = EditorGUI.DelayedFloatField(f2, (float)Math.Round(value.xMax, useCompact ? 2 : 3), valueStyle); + newymin = EditorGUI.DelayedFloatField(f3, (float)Math.Round(value.yMin, useCompact ? 2 : 3), valueStyle); + newymax = EditorGUI.DelayedFloatField(f4, (float)Math.Round(value.yMax, useCompact ? 2 : 3), valueStyle); + } + + if (check.changed) { + if (newxmin != value.xMin) value.xMin = Math.Min(newxmin, value.xMax - .05f); + if (newxmax != value.xMax) value.xMax = Math.Max(newxmax, value.xMin + .05f); + if (newymax != value.yMax) value.yMax = Math.Max(newymax, value.yMin + .05f); + if (newymin != value.yMin) value.yMin = Math.Min(newymin, value.yMax - .05f); + property.rectValue = value; + property.serializedObject.ApplyModifiedProperties(); + } + } + + var nmins = new Vector2(value.xMin, invertY ? 1f - value.yMin : value.yMin); + var nmaxs = new Vector2(value.xMax, invertY ? 1f - value.yMax : value.yMax); + var mins = Rect.NormalizedToPoint(dragarea, nmins); + var maxs = Rect.NormalizedToPoint(dragarea, nmaxs); + var area = Rect.MinMaxRect(minreal.x, invertY ? maxreal.y : minreal.y, maxreal.x, invertY ? minreal.y : maxreal.y); + + EditorGUI.DrawRect(area, new Color(1f, 1f, 1f, .1f)); + //GUI.DrawTexture(area, GUIContent.none, EditorStyles.helpBox); + //GUI.Box(area, GUIContent.none, EditorStyles.helpBox); + + } else { + Debug.LogWarning($"{nameof(NormalizedRectAttribute)} only valid on UnityEngine.Rect fields. Will use default rendering for '{property.type} {property.name}' in class '{fieldInfo.DeclaringType}'."); + EditorGUI.PropertyField(position, property, label); + } + + if (hasChanged) { + GUI.changed = true; + property.serializedObject.ApplyModifiedProperties(); + } + + EditorGUI.EndProperty(); + } + } +#endif + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/Pow2SliderAttributeDrawer.cs + +namespace Fusion.Editor { + + using System; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(Pow2SliderAttribute))] + public class Pow2SliderAttributeDrawer : PropertyDrawer { + public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { + + EditorGUI.BeginProperty(r, label, property); + + var attr = attribute as Pow2SliderAttribute; + + bool showUnits = attr.Unit != Units.None; + float fieldWidth = showUnits ? UnitAttributeDecoratorDrawer.FIELD_WIDTH : 50; + + EditorGUI.LabelField(r, label); + + float labelw = EditorGUIUtility.labelWidth; + + int exp; + if (attr.AllowZero && property.intValue == 0) + exp = 0; + else + exp = (int)Math.Log(property.intValue, 2); + + int oldValue = property.intValue; + + Rect rightRect = new Rect(r) { xMin = r.xMin + EditorGUIUtility.labelWidth + 2 }; + Rect r1 = new Rect(rightRect) { xMax = rightRect.xMax - fieldWidth - 4 }; + Rect r2 = new Rect(rightRect) { xMin = rightRect.xMax - fieldWidth }; + int newExp = (int)GUI.HorizontalSlider(r1, exp, attr.MinPower, attr.MaxPower); + + if (newExp != exp) { + int newValue = (attr.AllowZero && newExp == 0) ? 0 : (int)Math.Round(Math.Pow(2, newExp)); + + property.intValue = newValue; + + if (property.intValue != newValue) + Debug.LogWarning(property.name + " Pow2SliderAttribute range exceeds the possible value range of its field type."); + + property.serializedObject.ApplyModifiedProperties(); + } + + EditorGUI.BeginChangeCheck(); + int newVal = EditorGUI.DelayedIntField(r2, property.intValue); + if (newVal != property.intValue) { + + // Round to the nearest even exponent + int rounded; + if (attr.AllowZero && newVal == 1) + rounded = 0; + else + rounded = (int)Math.Pow(2, (int)Math.Log(newVal, 2)); + + property.intValue = rounded; + + if (property.intValue != rounded) + Debug.LogWarning(property.name + " Pow2SliderAttribute range exceeds the possible value range of its field type."); + + property.serializedObject.ApplyModifiedProperties(); + } + + if (showUnits) { + var (style, name) = UnitAttributeDecoratorDrawer.GetOverlayStyle(attr.Unit); + GUI.Label(r, name, style); + } + + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return EditorGUI.GetPropertyHeight(property); + } + } + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/PropertyDrawerWithErrorHandling.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + public abstract class PropertyDrawerWithErrorHandling : PropertyDrawer { + private SerializedProperty _currentProperty; + private string _info; + public float IconOffset; + + struct Entry { + public string message; + public MessageType type; + } + + private Dictionary _errors = new Dictionary(); + private bool _hadError; + + public override sealed void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + Debug.Assert(_currentProperty == null); + + var decoration = GetDecoration(property); + + if (decoration != null) { + DrawDecoration(position, decoration.Value, label != GUIContent.none, drawButton: true, drawIcon: false); + } + + + _currentProperty = property; + _hadError = false; + _info = null; + + EditorGUI.BeginChangeCheck(); + + try { + OnGUIInternal(position, property, label); + } catch (ExitGUIException) { + // pass through + } catch (Exception ex) { + SetError(ex.ToString()); + } finally { + // if there was a change but no error clear + if (EditorGUI.EndChangeCheck() && !_hadError) { + ClearError(); + } + + _currentProperty = null; + } + + if (decoration != null) { + DrawDecoration(position, decoration.Value, label != GUIContent.none, drawButton: false, drawIcon: true); + } + } + + private void DrawDecoration(Rect position, (string, MessageType, bool) decoration, bool hasLabel, bool drawButton = true, bool drawIcon = true) { + var iconPosition = position; + iconPosition.height = EditorGUIUtility.singleLineHeight; + iconPosition.x -= IconOffset; + FusionEditorGUI.Decorate(iconPosition, decoration.Item1, decoration.Item2, hasLabel, drawButton: drawButton, drawBorder: decoration.Item3); + } + + private (string, MessageType, bool)? GetDecoration(SerializedProperty property) { + if (_errors.TryGetValue(property.propertyPath, out var error)) { + return (error.message, error.type, true); + } else if (_info != null) { + return (_info, MessageType.Info, false); + } + return null; + } + + protected abstract void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label); + + protected void ClearError() { + ClearError(_currentProperty); + } + + protected void ClearError(SerializedProperty property) { + _hadError = false; + _errors.Remove(property.propertyPath); + } + + protected void ClearErrorIfLostFocus() { + if (GUIUtility.keyboardControl != UnityInternal.EditorGUIUtility.LastControlID) { + ClearError(); + } + } + + protected void SetError(string error) { + _hadError = true; + _errors[_currentProperty.propertyPath] = new Entry() { + message = error, + type = MessageType.Error + }; + } + + protected void SetWarning(string warning) { + if (_errors.TryGetValue(_currentProperty.propertyPath, out var entry) && entry.type == MessageType.Error) { + return; + } + + _errors[_currentProperty.propertyPath] = new Entry() { + message = warning, + type = MessageType.Warning + }; + } + + protected void SetInfo(string message) { + _info = message; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/ReserveArrayPropertyHeightDecorator.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + internal class ReserveArrayPropertyHeightDecorator : DecoratorDrawer { + + public float Height; + + private bool IsGettingHeight() { + var method = new System.Diagnostics.StackFrame(2)?.GetMethod(); + if (method == null) { + // impossible to tell + return false; + } + + if (method.DeclaringType == UnityInternal.PropertyHandler.InternalType && method.Name == "GetHeight") { + return true; + } + + return false; + } + + + public override float GetHeight() { + if (Height == 0.0f) { + // nothing to do + return 0.0f; + } + + if (!IsGettingHeight()) { + // being drawn, it seems + return 0.0f; + } + + return Height; + } + + public override void OnGUI(Rect position) { + // do nothing + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/ResourcePathAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEditorInternal; + using UnityEngine; + + [CustomPropertyDrawer(typeof(UnityResourcePathAttribute))] + public class ResourcePathAttributeDrawer : PropertyDrawerWithErrorHandling { + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var attrib = (UnityResourcePathAttribute)attribute; + + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + + position.width -= 40; + EditorGUI.PropertyField(position, property, GUIContent.none, false); + UnityEngine.Object asset = null; + + var path = property.stringValue; + if (string.IsNullOrEmpty(path)) { + ClearError(); + } else { + asset = Resources.Load(path, attrib.ResourceType); + if (asset == null) { + SetError($"Resource of type {attrib.ResourceType} not found at {path}"); + } else { + SetInfo(AssetDatabase.GetAssetPath(asset)); + } + } + + using (new EditorGUI.DisabledScope(asset == null)) { + position.x += position.width; + position.width = 40; + if (GUI.Button(position, "Ping")) { + // ping the main asset + EditorGUIUtility.PingObject(Resources.Load(path)); + } + } + } + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/ScenePathAttributeDrawer.cs + +// --------------------------------------------------------------------------------------------- +// PhotonNetwork Framework for Unity - Copyright (C) 2020 Exit Games GmbH +// developer@exitgames.com +// --------------------------------------------------------------------------------------------- + +namespace Fusion.Editor { + using UnityEngine; + using UnityEditor; + using UnityEditor.SceneManagement; + using System.Linq; + using System.IO; + using System; + + [CanEditMultipleObjects] + [CustomPropertyDrawer(typeof(ScenePathAttribute))] + public class ScenePathAttributeDrawer : PropertyDrawerWithErrorHandling { + + private SceneAsset[] _allScenes; + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + var oldScene = AssetDatabase.LoadAssetAtPath(property.stringValue); + if (oldScene == null && !string.IsNullOrEmpty(property.stringValue)) { + + // well, maybe by name then? + _allScenes = _allScenes ?? AssetDatabase.FindAssets("t:scene") + .Select(x => AssetDatabase.GUIDToAssetPath(x)) + .Select(x => AssetDatabase.LoadAssetAtPath(x)) + .ToArray(); + + var matchedByName = _allScenes.Where(x => x.name == property.stringValue).ToList(); ; + + if (matchedByName.Count == 0) { + base.SetError($"Scene not found: {property.stringValue}"); + } else { + oldScene = matchedByName[0]; + if (matchedByName.Count > 1) { + base.SetWarning($"There are multiple scenes with this name"); + } + } + } + + using (new FusionEditorGUI.PropertyScope(position, label, property)) { + EditorGUI.BeginChangeCheck(); + var newScene = EditorGUI.ObjectField(position, label, oldScene, typeof(SceneAsset), false) as SceneAsset; + if (EditorGUI.EndChangeCheck()) { + var assetPath = AssetDatabase.GetAssetPath(newScene); + property.stringValue = assetPath; + property.serializedObject.ApplyModifiedProperties(); + base.ClearError(); + } + } + } + } + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/ScriptHeaderAttributeDrawer.cs + +namespace Fusion.Editor { + + using UnityEngine; + using UnityEditor; + + internal class ScriptHeaderAttributeDrawer : PropertyDrawer { + + internal class Attribute : PropertyAttribute { + public ScriptHelpAttribute Settings; + } + + internal new Attribute attribute => (Attribute)base.attribute; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + var settings = attribute.Settings; + + if (settings == null ) { + EditorGUI.PropertyField(position, property, label, property.isExpanded); + return; + } + + bool useEnhancedHeader = settings.BackColor != EditorHeaderBackColor.None; + + // ensures exact height correctness (might slightly differ due to scaling) + position.height = useEnhancedHeader ? 24f : 18; + + var scriptType = property.serializedObject.targetObject.GetType(); + + EditorGUIUtility.AddCursorRect(position, MouseCursor.Link); + + if (useEnhancedHeader) { + using (new FusionEditorGUI.EnabledScope(true)) { + Event e = Event.current; + if (e.type == EventType.MouseDown && position.Contains(e.mousePosition)) { + if (e.clickCount == 1) { + if (!string.IsNullOrEmpty(settings.Url)) { + Application.OpenURL(settings.Url); + } + EditorGUIUtility.PingObject(MonoScript.FromMonoBehaviour(property.serializedObject.targetObject as MonoBehaviour)); + } else { + AssetDatabase.OpenAsset(MonoScript.FromMonoBehaviour(property.serializedObject.targetObject as MonoBehaviour)); + } + } + var scriptName = string.IsNullOrEmpty(settings.Title) ? scriptType.Name : settings.Title; + EditorGUI.LabelField(position, ObjectNames.NicifyVariableName(scriptName).ToUpper(), FusionGUIStyles.GetFusionHeaderBackStyle((int)settings.BackColor)); + } + } else { + using (new FusionEditorGUI.EnabledScope(false)) { + SerializedProperty prop = property.serializedObject.FindProperty("m_Script"); + EditorGUI.PropertyField(position, prop); + } + } + + + //// Draw Icon overlay + //var icon = FusionGUIStyles.GetFusionIconTexture(attribute.Settings.Icon); + //if (icon != null) { + // GUI.DrawTexture(new Rect(position) { xMin = position.xMax - 128 }, icon); + //} + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + var settings = attribute.Settings; + return settings == null ? base.GetPropertyHeight(property, label) : settings.BackColor != EditorHeaderBackColor.None ? 24.0f : 18.0f; + } + } +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/SerializableDictionaryDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEditorInternal; + using UnityEngine; + + [CustomPropertyDrawer(typeof(SerializableDictionary), true)] + public class SerializableDictionaryDrawer : PropertyDrawerWithErrorHandling { + const string ItemsPropertyPath = SerializableDictionary.ItemsPropertyPath; + const string EntryKeyPropertyPath = SerializableDictionary.EntryKeyPropertyPath; + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + var entries = property.FindPropertyRelativeOrThrow(ItemsPropertyPath); + entries.isExpanded = property.isExpanded; + using (new FusionEditorGUI.PropertyScope(position, label, property)) { + EditorGUI.PropertyField(position, entries, label, true); + property.isExpanded = entries.isExpanded; + + string error = VerifyDictionary(entries, EntryKeyPropertyPath); + if (error != null) { + SetError(error); + } else { + ClearError(); + } + } + } + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + var entries = property.FindPropertyRelativeOrThrow(ItemsPropertyPath); + return EditorGUI.GetPropertyHeight(entries, label, true); + } + + private static HashSet _dictionaryKeyHash = new HashSet(new SerializedPropertyUtilities.SerializedPropertyEqualityComparer()); + + private static string VerifyDictionary(SerializedProperty prop, string keyPropertyName) { + Debug.Assert(prop.isArray); + try { + for (int i = 0; i < prop.arraySize; ++i) { + var keyProperty = prop.GetArrayElementAtIndex(i).FindPropertyRelativeOrThrow(keyPropertyName); + if (!_dictionaryKeyHash.Add(keyProperty)) { + + var groups = Enumerable.Range(0, prop.arraySize) + .GroupBy(x => prop.GetArrayElementAtIndex(x).FindPropertyRelative(keyPropertyName), x => x, _dictionaryKeyHash.Comparer) + .Where(x => x.Count() > 1) + .ToList(); + + // there are duplicates - take the slow and allocating path now + return string.Join("\n", groups.Select(x => $"Duplicate keys for elements: {string.Join(", ", x)}")); + } + } + + return null; + + } finally { + _dictionaryKeyHash.Clear(); + } + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/ToggleLeftAttributeDrawer.cs + +namespace Fusion.Editor { + + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(ToggleLeftAttribute))] + public class ToggleLeftAttributeDrawer : PropertyDrawer { + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + EditorGUI.BeginProperty(position, label, property); + + EditorGUI.BeginChangeCheck(); + var val = EditorGUI.ToggleLeft(position, label, property.boolValue); + + + + if (EditorGUI.EndChangeCheck()) { + property.boolValue = val; + } + + EditorGUI.EndProperty(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/UnitAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(UnitAttribute))] + public class UnitAttributeDecoratorDrawer : PropertyDrawer { + + public const float FIELD_WIDTH = 130; + public const int MAX_PLACES = 6; + + static Dictionary _unitStyles = new Dictionary(); + + public static (GUIStyle, string) GetOverlayStyle(Units unit) { + if (_unitStyles.TryGetValue(unit, out var styleAndName) == false) { + GUIStyle style; + style = new GUIStyle(EditorStyles.miniLabel); + style.alignment = TextAnchor.MiddleRight; + style.contentOffset = new Vector2(-2, 0); + + style.normal.textColor = EditorGUIUtility.isProSkin ? new Color(255f/255f, 221/255f, 0/255f, 1f) : Color.blue; + + _unitStyles.Add(unit, styleAndName = (style, $"{unit.GetDescription()}")); + } + + return styleAndName; + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + var attr = attribute as UnitAttribute; + if (attr.UseInverse) { + return EditorGUI.GetPropertyHeight(property) * 2 + 6; + } + return EditorGUI.GetPropertyHeight(property); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + DrawUnitsProperty(position, property, label, attribute as UnitAttribute); + } + + public static void DrawUnitsProperty(Rect position, SerializedProperty property, GUIContent label, UnitAttribute attr) { + + // Strange gui bug + if (position.width == 1) + return; + + EditorGUI.BeginProperty(position, label, property); + var holdIndent = EditorGUI.indentLevel; + + var useInverse = attr.UseInverse; + + if (useInverse) { + EditorGUI.DrawRect(new Rect(position) { xMin = position.xMin - 2 }, new Color(0, 0, 0, .2f)); + position.xMax -= 2; + position.yMin += 2; + position.yMax -= 2; + } + + Rect secondRow; + if (useInverse) { + position.height = 17; + secondRow = new Rect(position) { y = position.y + 20 }; + } else { + secondRow = default; + } + + var proptype = property.type; + + double min = attr.Min; + double max = attr.Max; + + double realmin = min < max ? min : max; + double realmax = min < max ? max : min; + + + /*if (min != 0 || max != 0) */ + { + + Rect rightSide1 = new Rect(position) { xMin = position.xMin + EditorGUIUtility.labelWidth + 2 }; + Rect rightSide2 = (useInverse) ? new Rect(secondRow) { xMin = secondRow.xMin + EditorGUIUtility.labelWidth + 2 } : default; + Rect sliderRect1 = new Rect(rightSide1) { xMax = rightSide1.xMax - FIELD_WIDTH - 4 }; + Rect sliderRect2 = new Rect(rightSide2) { xMax = rightSide2.xMax - FIELD_WIDTH - 4 }; + bool useSlider = (min != 0 || max != 0) && sliderRect1.width > 20; + + Rect valRect1 = new Rect(rightSide1); + Rect valRect2 = new Rect(rightSide2); + + if (useSlider) { + var valwidth = rightSide1.xMax - FIELD_WIDTH; + valRect1.xMin = valwidth; + valRect2.xMin = valwidth; + } + + if (property.propertyType == SerializedPropertyType.Float) { + + // Slider is always float based, even for doubles + if (useSlider) { + bool isDouble = proptype == "double"; + + // Slider is the same for double and float, it just casts differently at the end. + EditorGUI.LabelField(position, label, GUIContent.none); + EditorGUI.BeginChangeCheck(); + EditorGUI.indentLevel = 0; + float sliderval = GUI.HorizontalSlider(sliderRect1, property.floatValue, (float)min, (float)max); + if (EditorGUI.EndChangeCheck()) { + double rounded = Math.Round(sliderval, attr.DecimalPlaces); + double clamped = (attr.Clamp ? (rounded < realmin ? realmin : (rounded > realmax ? realmax : rounded)) : rounded); + property.doubleValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + if (useInverse) { + EditorGUI.LabelField(secondRow, attr.InverseName); + EditorGUI.BeginChangeCheck(); + EditorGUI.indentLevel = 0; + float sliderinv = 1f / GUI.HorizontalSlider(sliderRect2, property.floatValue, (float)max, (float)min); + double val = Math.Round(1d / sliderinv, attr.InverseDecimalPlaces); + double clamped = attr.Clamp ? (val < realmin ? realmin : (val > realmax ? realmax : val)) : val; + if (EditorGUI.EndChangeCheck()) { + if (isDouble) { + property.doubleValue = clamped; + } else { + property.floatValue = (float)clamped; + } + property.serializedObject.ApplyModifiedProperties(); + } + } + + // Double editable fields + if (isDouble) { + EditorGUI.BeginChangeCheck(); + double newval = EditorGUI.DelayedDoubleField(valRect1, GUIContent.none, Math.Round(property.doubleValue, MAX_PLACES)); + if (EditorGUI.EndChangeCheck()) { + //double rounded = Math.Round(d, places); + double clamped = attr.Clamp ? (newval < realmin ? realmin : (newval > realmax ? realmax : newval)) : newval; + property.doubleValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + + if (useInverse) { + EditorGUI.BeginChangeCheck(); + // Cast to float going into the field rendering, to limit the number of shown characters, but doesn't actually affect the accuracy of the value. + double newinv = 1d / EditorGUI.DelayedDoubleField(valRect2, Math.Round(1d / property.doubleValue, MAX_PLACES)); + if (EditorGUI.EndChangeCheck()) { + //double rounded = Math.Round(newinv, attr.InverseDecimalPlaces); + double clamped = (float)(attr.Clamp ? (newinv < realmin ? realmin : (newinv > realmax ? realmax : newinv)) : newinv); + property.doubleValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + } + } + // Float editable fields + else { + EditorGUI.BeginChangeCheck(); + float newval = EditorGUI.DelayedFloatField(valRect1, property.floatValue); + if (EditorGUI.EndChangeCheck()) { + //double rounded = Math.Round(newval, places); + float clamped = (float)(attr.Clamp ? (newval < realmin ? realmin : (newval > realmax ? realmax : newval)) : newval); + property.doubleValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + + if (useInverse) { + EditorGUI.BeginChangeCheck(); + float newinv = 1f / EditorGUI.DelayedFloatField(valRect2, 1f / property.floatValue); + if (EditorGUI.EndChangeCheck()) { + //double rounded = Math.Round(newinv, places); + float clamped = (float)(attr.Clamp ? (newinv < realmin ? realmin : (newinv > realmax ? realmax : newinv)) : newinv); + property.floatValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + } + } + + // No slider handling. Just using a regular property so that dragging over the label works. + } else { + + EditorGUI.BeginChangeCheck(); + EditorGUI.PropertyField(position, property, label); + if (EditorGUI.EndChangeCheck()) { + double newval = property.doubleValue; + if (realmin != 0 || realmax != 0) { + double clamped = attr.Clamp ? newval < realmin ? realmin : (newval > realmax ? realmax : newval) : newval; + property.doubleValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + } + if (useInverse) { + EditorGUI.BeginChangeCheck(); + double newval = 1d / EditorGUI.DelayedFloatField(secondRow, attr.InverseName, (float)Math.Round(1d / property.doubleValue, MAX_PLACES)); + if (EditorGUI.EndChangeCheck()) { + if (realmin != 0 || realmax != 0) { + double clamped = attr.Clamp ? newval < realmin ? realmin : (newval > realmax ? realmax : newval) : newval; + property.doubleValue = clamped; + } else { + property.doubleValue = newval; + } + property.serializedObject.ApplyModifiedProperties(); + } + } + } + + } else if (property.propertyType == SerializedPropertyType.Integer) { + // Slider + if (useSlider) { + EditorGUI.LabelField(position, label, GUIContent.none); + + //float drag = DrawLabelDrag(position); + //if (drag != 0) { + // int draggedval = property.intValue + (int)drag; + // int clamped = attr.Clamp ? (int)(draggedval < realmin ? realmin : (draggedval > realmax ? realmax : draggedval)) : draggedval; + // property.intValue = clamped; + // property.serializedObject.ApplyModifiedProperties(); + //} + + // Int slider + EditorGUI.BeginChangeCheck(); + EditorGUI.indentLevel = 0; + int sliderval = (int)GUI.HorizontalSlider(sliderRect1, property.intValue, (float)min, (float)max); + if (EditorGUI.EndChangeCheck()) { + property.intValue = sliderval; + property.serializedObject.ApplyModifiedProperties(); + } + + // Int input Field + EditorGUI.BeginChangeCheck(); + int i = EditorGUI.DelayedIntField(valRect1, property.intValue); + if (EditorGUI.EndChangeCheck()) { + property.intValue = (int)(i < realmin ? realmin : (i > realmax ? realmax : i)); + property.serializedObject.ApplyModifiedProperties(); + } + + if (useInverse) { + EditorGUI.LabelField(secondRow, attr.InverseName); + + // Inverse slider for Ints + EditorGUI.BeginChangeCheck(); + EditorGUI.indentLevel = 0; + float sliderinv = 1f / GUI.HorizontalSlider(sliderRect2, property.intValue, (float)max, (float)min); + if (EditorGUI.EndChangeCheck()) { + property.intValue = (int)Math.Round(1f / sliderinv); + property.serializedObject.ApplyModifiedProperties(); + } + + // inverse Int field when slider exists + EditorGUI.BeginChangeCheck(); + float newinv = 1f / EditorGUI.DelayedFloatField(valRect2, (float)Math.Round(1d / property.intValue, MAX_PLACES)); + if (EditorGUI.EndChangeCheck()) { + int val = (int)(1d / newinv); + int clamped = (int)(attr.Clamp ? (val < realmin ? realmin : (val > realmax ? realmax : val)) : val); + property.intValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + } + + + // No slider handling. Just using a regular property so that dragging over the label works. + } else { + EditorGUI.BeginChangeCheck(); + EditorGUI.PropertyField(position, property, label); + if (EditorGUI.EndChangeCheck() && (realmin != 0 || realmax != 0)) { + int intval = property.intValue; + int clamped = attr.Clamp ? (int)(intval < realmin ? realmin : (intval > realmax ? realmax : intval)) : intval; + property.intValue = clamped; + property.serializedObject.ApplyModifiedProperties(); + } + + if (useInverse) { + EditorGUI.BeginChangeCheck(); + double newval = EditorGUI.DelayedFloatField(secondRow, attr.InverseName, (float)Math.Round(1d / property.intValue, MAX_PLACES)); + if (EditorGUI.EndChangeCheck()) { + double rounded = 1d / newval; + if (realmin != 0 || realmax != 0) { + double clamped = attr.Clamp ? rounded < realmin ? realmin : (rounded > realmax ? realmax : rounded) : rounded; + property.doubleValue = clamped; + } else + property.doubleValue = rounded; + } + } + } + + // Fallback for unsupported field types + } else { + Debug.LogWarning(nameof(UnitAttribute) + " only is applicable to double, float and integer field types."); + EditorGUI.PropertyField(position, property, label, true); + } + } + + if (attr.Unit != Units.None) { + var (style, name) = GetOverlayStyle(attr.Unit); + GUI.Label(position, position.width - EditorGUIUtility.labelWidth > 80 ? name : "", style); + } + if (useInverse && attr.InverseUnit != Units.Units) { + var (style, name) = GetOverlayStyle(attr.InverseUnit); + GUI.Label(secondRow, secondRow.width - EditorGUIUtility.labelWidth > 80 ? name : "", style); + } + + EditorGUI.indentLevel = holdIndent; + EditorGUI.EndProperty(); + } + + + // Makes the label field draggable - TODO: doesn't handle dragging out of window + private static float DrawLabelDrag(Rect rect) { + + rect.width = EditorGUIUtility.labelWidth; + + EditorGUIUtility.AddCursorRect(rect, MouseCursor.SlideArrow); + var e = Event.current; + + if (e.type == EventType.MouseDrag && rect.Contains(e.mousePosition)) { + return e.delta.x; + } + + return 0; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/UnityAssetGuidAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(UnityAssetGuidAttribute))] + public class UnityAssetGuidAttributeDrawer : PropertyDrawerWithErrorHandling { + + private NetworkObjectGuidDrawer _guidDrawer = null; + + protected override void OnGUIInternal(Rect position, SerializedProperty property, GUIContent label) { + + string guid; + position.width -= 40; + + if (GetUnityLeafType(fieldInfo.FieldType) == typeof(NetworkObjectGuid)) { + if (_guidDrawer == null) { + _guidDrawer = new NetworkObjectGuidDrawer(); + } + _guidDrawer.OnGUI(position, property, label); + guid = NetworkObjectGuidDrawer.GetValue(property).ToString("N"); + } else { + using (new FusionEditorGUI.PropertyScopeWithPrefixLabel(position, label, property, out position)) { + EditorGUI.PropertyField(position, property, GUIContent.none, false); + guid = property.stringValue; + } + } + + string assetPath = string.Empty; + + if (!string.IsNullOrEmpty(guid)) { + assetPath = AssetDatabase.GUIDToAssetPath(guid); + } + + using (new EditorGUI.DisabledScope(string.IsNullOrEmpty(assetPath))) { + position.x += position.width; + position.width = 40; + bool buttonPressed; + + bool wasEnabled = GUI.enabled; + GUI.enabled = true; + buttonPressed = GUI.Button(position, "Ping"); + GUI.enabled = wasEnabled; + + if (buttonPressed) { + // ping the main assets + EditorGUIUtility.PingObject(AssetDatabase.LoadMainAssetAtPath(assetPath)); + } + } + + if (!string.IsNullOrEmpty(assetPath)) { + var asset = AssetDatabase.LoadMainAssetAtPath(assetPath); + if (asset == null) { + SetError($"Asset with this guid does not exist. Last path:\n{assetPath}"); + } else { + SetInfo($"Asset path:\n{assetPath}"); + } + } + } + + private static Type GetUnityLeafType(Type type) { + if (type.HasElementType) { + type = type.GetElementType(); + } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { + type = type.GetGenericArguments()[0]; + } + return type; + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/VersaMaskAttributeDrawer.cs + +namespace Fusion.Editor { + + using UnityEditor; + + [CustomPropertyDrawer(typeof(VersaMaskAttribute))] + public class VersaMaskAttributeDrawer : VersaMaskDrawer { + protected override bool FirstIsZero { + get { + var attr = attribute as VersaMaskAttribute; + return attr.definesZero; + } + } + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/VersaMaskDrawer.cs + +// --------------------------------------------------------------------------------------------- +// PhotonNetwork Framework for Unity - Copyright (C) 2020 Exit Games GmbH +// developer@exitgames.com +// --------------------------------------------------------------------------------------------- + +namespace Fusion.Editor { + using UnityEngine; + using UnityEditor; + using System.Collections.Generic; + using System; + + [CanEditMultipleObjects] + public abstract class VersaMaskDrawer : PropertyDrawer { + protected static GUIContent reuseGC = new GUIContent(); + protected abstract bool FirstIsZero { get; } + //protected virtual bool ShowMaskBits { get { return true; } } + + static Dictionary _nameArrayLookup; + + protected virtual string[] GetStringNames(SerializedProperty property, bool removeZero) { + + if (_nameArrayLookup == null) { + _nameArrayLookup = new Dictionary(); + } + if (_nameArrayLookup.TryGetValue(fieldInfo.FieldType, out var names)) { + return names; + } + + string[] rawnames; + var maskattr = attribute as VersaMaskAttribute; + if (maskattr.castTo != null) { + rawnames = System.Enum.GetNames(maskattr.castTo); + } else { + rawnames = property.enumDisplayNames; + } + + // Remove Zero value from the array if need be. + int len = removeZero ? rawnames.Length - 1 : rawnames.Length; + names = new string[len]; + for (int i = 0; i < len; i++) { + names[i] = rawnames[FirstIsZero ? (i + 1) : i]; + } + _nameArrayLookup.Add(fieldInfo.FieldType, names); + return names; + } + + const float PAD = 4; + const float LINE_SPACING = 18; + const float BOX_INDENT = 0; //16 - PAD; + const float FOLDOUT_WIDTH = 16; + + protected static SerializedProperty currentProperty; + protected int maskValue; + + + public override void OnGUI(Rect r, SerializedProperty property, GUIContent label) { + + currentProperty = property; + var attr = attribute as VersaMaskAttribute; + + bool usefoldout = !attr.AlwaysExpanded && UseFoldout(label); + + if (usefoldout) { + property.isExpanded = EditorGUI.Toggle(new Rect(r) { xMin = r.xMin + EditorGUIUtility.labelWidth, height = LINE_SPACING, width = FOLDOUT_WIDTH }, property.isExpanded, (GUIStyle)"Foldout"); + } + + label = EditorGUI.BeginProperty(r, label, property); + + /// For extended drawer types, the mask field needs to be named mask + var mask = property.FindPropertyRelative("mask"); + + /// ELSE If this drawer is being used as an attribute, then the property itself is the enum mask. + if (mask == null) + mask = property; + + maskValue = mask.intValue; + + int tempmask; + Rect br = new Rect(r) { xMin = r.xMin + BOX_INDENT }; + Rect ir = new Rect(br) { height = LINE_SPACING }; + + Rect labelRect = new Rect(r) { /*xMin = usefoldout ? r.xMin + 14 : r.xMin, */height = LINE_SPACING }; + + //TODO: Cache most of this in dictionaries to reduce GC + var namearray = GetStringNames(property, FirstIsZero); + ///// Remove Zero value from the array if need be. + //int len = FirstIsZero ? stringNames.Length - 1 : stringNames.Length; + //var namearray = new string[len]; + //for (int i = 0; i < len; i++) + // namearray[i] = stringNames[FirstIsZero ? (i + 1) : i]; + + if (attr.AlwaysExpanded || (usefoldout && property.isExpanded)) { + tempmask = 0; + + EditorGUI.LabelField(new Rect(br) { yMin = br.yMin + LINE_SPACING }, "", EditorStyles.helpBox); + ir.xMin += PAD * 2; + ir.y += PAD; + + var ShowMaskBits = attr.ShowBitmask; + + string drawmask = ShowMaskBits ? "" : null; + + int len = namearray.Length; + for (int i = 0; i < len; ++i) { + ir.y += LINE_SPACING; + + int offsetbit = 1 << i; + + if (EditorGUI.ToggleLeft(ir, "", ((mask.intValue & offsetbit) != 0))) { + tempmask |= offsetbit; + if (ShowMaskBits) { + drawmask = "1" + drawmask; + } + } else if (ShowMaskBits) { + drawmask = "0" + drawmask; + } + + using (new EditorGUI.DisabledScope((mask.intValue & offsetbit) != 0 == false)) { + EditorGUI.LabelField(new Rect(ir) { xMin = ir.xMin + 24 }, new GUIContent(namearray[i])); + } + } + + // Draw Label + EditorGUI.LabelField(labelRect, label, GUIContent.none); + + // Draw Bitmask + if (ShowMaskBits) { + reuseGC.text = $" [{drawmask}]"; + EditorGUI.LabelField(new Rect(labelRect) { xMin = labelRect.xMin + +EditorGUIUtility.labelWidth + FOLDOUT_WIDTH }, reuseGC); + } else { + var valueAreaRect = new Rect(labelRect) { xMin = labelRect.xMin + EditorGUIUtility.labelWidth + FOLDOUT_WIDTH }; + if (GUI.Button(new Rect(valueAreaRect) { width = valueAreaRect.width * .5f }, "All")) { + tempmask = (int)(~0); + } + if (GUI.Button(new Rect(valueAreaRect) { xMin = valueAreaRect.xMin + valueAreaRect.width * .5f }, "None")) { + tempmask = 0; + } + } + + } else { + reuseGC.text = null; + + EditorGUI.LabelField(r, label, reuseGC, EditorStyles.label); + + tempmask = EditorGUI.MaskField(new Rect(r) { xMin = r.xMin + EditorGUIUtility.labelWidth + FOLDOUT_WIDTH}, GUIContent.none/* usefoldout ? " " : ""*/, mask.intValue, namearray); + } + + if (tempmask != mask.intValue) { + Undo.RecordObject(property.serializedObject.targetObject, "Change Mask Selection"); + mask.intValue = tempmask; + maskValue = tempmask; + property.serializedObject.ApplyModifiedProperties(); + } + + EditorGUI.EndProperty(); + } + + protected bool UseFoldout(GUIContent label) { + return label.text != null && label.text != ""; + } + + //protected void EnsureHasEnumtype() + //{ + // /// Set the attribute enum type if it wasn't set by user in attribute arguments. + // if (attribute == null) + // { + // Debug.LogWarning("Null Attribute"); + // return; + // } + // var attr = attribute as VersaMaskAttribute; + // var type = attr.castTo; + // if (type == null) + // attr.castTo = fieldInfo.FieldType; + //} + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + currentProperty = property; + + var attr = attribute as VersaMaskAttribute; + + bool expanded = (attr.AlwaysExpanded || (property.isExpanded && UseFoldout(label))); + + if (expanded) { + var stringNames = GetStringNames(property, FirstIsZero); + return LINE_SPACING * (stringNames.Length + 1) + PAD * 2; + } else + return base.GetPropertyHeight(property, label); + } + } + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/CustomTypes/WarnIfAttributeDrawer.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomPropertyDrawer(typeof(WarnIfAttribute))] + public class WarnIfAttributeDrawer : DoIfAttributeDrawer { + + public WarnIfAttribute Attribute => (WarnIfAttribute)attribute; + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { + return base.GetPropertyHeight(property, label); + } + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { + + //property.DrawPropertyUsingFusionAttributes(position, label, fieldInfo); + base.OnGUI(position, property, label); + + double condValue = GetCompareValue(property, Attribute.ConditionMember, fieldInfo); + + // Try is needed because when first selecting or after recompile, Unity throws errors when trying to inline a element like this. + try { + if (CheckDraw(Attribute, condValue)) { + BehaviourEditorUtils.DrawWarnBox(Attribute.Message, (MessageType)Attribute.MessageType, (FusionGUIStyles.GroupBoxType)Attribute.MessageType); + } + } catch { + + } + + } + } + +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/DebugDllToggle.cs + +namespace Fusion.Editor { + using System; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEngine; + + public static class DebugDllToggle { + + const string FusionRuntimeDllGuid = "e725a070cec140c4caffb81624c8c787"; + + public static string[] FileList = new[] { + "Fusion.Common.dll", + "Fusion.Common.pdb", + "Fusion.Runtime.dll", + "Fusion.Runtime.pdb", + "Fusion.Realtime.dll", + "Fusion.Realtime.pdb", + "Fusion.Sockets.dll", + "Fusion.Sockets.pdb"}; + + [MenuItem("Fusion/Toggle Debug Dlls")] + public static void Toggle() { + + // find the root + string dir; + { + var fusionRuntimeDllPath = AssetDatabase.GUIDToAssetPath(FusionRuntimeDllGuid); + if (string.IsNullOrEmpty(fusionRuntimeDllPath)) { + Debug.LogError($"Cannot locate assemblies directory"); + return; + } else { + dir = PathUtils.MakeSane(Path.GetDirectoryName(fusionRuntimeDllPath)); + } + } + + var dllsAvailable = FileList.All(f => File.Exists($"{dir}/{f}")); + var debugFilesAvailable = FileList.All(f => File.Exists($"{dir}/{f}.debug")); + + if (dllsAvailable == false) { + Debug.LogError("Cannot find all fusion dlls"); + return; + } + + if (debugFilesAvailable == false) { + Debug.LogError("Cannot find all specially marked .debug dlls"); + return; + } + + if (FileList.Any(f => new FileInfo($"{dir}/{f}.debug").Length == 0)) { + Debug.LogError("Debug dlls are not valid"); + return; + } + + try { + foreach (var f in FileList) { + var tempFile = FileUtil.GetUniqueTempPathInProject(); + FileUtil.MoveFileOrDirectory($"{dir}/{f}", tempFile); + FileUtil.MoveFileOrDirectory($"{dir}/{f}.debug", $"{dir}/{f}"); + FileUtil.MoveFileOrDirectory(tempFile, $"{dir}/{f}.debug"); + File.Delete(tempFile); + } + + if (new FileInfo($"{dir}/{FileList[0]}").Length > + new FileInfo($"{dir}/{FileList[0]}.debug").Length) { + Debug.Log("Activated Fusion DEBUG dlls"); + } + else { + Debug.Log("Activated Fusion RELEASE dlls"); + } + } catch (Exception e) { + Debug.LogAssertion(e); + Debug.LogError($"Failed to rename files"); + } + + AssetDatabase.Refresh(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/EditorRecompileHook.cs + +namespace Fusion.Editor { + using UnityEditor; + using UnityEditor.Compilation; + + [InitializeOnLoad] + public static class EditorRecompileHook { + static EditorRecompileHook() { + AssemblyReloadEvents.beforeAssemblyReload += ShutdownRunners; + CompilationPipeline.compilationStarted += _ => ShutdownRunners(); + } + + static void ShutdownRunners() { + var runners = NetworkRunner.GetInstancesEnumerator(); + + while (runners.MoveNext()) { + runners.Current.Shutdown(); + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionGUIStyles.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + public static class FusionGUIStyles { + + const string GRAPHICS_FOLDER = "EditorGraphics/"; + //const string HEADER_BACKS_PATH = "ComponentHeaderGraphics/Backs/"; + //const string GROUPBOX_FOLDER = GRAPHICS_FOLDER + "GroupBox/"; + const string FONT_PATH = "ComponentHeaderGraphics/Fonts/Oswald-Header"; + const string ICON_PATH = "ComponentHeaderGraphics/Icons/"; + + //// Extra safety code to deal with these styles being used in the inspector for a currently selected object. + //// The styles array never gets nulled if they are being used for some reason, but the textures do get nulled. + //// This OnPlayModeState change to exit detects this and nulls the styles manually. + //static FusionGUIStyles() { + // EditorApplication.playModeStateChanged += OnPlayModeState; + //} + //private static void OnPlayModeState(PlayModeStateChange state) { + // if (state == PlayModeStateChange.EnteredEditMode) { + // _groupBoxStyles = null; + // _fusionHeaderStyles = null; + // } + //} + + + private static Color[] _headerColors = new Color[12] { + // none + new Color(), + // gray + new Color(0.35f, 0.35f, 0.35f), + // blue + new Color(0.15f, 0.25f, 0.6f), + // red + new Color(0.5f, 0.1f, 0.1f), + // green + new Color(0.1f, 0.4f, 0.1f), + // orange + new Color(0.6f, 0.35f, 0.1f), + // black + new Color(0.1f, 0.1f, 0.1f), + // steel + new Color(0.32f, 0.35f, 0.38f), + // sand + new Color(0.38f, 0.35f, 0.32f), + // olive + new Color(0.25f, 0.33f, 0.15f), + // cyan + new Color(0.25f, 0.5f, 0.5f), + // violet + new Color(0.35f, 0.2f, 0.4f), + }; + + public enum GroupBoxType { + None, + Info, + Warn, + Error, + Help, + Gray, + Steel, + Sand, + InlineHelpOuter, + InlineHelpInner, + Outline, + } + + private struct GroupBoxInfo { + public string Name; + public Color? BackColor; + public Color? OutlineColor; + public Color? FontColor; + public int? Padding; + public RectOffset Margin; + public int? BorderWidth; + public bool? RichTextBox; + public GroupBoxInfo(string name, Color? backColor = null, Color? outlineColor = null, Color? fontColor = null, int? padding = null, int? borderWidth = null, bool? richTextBox = null, RectOffset margin = null) { + Name = name; + BackColor = backColor; + OutlineColor = outlineColor; + FontColor = fontColor; + Padding = padding; + Margin = margin; + BorderWidth = borderWidth; + RichTextBox = richTextBox; + } + } + + static Color HelpOuterColor = EditorGUIUtility.isProSkin ? new Color(0.431f, 0.490f, 0.529f, 1.000f) : new Color(0.478f, 0.639f, 0.800f); //new Color(0.451f, 0.675f, 0.898f); + static Color HelpInnerColor = EditorGUIUtility.isProSkin ? new Color(0.317f, 0.337f, 0.352f, 1.000f) : new Color(0.686f, 0.776f, 0.859f); + + private static GroupBoxInfo[] _groupBoxInfo = new GroupBoxInfo[] { + // None + new GroupBoxInfo(), + // Info + new GroupBoxInfo() { BackColor = new Color(0.50f, 0.50f, 0.50f, 0.20f), OutlineColor = Color.black}, + // Warn + new GroupBoxInfo() { + BackColor = EditorGUIUtility.isProSkin ? new Color(0.80f, 0.65f, 0.20f, 0.25f) : new Color(1.00f, 0.96f, 0.80f, 0.20f), OutlineColor = Color.black}, + // Error + new GroupBoxInfo() { BackColor = new Color(0.80f, 0.00f, 0.00f, 0.66f), OutlineColor = Color.black}, + // Help + new GroupBoxInfo() { BackColor = new Color(0.50f, 0.50f, 0.50f, 0.20f), OutlineColor = Color.black}, + // Gray + new GroupBoxInfo() { BackColor = new Color(0.40f, 0.40f, 0.40f, 0.20f), OutlineColor = Color.black}, + // Steel + new GroupBoxInfo() { BackColor = new Color(0.45f, 0.50f, 0.55f, 0.20f), OutlineColor = Color.black}, + // Sand + new GroupBoxInfo() { BackColor = new Color(0.55f, 0.50f, 0.45f, 0.20f), OutlineColor = Color.black}, + + // Outer Help + new GroupBoxInfo() { + BackColor = HelpOuterColor, + OutlineColor = HelpOuterColor, + }, + // Inner Help + new GroupBoxInfo() { + BackColor = HelpInnerColor, + OutlineColor = HelpOuterColor, + BorderWidth = 4, + Padding = 12, + RichTextBox = true + }, + // Outline + new GroupBoxInfo() { + BackColor = new Color(0.0f, 0.0f, 0.0f, 0f), + OutlineColor = EditorGUIUtility.isProSkin ? new Color(0,0,0, 0.5f) : new Color(0,0,0, 0.5f) }, + }; + + + public const int HEADER_CORNER_RADIUS = 4; + + // Creates the "?" inline help icon with pre-rendered alpha channel, to avoid resorting to an editor resource file. + static Texture2D _questionMarkTexture; + public static Texture2D QuestionMarkTexture { + + get { + if (_questionMarkTexture == null) { + var alphas + = new float[,]{ + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.05f, 0.04f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.12f, 0.74f, 0.88f, 0.88f, 0.65f, 0.18f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.09f, 0.14f, 0.14f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.86f, 1.00f, 1.00f, 1.00f, 0.88f, 0.11f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.62f, 1.00f, 1.00f, 0.57f, 0.14f, 0.86f, 0.86f, 0.86f, 0.14f, 0.00f, 0.00f, 0.00f, 0.61f, 1.00f, 1.00f, 1.00f, 1.00f, 0.57f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.62f, 1.00f, 1.00f, 0.57f, 0.14f, 1.00f, 1.00f, 1.00f, 0.86f, 0.25f, 0.00f, 0.00f, 0.25f, 0.18f, 0.42f, 1.00f, 1.00f, 0.80f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.62f, 1.00f, 1.00f, 0.57f, 0.14f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.43f, 0.00f, 0.00f, 0.00f, 0.14f, 1.00f, 1.00f, 0.84f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.18f, 0.29f, 0.29f, 0.14f, 0.00f, 0.14f, 0.14f, 0.71f, 1.00f, 1.00f, 1.00f, 0.86f, 0.71f, 0.71f, 0.86f, 1.00f, 1.00f, 0.72f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.71f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.48f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.43f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.71f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.09f, 0.45f, 0.63f, 0.69f, 0.59f, 0.40f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + {0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + }; + + // Previous thinner question mark + // = new float[,]{ + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.14f, 0.57f, 0.57f, 0.43f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.43f, 1.00f, 1.00f, 1.00f, 0.86f, 0.14f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.71f, 0.71f, 0.43f, 0.00f, 0.29f, 0.57f, 0.57f, 0.14f, 0.00f, 0.00f, 0.00f, 0.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.71f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 1.00f, 1.00f, 0.43f, 0.00f, 0.43f, 1.00f, 1.00f, 0.86f, 0.14f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.57f, 1.00f, 1.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 1.00f, 1.00f, 0.43f, 0.00f, 0.43f, 0.86f, 1.00f, 1.00f, 0.86f, 0.29f, 0.00f, 0.00f, 0.00f, 0.00f, 0.43f, 1.00f, 1.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.14f, 1.00f, 1.00f, 1.00f, 0.71f, 0.29f, 0.29f, 0.29f, 0.86f, 1.00f, 1.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.14f, 0.86f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.43f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.57f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 0.71f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.07f, 0.33f, 0.47f, 0.40f, 0.23f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //{0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, 0.00f, }, + //}; + + var tex = new Texture2D(32, 32, TextureFormat.RGBA32, false); + for (int x = 0; x < 32; ++x) { + for (int y = 0; y < 32; ++y) { + tex.SetPixel(x, y, EditorGUIUtility.isProSkin ? new Color(1, 1, 1, alphas[x, y]) : new Color(0, 0, 0, alphas[x, y])); + } + } + tex.Apply(); + _questionMarkTexture = tex; + } + return _questionMarkTexture; + } + } + + static Color HelpIconColorSelected = EditorGUIUtility.isProSkin ? new Color(0.000f, 0.500f, 1.000f, 1.000f) : new Color(0.502f, 0.749f, 1.000f, 1.000f); + static Color HelpIconColorUnselected = EditorGUIUtility.isProSkin ? new Color(1.000f, 1.000f, 1.000f, 0.250f) : new Color(0.000f, 0.000f, 0.000f, 0.250f); + + static Texture2D _helpIconSelected; + public static Texture2D HelpIconSelected { + get { + if (_helpIconSelected == null) { + var tex = new Texture2D(32, 32, TextureFormat.RGBA32, false); + tex.ClearTexture(new Color(0.06f, 0.5f, 0.75f, 0)); + tex.RenderCircle(13, HelpIconColorSelected, Blend.Overwrite); + tex.OverlayTexture(QuestionMarkTexture, .9f); + tex.alphaIsTransparency = true; + tex.Apply(); + _helpIconSelected = tex; + } + return _helpIconSelected; + } + } + + static Texture2D _helpIconUnselected; + public static Texture2D HelpIconUnselected { + get { + if (_helpIconUnselected == null) { + var tex = new Texture2D(32, 32, TextureFormat.RGBA32, false); + tex.ClearTexture(HelpIconColorUnselected * new Color(1, 1, 1, 0)); + tex.RenderCircle(13, HelpIconColorUnselected, Blend.Overwrite); + tex.ClearCircle(EditorGUIUtility.pixelsPerPoint > 1 ? 10 : 11, true); + tex.OverlayTexture(QuestionMarkTexture, .25f); + tex.alphaIsTransparency = true; + tex.filterMode = FilterMode.Trilinear; + tex.Apply(); + _helpIconUnselected = tex; + } + return _helpIconUnselected; + } + } + + static Texture2D[] _headerTextures; + public static Texture2D[] HeaderTextures { + get { + if (_headerTextures == null || _headerTextures[0] == null) { + _headerTextures = new Texture2D[_headerColors.Length]; + for(int i = 0; i < _headerColors.Length; ++i) { + _headerTextures[i] = GenerateRoundedBoxTexture(HEADER_CORNER_RADIUS, new GroupBoxInfo($"Header{i}", _headerColors[i], Color.black), false); + } + } + return _headerTextures; + } + } + static Texture2D[] _headerTexturesHiRez; + public static Texture2D[] HeaderTexturesHiRez { + get { + if (_headerTexturesHiRez == null || _headerTexturesHiRez[0] == null) { + _headerTexturesHiRez = new Texture2D[_headerColors.Length]; + for (int i = 0; i < _headerColors.Length; ++i) { + _headerTexturesHiRez[i] = GenerateRoundedBoxTexture(HEADER_CORNER_RADIUS, new GroupBoxInfo($"Header{i}", _headerColors[i], Color.black), true); + } + } + return _headerTexturesHiRez; + } + } + + static Texture2D[] _groupBoxTextures; + public static Texture2D[] GroupBoxTextures { + get { + if (_groupBoxTextures == null || _groupBoxTextures[0] == null) { + _groupBoxTextures = new Texture2D[_groupBoxInfo.Length]; + for (int i = 0; i < _groupBoxInfo.Length; ++i) { + _groupBoxTextures[i] = GenerateRoundedBoxTexture(HEADER_CORNER_RADIUS/* + _groupBoxInfo[i].BorderWidth.GetValueOrDefault(1)*/, _groupBoxInfo[i], false); + } + } + return _groupBoxTextures; + } + } + + static Texture2D[] _groupBoxTexturesHiRez; + public static Texture2D[] GroupBoxTexturesHiRez { + get { + if (_groupBoxTexturesHiRez == null || _groupBoxTexturesHiRez[0] == null) { + _groupBoxTexturesHiRez = new Texture2D[_groupBoxInfo.Length]; + for (int i = 0; i < _groupBoxInfo.Length; ++i) { + _groupBoxTexturesHiRez[i] = GenerateRoundedBoxTexture(HEADER_CORNER_RADIUS/* + _groupBoxInfo[i].BorderWidth.GetValueOrDefault(1)*/, _groupBoxInfo[i], true); + } + } + return _groupBoxTexturesHiRez; + } + } + + static void OverlayTexture(this Texture2D tex, Texture2D overlay, float opacity) { + for (int x = 0; x < tex.width; ++x) { + for (int y = 0; y < tex.width; ++y) { + var b = tex.GetPixel(x, y); + var o = overlay.GetPixel(x, y) * new Color(1, 1, 1, opacity); + var c = Color.Lerp(b, o, o.a); + tex.SetPixel(x, y, c); + } + } + } + + static Texture2D GenerateRoundedBoxTexture(int radius, GroupBoxInfo info, bool hirez) { + + if (hirez) { + radius *= 2; + } + var border = hirez ? info.BorderWidth.GetValueOrDefault(1) * 2 : info.BorderWidth.GetValueOrDefault(1); + + var tex = new Texture2D((radius + border) * 2, (radius + border) * 2, TextureFormat.RGBA32, false); + bool hasOutline = info.OutlineColor != null; + var color = info.BackColor.GetValueOrDefault(); + tex.ClearTexture(color * new Color(1, 1, 1, 0)); + bool outlineOnly = color.a == 0; + + if (hasOutline) { + tex.RenderCircle(radius + border, info.OutlineColor.Value, Blend.Overwrite); + } + + + if (outlineOnly) { + tex.RenderCircle(hasOutline ? radius /*- border */: radius, info.OutlineColor.GetValueOrDefault(), Blend.Subtract); + } else { + tex.RenderCircle(hasOutline ? radius /*- border*/ : radius, color/* new Color(color.r, color.g, color.b, 1f)*/, hasOutline ? Blend.Overlay : Blend.Overwrite); + tex.ApplyAlpha(color.a); + } + + tex.alphaIsTransparency = true; + tex.filterMode = FilterMode.Point; + tex.mipMapBias = 0; + tex.Apply(); + return tex; + } + + private static void ClearTexture(this Texture2D tex, Color color) { + for(int x = 0; x < tex.width; ++x) { + for(int y = 0; y < tex.width; ++y) { + tex.SetPixel(x, y, color); + } + } + } + + enum Blend { + Overwrite, + Overlay, + Subtract + } + + private static void RenderCircle(this Texture2D tex, int r, Color color, Blend blend, int? midX = null, int? midY = null) { + if (midX == null) { + midX = tex.width / 2; + } + + if (midY == null) { + midY = tex.height / 2; + } + + var midx = midX.Value; + var midy = midY.Value; + + for (int x = 0; x < r; ++x) { + for (int y = 0; y < r; ++y) { + double h = Math.Abs(Math.Sqrt(x * x + y * y)); + float a = Mathf.Clamp01((float)(r - h)); + + var e = tex.GetPixel(midx + x, midy + y); + var c = color * new Color(1f, 1f, 1f, a); + if (blend == Blend.Overwrite) { + c = color * new Color(1, 1, 1, a * color.a); + } + else if (blend == Blend.Overlay) { + if (a > 0) { + c = Color.Lerp(e, c, a); + c.a = e.a; + } else { + c = e; + } + } + else if (blend == Blend.Subtract) { + c = new Color(e.r, e.g, e.b, a > 0 ? Math.Min(e.a, (1-a) * color.a) : e.a); + } + tex.SetPixel(midx + 0 + x, midy + 0 + y, c); + tex.SetPixel(midx - 1 - x, midy + 0 + y, c); + tex.SetPixel(midx + 0 + x, midy - 1 - y, c); + tex.SetPixel(midx - 1 - x, midy - 1 - y, c); + + } + } + } + + private static void ClearCircle(this Texture2D tex, int r, bool blend, int? midX = null, int? midY = null) { + if (midX == null) { + midX = tex.width / 2; + } + + if (midY == null) { + midY = tex.height / 2; + } + + var midx = midX.Value; + var midy = midY.Value; + + for (int x = 0; x < tex.width; ++x) { + for (int y = 0; y < tex.height; ++y) { + double h = Math.Abs((Math.Sqrt(x * x + y * y))); + float a = Mathf.Clamp01((float)(h - r)); + var e = tex.GetPixel(midx + x, midy + y); + var c = e * new Color(1f, 1f, 1f, a); + tex.SetPixel(midx + 0 + x, midy + 0 + y, c); + tex.SetPixel(midx - 1 - x, midy + 0 + y, c); + tex.SetPixel(midx + 0 + x, midy - 1 - y, c); + tex.SetPixel(midx - 1 - x, midy - 1 - y, c); + + } + } + } + + private static void ApplyAlpha(this Texture2D tex, float alpha) { + int midx = tex.width / 2; + int midy = tex.height / 2; + + Color multiplier = new Color(1, 1, 1, alpha); + + for (int x = 0; x < midx; ++x) { + for (int y = 0; y < midy; ++y) { + var e = tex.GetPixel(midx + x, midy + y); + var c = e * multiplier; + tex.SetPixel(midx + 0 + x, midy + 0 + y, c); + tex.SetPixel(midx - 1 - x, midy + 0 + y, c); + tex.SetPixel(midx + 0 + x, midy - 1 - y, c); + tex.SetPixel(midx - 1 - x, midy - 1 - y, c); + + } + } + } + + private static GUIStyle _warnLabelStyle; + public static GUIStyle WarnLabelStyle { + get { + if (_warnLabelStyle == null) { + _warnLabelStyle = new GUIStyle(EditorStyles.miniLabel) { fontSize = 10, richText = true, wordWrap = true, alignment = TextAnchor.MiddleLeft }; + } + return _warnLabelStyle; + } + } + + private static Texture2D _infoIcon; + public static Texture2D InfoIcon { + get { + if (_infoIcon == null) { + _infoIcon = EditorGUIUtility.FindTexture(EditorGUIUtility.isProSkin ? "d_console.infoicon@2x" : "console.infoicon@2x"); + } + return _infoIcon; + } + } + + private static Texture2D _warnIcon; + public static Texture2D WarnIcon { + get { + if (_warnIcon == null) { + _warnIcon = EditorGUIUtility.FindTexture(EditorGUIUtility.isProSkin ? "d_console.warnicon@2x" : "console.warnicon@2x"); + } + return _warnIcon; + } + } + + private static Texture2D _errorIcon; + public static Texture2D ErrorIcon { + get { + if (_errorIcon == null) { + _errorIcon = EditorGUIUtility.FindTexture(EditorGUIUtility.isProSkin ? "d_console.erroricon@2x" : "console.erroricon@2x"); + } + return _errorIcon; + } + } + + const int IconWidth = 48; + + private static GUIStyle _baseHeaderLabelStyle; + + private static (int fontsize, int padTop) GetHeaderFontAttrs() { + switch (EditorGUIUtility.pixelsPerPoint) { + case 2.0f: + case 3.0f: + case 4.0f: + return (15, 0); + case 1.25f: + case 2.5f: + case 2.25f: + case 2.75f: + case 3.5f: + return (16, -1); + default: + return (17, -1); + } + } + + public static GUIStyle BaseHeaderLabelStyle { + get { + if (_baseHeaderLabelStyle == null) { + var fontattrs = GetHeaderFontAttrs(); + + _baseHeaderLabelStyle = new GUIStyle(EditorStyles.label) { + font = Resources.Load(FONT_PATH), + fontSize = fontattrs.fontsize, + alignment = TextAnchor.UpperLeft, + padding = new RectOffset(5, 5 /*IconWidth*/, fontattrs.padTop, 0), + margin = new RectOffset(0, 0, 0, 0), + border = new RectOffset(3, 3, 3, 3), + clipping = TextClipping.Clip, + wordWrap = true, + + }; + } + return _baseHeaderLabelStyle; + } + } + + private static GUIStyle[] _fusionHeaderStyles; + + internal static GUIStyle GetFusionHeaderBackStyle(int colorIndex) { + + if (_fusionHeaderStyles == null || _fusionHeaderStyles[0] == null) { + string[] colorNames = Enum.GetNames(typeof(EditorHeaderBackColor)); + _fusionHeaderStyles = new GUIStyle[colorNames.Length]; + for (int i = 1; i < colorNames.Length; ++i) { + var style = new GUIStyle(BaseHeaderLabelStyle); + style.normal.background = HeaderTextures[i]; + style.normal.scaledBackgrounds = new Texture2D[] { HeaderTexturesHiRez[i] }; + style.border = new RectOffset(HEADER_CORNER_RADIUS, HEADER_CORNER_RADIUS, HEADER_CORNER_RADIUS, HEADER_CORNER_RADIUS); + //style.fixedHeight = 24; + style.normal.textColor = new Color(1, 1, 1, .9f); + _fusionHeaderStyles[i] = style; + } + } + return _fusionHeaderStyles[colorIndex]; + } + + static Texture2D[] _loadedIcons; + public static Texture2D GetFusionIconTexture(EditorHeaderIcon icon) { + if (_loadedIcons == null || _loadedIcons[0] == null) { + string[] iconNames = Enum.GetNames(typeof(EditorHeaderIcon)); + _loadedIcons = new Texture2D[iconNames.Length]; + for (int i = 1; i < iconNames.Length; ++i) { + _loadedIcons[i] = Resources.Load(ICON_PATH + iconNames[i] + "HeaderIcon"); + } + } + return _loadedIcons[(int)icon]; + } + + + private static GUIStyle[] _groupBoxStyles; + private static GUIStyle GetGroupBoxStyle(GroupBoxType groupType) { + // texture can be reset without the styles going null in some cases (scene changes, exiting play mode, etc), so need to make sure the texture itself has not gone null always. + if (_groupBoxStyles == null || _groupBoxStyles[0] == null || _groupBoxStyles[0].normal.background == null) { + var len = Enum.GetNames(typeof(GroupBoxType)).Length; + _groupBoxStyles = new GUIStyle[len]; + for (int i = 0; i < len; ++i) { + var info = _groupBoxInfo[i]; + _groupBoxStyles[i] = CreateGroupStyle(i, info); + } + } + return _groupBoxStyles[(int)groupType]; + } + + public static GUIStyle GetStyle(this GroupBoxType type) { + return GetGroupBoxStyle(type); + } + + private static GUIStyle CreateGroupStyle(int groupBoxType, GroupBoxInfo info) { + + var style = new GUIStyle(EditorStyles.label); + var border = GroupBoxTextures[groupBoxType].width / 2; + //var border = HEADER_CORNER_RADIUS + info.BorderWidth.GetValueOrDefault(0); + var padding = info.Padding.GetValueOrDefault(10); + var margin = info.Margin != null ? info.Margin : new RectOffset(5, 5, 5, 5); + style.border = new RectOffset(border, border, border, border); + style.padding = new RectOffset(padding, padding, padding, padding); + style.margin = margin; + style.wordWrap = true; + style.normal.background = GroupBoxTextures[groupBoxType]; + style.normal.scaledBackgrounds = new Texture2D[] { GroupBoxTexturesHiRez[groupBoxType] }; + + if (info.RichTextBox.GetValueOrDefault()) { + style.alignment = TextAnchor.UpperLeft; + style.richText = true; + } + return style; + } + } + + +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionHub/FusionHubWindow.cs + + +#if FUSION_WEAVER && UNITY_EDITOR +namespace Fusion.Editor { + + using System; + using System.Collections.Generic; + using UnityEditor; + using UnityEngine; + using EditorUtility = UnityEditor.EditorUtility; + + [InitializeOnLoad] + public partial class FusionHubWindow : EditorWindow { + + const int NAV_WIDTH = 256 + 2; + + private static bool? ready; // true after InitContent(), reset onDestroy, onEnable, etc. + + private static Vector2 windowSize; + private static Vector2 windowPosition = new Vector2(100, 100); + + int currentSection; + // Indicates that the AppId is invalid and needs to be presented on the welcome screen. + static bool _showAppIdInWelcome; + + [MenuItem("Fusion/Fusion Hub &f", false, 0)] + public static void Open() { + if (Application.isPlaying) { + return; + } + + FusionHubWindow window = GetWindow(true, WINDOW_TITLE, true); + window.position = new Rect(windowPosition, windowSize); + _showAppIdInWelcome = !IsAppIdValid(); + window.Show(); + } + + private static void ReOpen() { + if (ready.HasValue && ready.Value == false) { + Open(); + } + + EditorApplication.update -= ReOpen; + } + + + private void OnEnable() { + ready = false; + windowSize = new Vector2(800, 540); + + this.minSize = windowSize; + + // Pre-load Release History + this.PrepareReleaseHistoryText(); + wantsMouseMove = true; + } + + private void OnDestroy() { + ready = false; + } + + private void OnGUI() { + + GUI.skin = FusionHubSkin; + + try { + InitContent(); + + windowPosition = this.position.position; + + // full window wrapper + EditorGUILayout.BeginHorizontal(GUI.skin.window); + { + // Left Nav menu + EditorGUILayout.BeginVertical(GUILayout.MaxWidth(NAV_WIDTH), GUILayout.MinWidth(NAV_WIDTH)); + DrawHeader(); + DrawLeftNavMenu(); + EditorGUILayout.EndVertical(); + + // Right Main Content + EditorGUILayout.BeginVertical(); + DrawContent(); + EditorGUILayout.EndVertical(); + + } + EditorGUILayout.EndHorizontal(); + + DrawFooter(); + + } catch (Exception) { + // ignored + } + + // Force repaints while mouse is over the window, to keep Hover graphics working (Unity quirk) + var timeSinceStartup = Time.realtimeSinceStartupAsDouble; + if (Event.current.type == EventType.MouseMove && timeSinceStartup > _nextForceRepaint) { + // Cap the repaint rate a bit since we are forcing repaint on mouse move + _nextForceRepaint = timeSinceStartup + .05f; + Repaint(); + } + } + + private double _nextForceRepaint; + private Vector2 _scrollRect; + + private void DrawContent() { + { + var section = Sections[currentSection]; + GUILayout.Label(section.Description, headerTextStyle); + + EditorGUILayout.BeginVertical(FusionHubSkin.box); + _scrollRect = EditorGUILayout.BeginScrollView(_scrollRect); + section.DrawMethod.Invoke(); + EditorGUILayout.EndScrollView(); + EditorGUILayout.EndVertical(); + } + } + + static void DrawWelcomeSection() { + + // Top Welcome content box + GUILayout.Label(WELCOME_TEXT); + GUILayout.Space(16); + + if (_showAppIdInWelcome) + DrawSetupAppIdBox(); + } + + static void DrawSetupSection() { + DrawSetupAppIdBox(); + DrawButtonAction(Icon.FusionIcon, "Fusion Network Project Settings", "Network settings specific to Fusion.", + callback: () => NetworkProjectConfigUtilities.PingGlobalConfigAsset(true)); + DrawButtonAction(Icon.PhotonCloud, "Photon App Settings", "Network settings specific to the Photon transport.", + callback: () => { EditorGUIUtility.PingObject(Photon.Realtime.PhotonAppSettings.Instance); Selection.activeObject = Photon.Realtime.PhotonAppSettings.Instance; }); + + } + + static void DrawDocumentationSection() { + DrawButtonAction(Icon.Documentation, "Fusion Introduction", "The Fusion Introduction web page.", callback: OpenURL(UrlFusionIntro)); + DrawButtonAction(Icon.Documentation, "API Reference", "The API library reference documentation.", callback: OpenURL(UrlFusionDocApi)); + } + + static void DrawSamplesSection() { + + GUILayout.Label("Samples", headerLabelStyle); + DrawButtonAction(Resources.Load("FusionHubSampleIcons/tanknarok-logo"), "Fusion Tanknarok Demo", callback: OpenURL(UrlTanks)); + GUILayout.Space(15); + + DrawButtonAction(Icon.Samples, "Hello Fusion Demo", callback: OpenURL(UrlHelloFusion)); + DrawButtonAction(Icon.Samples, "Hello Fusion VR Demo", callback: OpenURL(UrlHelloFusionVr)); + } + + static void DrawRealtimeReleaseSection() { + GUILayout.BeginVertical(); + { + GUILayout.Space(5); + + DrawReleaseHistoryItem("Added:", releaseHistoryTextAdded); + DrawReleaseHistoryItem("Changed:", releaseHistoryTextChanged); + DrawReleaseHistoryItem("Fixed:", releaseHistoryTextFixed); + DrawReleaseHistoryItem("Removed:", releaseHistoryTextRemoved); + DrawReleaseHistoryItem("Internal:", releaseHistoryTextInternal); + } + GUILayout.EndVertical(); + } + + static void DrawFusionReleaseSection() { + GUILayout.Label(fusionReleaseHistory, releaseNotesStyle); + } + + static void DrawReleaseHistoryItem(string label, List items) { + if (items != null && items.Count > 0) { + GUILayout.BeginVertical(); + { + GUILayout.Space(5); + + foreach (string text in items) { + GUILayout.Label(string.Format("- {0}.", text), textLabelStyle); + } + } + GUILayout.EndVertical(); + } + } + + static void DrawSupportSection() { + + GUILayout.BeginVertical(); + GUILayout.Space(5); + GUILayout.Label(SUPPORT, textLabelStyle); + GUILayout.EndVertical(); + + GUILayout.Space(15); + + DrawButtonAction(Icon.Community, DISCORD_HEADER, DISCORD_TEXT, callback: OpenURL(UrlDiscordGeneral)); + DrawButtonAction(Icon.Documentation, DOCUMENTATION_HEADER, DOCUMENTATION_TEXT, callback: OpenURL(UrlFusionDocsOnline)); + } + + static void DrawSetupAppIdBox() { + var realtimeSettings = Photon.Realtime.PhotonAppSettings.Instance; + var realtimeAppId = realtimeSettings.AppSettings.AppIdFusion; + // Setting up AppId content box. + EditorGUILayout.BeginVertical(FusionHubSkin.GetStyle("SteelBox") /*contentBoxStyle*/) ; + { + GUILayout.Label(REALTIME_APPID_SETUP_INSTRUCTIONS); + + DrawButtonAction(Icon.PhotonCloud, "Open the Photon Dashboard", callback: OpenURL(UrlDashboard)); + EditorGUILayout.Space(4); + + EditorGUILayout.BeginHorizontal(FusionHubSkin.GetStyle("SteelBox")); + { + EditorGUI.BeginChangeCheck(); + GUILayout.Label("Fusion App Id:", GUILayout.Width(120)); + var icon = IsAppIdValid() ? Resources.Load("icons/correct-icon") : EditorGUIUtility.FindTexture("console.erroricon.sml"); + GUILayout.Label(icon, GUILayout.Width(24), GUILayout.Height(24)); + var editedAppId = EditorGUILayout.DelayedTextField("", realtimeAppId, FusionHubSkin.textField, GUILayout.Height(24)); + if (EditorGUI.EndChangeCheck()) { + realtimeSettings.AppSettings.AppIdFusion = editedAppId; + EditorUtility.SetDirty(realtimeSettings); + AssetDatabase.SaveAssets(); + } + } + EditorGUILayout.EndHorizontal(); + } + EditorGUILayout.EndVertical(); + } + + void DrawLeftNavMenu() { + for (int i = 0; i < Sections.Length; ++i) { + var section = Sections[i]; + if (DrawNavButton(section, currentSection == i)) { + // Check if appid is valid whenever we change sections. It no longer needs to be shown on welcome page once it is set. + _showAppIdInWelcome = !IsAppIdValid(); + currentSection = i; + } + } + } + + static void DrawHeader() { + GUILayout.Label(Icons[(int)Icon.ProductLogo], _navbarHeaderGraphicStyle); + } + + static void DrawFooter() { + GUILayout.BeginHorizontal(FusionHubSkin.window); + { + GUILayout.Label("\u00A9 2021, Exit Games GmbH. All rights reserved."); + } + GUILayout.EndHorizontal(); + } + + static bool DrawNavButton(Section section, bool currentSection) { + var content = new GUIContent() { + text = " " + section.Title, + image = Icons[(int)section.Icon], + }; + + var renderStyle = currentSection ? buttonActiveStyle : GUI.skin.button; + return GUILayout.Button(content, renderStyle); + } + + static void DrawButtonAction(Icon icon, string header, string description = null, bool? active = null, Action callback = null, int? width = null) { + DrawButtonAction(Icons[(int)icon], header, description, active, callback, width); + } + + static void DrawButtonAction(Texture2D icon, string header, string description = null, bool? active = null, Action callback = null, int? width = null) { + + var padding = GUI.skin.button.padding.top + GUI.skin.button.padding.bottom; + var height = icon.height + padding; + + var renderStyle = active.HasValue && active.Value == true ? buttonActiveStyle : GUI.skin.button; + // Draw text separately (not part of button guiconent) to have control over the space between the icon and the text. + var rect = EditorGUILayout.GetControlRect(false, height, width.HasValue ? GUILayout.Width(width.Value) : GUILayout.ExpandWidth(true)); + bool clicked = GUI.Button(rect, icon, renderStyle); + GUI.Label(new Rect(rect) { xMin = rect.xMin + icon.width + 20 }, description == null ? "" + header +"" : string.Format("{0}\n{1}", header, description)); + if (clicked && callback != null) { + callback.Invoke(); + } + } + } +} +#endif + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionHub/FusionHubWindowUtils.cs + +// ---------------------------------------------------------------------------- +// +// PhotonNetwork Framework for Unity - Copyright (C) 2021 Exit Games GmbH +// +// +// MenuItems and in-Editor scripts for PhotonNetwork. +// +// developer@exitgames.com +// ---------------------------------------------------------------------------- + + +#if FUSION_WEAVER && UNITY_EDITOR +namespace Fusion.Editor { + + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.IO; + using System.Text.RegularExpressions; + using UnityEditor; + using UnityEngine; + + public partial class FusionHubWindow { + /// + /// Section Definition. + /// + internal class Section { + public string Title; + public string Description; + public Action DrawMethod; + public Icon Icon; + + public Section(string title, string description, Action drawMethod, Icon icon) { + Title = title; + Description = description; + DrawMethod = drawMethod; + Icon = icon; + } + } + + static Texture2D[] Icons; + internal enum Icon { + [Description("FusionHubIcons/information")] + Setup, + [Description("FusionHubIcons/documentation")] + Documentation, + [Description("FusionHubIcons/samples")] + Samples, + [Description("FusionHubIcons/community")] + Community, + [Description("FusionHubIcons/fusion-logo")] + ProductLogo, + [Description("FusionHubIcons/photon-cloud-32-dark")] + PhotonCloud, + [Description("FusionHubIcons/fusion-icon")] + FusionIcon, + } + + + static Section[] Sections = new Section[] { + new Section("Welcome", "Welcome to Photon Fusion", DrawWelcomeSection, Icon.Setup), + new Section("Fusion Setup", "Setup Photon Fusion", DrawSetupSection, Icon.PhotonCloud), + new Section("Samples & Tutorials", "Fusion Samples and Tutorials", DrawSamplesSection, Icon.Samples), + new Section("Documentation", "Photon Fusion Documentation", DrawDocumentationSection, Icon.Documentation), + new Section("Fusion Release Notes", "Fusion Release Notes", DrawFusionReleaseSection, Icon.Documentation), + //new Section("Realtime Release Notes", "Realtime Release Notes", DrawRealtimeReleaseSection, Icon.Documentation), + new Section("Support", "Support and Community Links", DrawSupportSection, Icon.Community), + }; + + internal const string UrlFusionDocsOnline = "https://doc.photonengine.com/fusion/"; + internal const string UrlFusionIntro = "https://doc.photonengine.com/fusion/current/getting-started/fusion-intro"; + internal const string UrlCloudDashboard = "https://id.photonengine.com/account/signin?email="; + internal const string UrlDiscordGeneral = "https://discord.gg/qP6XVe3XWK"; + internal const string UrlDashboard = "https://dashboard.photonengine.com/"; + internal const string UrlHelloFusion = "https://doc.photonengine.com/fusion/current/hello-fusion/hello-fusion"; + internal const string UrlHelloFusionVr = "https://doc.photonengine.com/fusion/current/hello-fusion/hello-fusion-vr"; + internal const string UrlTanks = "https://doc.photonengine.com/fusion/current/samples/fusion-tanknarok"; + internal const string UrlFusionDocApi = "https://doc-api.photonengine.com/en/fusion/current/annotated.html"; + + internal const string WINDOW_TITLE = "Photon Fusion Hub"; + internal const string SUPPORT = "You can contact the Photon Team using one of the following links. You can also go to Photon Documentation in order to get started."; + internal const string DISCORD_TEXT = "Join the Discord."; + internal const string DISCORD_HEADER = "Community"; + internal const string DOCUMENTATION_TEXT = "Open the documentation."; + internal const string DOCUMENTATION_HEADER = "Documentation"; + internal const string WELCOME_TEXT = "Thank you for installing Photon Fusion, " + + "and welcome to the Photon Fusion Beta.\n\n" + + "Once you have set up your Fusion App Id, explore the sections on the left to get started. " + + "More samples, tutorials, and documentation are being added regularly - so check back often."; + + internal const string REALTIME_APPID_SETUP_INSTRUCTIONS = +@"An App Id specific to Fusion is required for networking. + +To acquire an App Id: +- Open the Photon Dashboard (Log-in as required) +- Select an existing Fusion App Id, or create a new one. +- Copy the App Id and paste into the field below (or into the PhotonAppSettings.asset). +"; + + internal const string GETTING_STARTED_INSTRUCTIONS = + @"Links to demos, tutorials, API references and other information can be found on the PhotonEngine.com website."; + + private static string releaseHistoryHeader; + private static List releaseHistoryTextAdded; + private static List releaseHistoryTextChanged; + private static List releaseHistoryTextFixed; + private static List releaseHistoryTextRemoved; + private static List releaseHistoryTextInternal; + + private static string fusionReleaseHistory; + + private static GUISkin _fusionHubSkinBacking; + static GUISkin FusionHubSkin { + get { + if (_fusionHubSkinBacking == null) { + _fusionHubSkinBacking = Resources.Load("FusionHubSkin/FusionHubSkin"); + } + return _fusionHubSkinBacking; + } + } + + private static GUIStyle _navbarHeaderGraphicStyle; + private static GUIStyle textLabelStyle; + private static GUIStyle headerLabelStyle; + private static GUIStyle releaseNotesStyle; + private static GUIStyle headerTextStyle; + private static GUIStyle buttonActiveStyle; + + /// + /// Converts the enumeration of icons into the array of textures. + /// + private static void ConvertIconEnumToArray() { + bool isProSkin = EditorGUIUtility.isProSkin; + // convert icon enum into array of textures + var icons = Enum.GetValues(typeof(Icon)); + var list = new List(); + for (int i = 0; i < icons.Length; ++i) { + // : indicates two paths, one for dark, and one for light + var path = ((Icon)i).GetDescription(); + if (path.Contains(":")) { + if (isProSkin) { + path = path.Substring(0, path.IndexOf(":")); + } else { + path = path.Substring(path.IndexOf(":") + 1); + } + } + list.Add(Resources.Load(path)); + } + Icons = list.ToArray(); + } + + private static void InitContent() { + if (ready.HasValue && ready.Value) { + return; + } + + ConvertIconEnumToArray(); + + Color commonTextColor = Color.white; + + var _guiSkin = FusionHubSkin; + + _navbarHeaderGraphicStyle = new GUIStyle(_guiSkin.button) { alignment = TextAnchor.MiddleCenter }; + + headerTextStyle = new GUIStyle(_guiSkin.label) { + fontSize = 18, + padding = new RectOffset(12, 8, 8, 8), + fontStyle = FontStyle.Bold, + normal = { textColor = commonTextColor } + }; + + buttonActiveStyle = new GUIStyle(_guiSkin.button) { + fontStyle = FontStyle.Bold, + normal = { background = _guiSkin.button.active.background, textColor = Color.white } + }; + + + textLabelStyle = new GUIStyle(_guiSkin.label) { + wordWrap = true, + normal = { textColor = commonTextColor }, + richText = true, + + }; + headerLabelStyle = new GUIStyle(textLabelStyle) { + fontSize = 16, + }; + + releaseNotesStyle = new GUIStyle(textLabelStyle) { + richText = true, + }; + + ready = true; + } + + private static Action OpenURL(string url, params object[] args) { + return () => { + if (args.Length > 0) { + url = string.Format(url, args); + } + + Application.OpenURL(url); + }; + } + + protected static bool IsAppIdValid() { + var photonSettings = NetworkProjectConfigUtilities.GetOrCreatePhotonAppSettingsAsset(); + var val = photonSettings.AppSettings.AppIdFusion; + try { + new Guid(val); + } catch { + return false; + } + return true; + } + + static string titleVersionReformat, sectionReformat, header1Reformat, header2Reformat, header3Reformat, classReformat; + + void InitializeFormatters() { + titleVersionReformat = "$1" ; + sectionReformat = "$1"; + header1Reformat = "$1"; + header2Reformat = "$1"; + header3Reformat = "$1"; + classReformat = "$1"; + } + + /// + /// Converts readme files into Unity RichText. + /// + private void PrepareReleaseHistoryText() { + + if (sectionReformat == null || sectionReformat == "") { + InitializeFormatters(); + } + // Fusion + { + var filePath = BuildPath(Application.dataPath, "Photon", "Fusion", "release_history.txt"); + var text = (TextAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(TextAsset)); + var baseText = text.text; + + // # + baseText = Regex.Replace(baseText, @"^# (.*)", titleVersionReformat); + baseText = Regex.Replace(baseText, @"(?<=\n)# (.*)", header1Reformat); + // ## + baseText = Regex.Replace(baseText, @"(?<=\n)## (.*)", header2Reformat); + // ### + baseText = Regex.Replace(baseText, @"(?<=\n)### (.*)", header3Reformat); + // **Changes** + baseText = Regex.Replace(baseText, @"(?<=\n)\*\*(.*)\*\*", sectionReformat); + // `Class` + baseText = Regex.Replace(baseText, @"\`([^\`]*)\`", classReformat); + + fusionReleaseHistory = baseText; + } + + // Realtime + { + try { + + var filePath = BuildPath(Application.dataPath, "Photon", "PhotonRealtime", "Code", "changes-realtime.txt"); + + var text = (TextAsset)AssetDatabase.LoadAssetAtPath(filePath, typeof(TextAsset)); + + var baseText = text.text; + + var regexVersion = new Regex(@"Version (\d+\.?)*", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexAdded = new Regex(@"\b(Added:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexChanged = new Regex(@"\b(Changed:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexUpdated = new Regex(@"\b(Updated:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexFixed = new Regex(@"\b(Fixed:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexRemoved = new Regex(@"\b(Removed:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + var regexInternal = new Regex(@"\b(Internal:)(.*)\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Multiline); + + var matches = regexVersion.Matches(baseText); + + if (matches.Count > 0) { + var currentVersionMatch = matches[0]; + var lastVersionMatch = currentVersionMatch.NextMatch(); + + if (currentVersionMatch.Success && lastVersionMatch.Success) { + Func> itemProcessor = (match) => { + List resultList = new List(); + for (int index = 0; index < match.Count; index++) { + resultList.Add(match[index].Groups[2].Value.Trim()); + } + return resultList; + }; + + string mainText = baseText.Substring(currentVersionMatch.Index + currentVersionMatch.Length, + lastVersionMatch.Index - lastVersionMatch.Length - 1).Trim(); + + releaseHistoryHeader = currentVersionMatch.Value.Trim(); + releaseHistoryTextAdded = itemProcessor(regexAdded.Matches(mainText)); + releaseHistoryTextChanged = itemProcessor(regexChanged.Matches(mainText)); + releaseHistoryTextChanged.AddRange(itemProcessor(regexUpdated.Matches(mainText))); + releaseHistoryTextFixed = itemProcessor(regexFixed.Matches(mainText)); + releaseHistoryTextRemoved = itemProcessor(regexRemoved.Matches(mainText)); + releaseHistoryTextInternal = itemProcessor(regexInternal.Matches(mainText)); + } + } + } catch (Exception) { + releaseHistoryHeader = "\nPlease look the file changes-realtime.txt"; + releaseHistoryTextAdded = new List(); + releaseHistoryTextChanged = new List(); + releaseHistoryTextFixed = new List(); + releaseHistoryTextRemoved = new List(); + releaseHistoryTextInternal = new List(); + } + } + + } + + public static bool Toggle(bool value) { + GUIStyle toggle = new GUIStyle("Toggle") { + margin = new RectOffset(), + padding = new RectOffset() + }; + + return EditorGUILayout.Toggle(value, toggle, GUILayout.Width(15)); + } + + private static string BuildPath(params string[] parts) { + var basePath = ""; + + foreach (var path in parts) { + basePath = Path.Combine(basePath, path); + } + + return basePath.Replace(Application.dataPath, Path.GetFileName(Application.dataPath)); + } + } +} +#endif + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionInstaller.cs + +#if !FUSION_DEV +namespace Fusion.Editor { + using System; + using System.IO; + using UnityEditor; + using UnityEditor.PackageManager; + using UnityEngine; + + [InitializeOnLoad] + class FusionInstaller { + const string DEFINE = "FUSION_WEAVER"; + const string PACKAGE_TO_SEARCH = "nuget.mono-cecil"; + const string PACKAGE_TO_INSTALL = "com.unity.nuget.mono-cecil"; + const string PACKAGES_DIR = "Packages"; + const string MANIFEST_FILE = "manifest.json"; + + static FusionInstaller() { + var group = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); + + var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(group); + if (defines.IndexOf(DEFINE, StringComparison.Ordinal) >= 0) { + return; + } + + var manifest = Path.Combine(Path.GetDirectoryName(Application.dataPath), PACKAGES_DIR, MANIFEST_FILE); + + if (File.ReadAllText(manifest).IndexOf(PACKAGE_TO_SEARCH, StringComparison.Ordinal) >= 0) { + Debug.Log($"Setting '{DEFINE}' Define"); + PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines + ";" + DEFINE); + } else { + Debug.Log($"Installing '{PACKAGE_TO_INSTALL}' package"); + Client.Add(PACKAGE_TO_INSTALL); + } + + } + } +} +#endif + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionProfiler/FusionSamplerWindow.cs + +// deleted on 31st May 2021 + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/FusionWeaverTriggerImporter.cs + +namespace Fusion.Editor { + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.AssetImporters; + using UnityEngine; + + [ScriptedImporter(1, ExtensionWithoutDot, NetworkProjectConfigImporter.ImportQueueOffset + 1)] + public class FusionWeaverTriggerImporter : ScriptedImporter { + public const string DependencyName = "FusionILWeaverTriggerImporter/ConfigHash"; + public const string Extension = "." + ExtensionWithoutDot; + public const string ExtensionWithoutDot = "fusionweavertrigger"; + + public override void OnImportAsset(AssetImportContext ctx) { + ctx.DependsOnCustomDependency(DependencyName); + ILWeaverUtils.RunWeaver(); + } + + private static void RefreshDependencyHash() { + var configPath = NetworkProjectConfigUtilities.GetGlobalConfigPath(false); + if (string.IsNullOrEmpty(configPath)) { + return; + } + + try { + var cfg = NetworkProjectConfigImporter.LoadConfigFromFile(configPath); + + var hash = new Hash128(); + foreach (var key in cfg.AccuracyDefaults.coreKeys) { + hash.Append(key); + } + foreach (var val in cfg.AccuracyDefaults.coreVals) { + hash.Append(val._value); + } + foreach (var key in cfg.AccuracyDefaults.tags) { + hash.Append(key); + } + foreach (var val in cfg.AccuracyDefaults.values) { + hash.Append(val._value); + } + foreach (var path in cfg.AssembliesToWeave) { + hash.Append(path); + } + + hash.Append(cfg.UseSerializableDictionary ? 1 : 0); + hash.Append(cfg.NullChecksForNetworkedProperties ? 1 : 0); + + AssetDatabase.RegisterCustomDependency(DependencyName, hash); + AssetDatabase.Refresh(); + } catch { + // ignore the error + } + } + + private class Postprocessor : AssetPostprocessor { + private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + foreach (var path in importedAssets) { + if (path.EndsWith(NetworkProjectConfigImporter.Extension)) { + EditorApplication.delayCall -= RefreshDependencyHash; + EditorApplication.delayCall += RefreshDependencyHash; + } + } + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/ILWeaverUtils.cs + +namespace Fusion.Editor { + using UnityEditor; + using UnityEditor.Compilation; + + [InitializeOnLoad] + public static class ILWeaverUtils { + [MenuItem("Fusion/Run Weaver")] + public static void RunWeaver() { + + // ensure config exists + _ = NetworkProjectConfigUtilities.GetGlobalConfigPath(); + + CompilationPipeline.RequestScriptCompilation( +#if UNITY_2021_1_OR_NEWER + RequestScriptCompilationOptions.CleanBuildCache +#endif + ); + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkBehaviourEditor.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + [CustomEditor(typeof(NetworkBehaviour), true)] + [CanEditMultipleObjects] + public class NetworkBehaviourEditor : BehaviourEditor { + + internal const string NETOBJ_REQUIRED_WARN_TEXT = "This " + nameof(NetworkBehaviour) + " requires a " + nameof(NetworkObject) + " component to function."; + + IEnumerable ValidTargets => targets + .Cast() + .Where(x => x.Object && x.Object.IsValid && x.Object.gameObject.activeInHierarchy); + + public override void OnInspectorGUI() { + + base.PrepareInspectorGUI(); + + // can networked properties be altered for all selected targets? + bool? hasStateAuthorityForAllTargets = null; + foreach (var target in ValidTargets) { + if (target.Object.HasStateAuthority) { + hasStateAuthorityForAllTargets = true; + } else { + hasStateAuthorityForAllTargets = false; + break; + } + } + + + // copy changes from the state + serializedObject.UpdateIfRequiredOrScript(); + foreach (var target in ValidTargets) { + target.CopyStateToBackingFields(); + } + + EditorGUI.BeginChangeCheck(); + +#if ODIN_INSPECTOR && !FUSION_ODIN_DISABLED + base.DrawDefaultInspector(); +#else + SerializedProperty prop = serializedObject.GetIterator(); + for (bool enterChildren = true; prop.NextVisible(enterChildren); enterChildren = false) { + var field = UnityInternal.ScriptAttributeUtility.GetFieldInfoFromProperty(prop, out var type); + var attributes = field?.GetCustomAttributes() ?? Array.Empty(); + if (attributes.Any()) { + using (new EditorGUI.DisabledScope(hasStateAuthorityForAllTargets == false)) { +#if FUSION_DEV + DrawNetworkedProperty(prop); +#else + EditorGUILayout.PropertyField(prop, true); +#endif + } + } else { + using (new EditorGUI.DisabledScope(prop.propertyPath == FusionEditorGUI.ScriptPropertyName)) { + EditorGUILayout.PropertyField(prop, true); + } + } + } +#endif + + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + + // move changes to the state + foreach (var target in ValidTargets) { + if (target.Object.HasStateAuthority) { + target.CopyBackingFieldsToState(false); + } + + } + } + + DrawNetworkObjectCheck(); + DrawBehaviourActions(); + } + + + /// + /// Checks if GameObject or parent GameObject has a NetworkObject, and draws a warning and buttons for adding one if not. + /// + /// + void DrawNetworkObjectCheck() { + var targetsWithoutNetworkObjects = targets.Cast().Where(x => x.transform.GetParentComponent() == false).ToList(); + if (targetsWithoutNetworkObjects.Any()) { + EditorGUILayout.BeginVertical(FusionGUIStyles.GroupBoxType.Warn.GetStyle()); + + BehaviourEditorUtils.DrawWarnBox(NETOBJ_REQUIRED_WARN_TEXT, MessageType.Warning, FusionGUIStyles.GroupBoxType.None); + + GUILayout.Space(4); + + IEnumerable gameObjects = null; + + if (GUI.Button(EditorGUILayout.GetControlRect(false, 22), "Add Network Object")) { + gameObjects = targetsWithoutNetworkObjects.Select(x => x.gameObject).Distinct(); + } + if (GUI.Button(EditorGUILayout.GetControlRect(false, 22), "Add Network Object to Root")) { + gameObjects = targetsWithoutNetworkObjects.Select(x => x.transform.root.gameObject).Distinct(); + } + + if (gameObjects != null) { + foreach (var go in gameObjects) { + Undo.AddComponent(go); + } + } + + EditorGUILayout.EndVertical(); + } + } + +#if FUSION_DEV + private GUIContent _label = new GUIContent(); + [NonSerialized] + private string _prefixSpacing; + + private void DrawNetworkedProperty(SerializedProperty prop) { + var rect = EditorGUILayout.GetControlRect(true, EditorGUI.GetPropertyHeight(prop, true)); + + const float imageHeight = 14; + + + Texture2D image = FusionEditorGUI.NetworkedPropertyStyle.NetworkPropertyIcon; + + var buttonRect = rect; + buttonRect.height = EditorGUIUtility.singleLineHeight; + buttonRect.width = image.width * EditorGUIUtility.singleLineHeight / image.height; + + var imageRect = buttonRect; + imageRect.y += (buttonRect.height - imageHeight) / 2; + imageRect.height = imageHeight; + imageRect.width = image.width * imageHeight / image.height; + + EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link); + bool clicked = GUI.Button(buttonRect, GUIContent.none, GUIStyle.none); + + // find out how many spaces we need + if (string.IsNullOrEmpty(_prefixSpacing)) { + GUIContent prefixContent = new GUIContent(" "); + while (EditorStyles.label.CalcSize(prefixContent).x <= imageRect.width) { + prefixContent.text += " "; + } + _prefixSpacing = prefixContent.text; + } + + _label.text = $"{_prefixSpacing}{prop.displayName}"; + _label.tooltip = string.Empty; + _label.image = null; + + + // draw the property + EditorGUI.PropertyField(rect, prop, _label, true); + + // draw the icon + if (Event.current.type == EventType.Repaint) { + GUI.DrawTexture(imageRect, image); + } + + if (clicked) { + PopupWindow.Show(buttonRect, new NetworkedPropertyPopupWindowContent(prop, rect.width)); + } + } + + unsafe class NetworkedPropertyPopupWindowContent : PopupWindowContent { + + readonly SerializedProperty _property; + readonly FieldInfo _field; + readonly DefaultForPropertyAttribute[] _attributes; + readonly float _width; + readonly List _placeholderStrings; + readonly GUIContent _placeholder; + readonly GUIStyle _rawStyle; + readonly byte*[] _pointers; + + readonly GUIContent _wordOffsetLabel = new GUIContent("Word Offset"); + readonly GUIContent _wordCountLabel = new GUIContent("Word Count"); + + const int Margin = 2; + + public NetworkedPropertyPopupWindowContent(SerializedProperty property, float width) { + _width = width; + _property = property; + _field = UnityInternal.ScriptAttributeUtility.GetFieldInfoFromProperty(property, out _); + _attributes = _field.GetCustomAttributes().ToArray() ?? Array.Empty(); + _pointers = new byte*[_property.serializedObject.targetObjects.Length]; + _placeholder = new GUIContent(); + + _placeholderStrings = _attributes + .Select(x => new string(Enumerable.Repeat('F', x.WordCount * 2 * Allocator.REPLICATE_WORD_SIZE).ToArray())) + .ToList(); + + _rawStyle = new GUIStyle(EditorStyles.numberField) { + wordWrap = true, + }; + } + + public override void OnGUI(Rect rect) { + { + var boxRect = rect; + boxRect.x += Margin / 2; + boxRect.width -= Margin; + boxRect.y += Margin / 2; + boxRect.height -= Margin; + GUI.Box(boxRect, GUIContent.none, EditorStyles.helpBox); + } + + var targets = _property.serializedObject.targetObjects.Cast().ToArray(); + + rect.x += Margin; + rect.width -= 2 * Margin; + rect.y += Margin; + rect.height -= 2 * Margin; + + foreach (var (attribute, placeholder) in _attributes.Zip(_placeholderStrings, (a, b) => (a, b))) { + + rect.height = EditorGUIUtility.singleLineHeight; + + EditorGUI.LabelField(rect, attribute.PropertyName, EditorStyles.boldLabel); + rect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + EditorGUI.SelectableLabel(EditorGUI.PrefixLabel(rect, _wordOffsetLabel), $"{attribute.WordOffset}"); + rect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + EditorGUI.SelectableLabel(EditorGUI.PrefixLabel(rect, _wordCountLabel), $"{attribute.WordCount}"); + rect.y += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing; + + _placeholder.text = placeholder; + rect.height = Mathf.Max(EditorGUIUtility.singleLineHeight, _rawStyle.CalcHeight(_placeholder, rect.width - EditorGUIUtility.labelWidth)); + + if (!targets.All(x => x && x.Object && x.Object.IsValid)) { + using (new EditorGUI.DisabledScope(true)) { + EditorGUI.TextField(rect, "Raw", "Target not valid"); + } + } else { + + string content; + bool readOnly = true; + bool mixedValues = false; + int byteCount = attribute.WordCount * Allocator.REPLICATE_WORD_SIZE; + + readOnly = targets.Any(x => !x.Object.HasStateAuthority); + + for (int i = 0; i < targets.Length; ++i) { + _pointers[i] = (byte*)(targets[i].Ptr + attribute.WordOffset); + } + + for (int i = 1; i < _pointers.Length; ++i) { + if (Native.MemCmp(_pointers[0], _pointers[i], byteCount) != 0) { + mixedValues = true; + break; + } + } + + if (mixedValues) { + content = string.Empty; + } else { + content = BinUtils.BytesToHex(_pointers[0], byteCount, columns: int.MaxValue, columnSeparator: ""); + } + + using (new EditorGUI.DisabledScope(readOnly)) { + using (new FusionEditorGUI.ShowMixedValueScope(mixedValues)) { + var id = GUIUtility.GetControlID(UnityInternal.EditorGUI.DelayedTextFieldHash, FocusType.Keyboard, rect); + + EditorGUI.BeginChangeCheck(); + var newStr = UnityInternal.EditorGUI.DelayedTextFieldInternal(rect, id, new GUIContent("Raw"), content, "0123456789abcdefABCDEF ", _rawStyle); + if (EditorGUI.EndChangeCheck()) { + Native.MemClear(_pointers[0], byteCount); + BinUtils.HexToBytes(newStr, _pointers[0], byteCount); + for (int j = 1; j < _pointers.Length; ++j) { + Native.MemCpy(_pointers[j], _pointers[0], byteCount); + } + _property.serializedObject.Update(); + } + } + } + } + + rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; + } + } + + public override Vector2 GetWindowSize() { + var result = base.GetWindowSize(); + result.x = _width; + result.y = 2 * Margin; + + foreach (var (attribute, placeholder) in _attributes.Zip(_placeholderStrings, (a, b) => (a, b))) { + result.y += 3 * (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing); + + _placeholder.text = placeholder; + result.y += _rawStyle.CalcHeight(_placeholder, _width - EditorGUIUtility.labelWidth - 2 * Margin); + result.y += EditorGUIUtility.standardVerticalSpacing; + } + return result; + } + } +#endif + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkMecanimAnimatorEditor.cs + +namespace Fusion.Editor { + + using UnityEditor; + + [CustomEditor(typeof(NetworkMecanimAnimator))] + + public class NetworkMecanimAnimatorEditor : BehaviourEditor { + public override void OnInspectorGUI() { + + var na = target as NetworkMecanimAnimator; + + + if (na != null) + AnimatorControllerTools.GetHashesAndNames(na, null, null, ref na.TriggerHashes, ref na.StateHashes); + + base.OnInspectorGUI(); + + + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkObjectEditor.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using UnityEditor; + using UnityEngine; +#if UNITY_2021_2_OR_NEWER + using UnityEditor.SceneManagement; +#else + using UnityEditor.Experimental.SceneManagement; +#endif + + [CustomEditor(typeof(NetworkObject), true)] + [InitializeOnLoad] + [CanEditMultipleObjects] + public unsafe class NetworkObjectEditor : BehaviourEditor { + private bool _runtimeInfoFoldout; + + public static bool BakeHierarchy(GameObject root, NetworkObjectGuid? prefabGuid, Action setDirty = null, Func guidProvider = null) { + var networkObjectsBuffer = new List(); + var simulationBehaviourBuffer = new List(); + var networkBehaviourBuffer = new List(); + + Dictionary executionOrderCache = new Dictionary(); + + int GetExecutionOrderWarnIfFailed(MonoBehaviour obj) { + if (executionOrderCache.TryGetValue(obj.GetType(), out var result)) { + return result; + } + var monoScript = MonoScript.FromMonoBehaviour(obj); + if (monoScript) { + result = MonoImporter.GetExecutionOrder(monoScript); + } else { + Debug.LogWarning($"Unable to get execution order for deactivated {obj?.GetType().FullName}. Assuming 0.", obj); + result = 0; + } + executionOrderCache.Add(obj.GetType(), result); + return result; + } + + bool dirty = false; + + using (var pathCache = new TransformPathCache()) { + root.GetComponentsInChildren(true, networkObjectsBuffer); + if (networkObjectsBuffer.Count == 0) { + return dirty; + } + + var networkObjects = networkObjectsBuffer.Select(x => new { + Path = pathCache.Create(x.transform), + Object = x + }).OrderByDescending(x => x.Path).ToList(); + + root.GetComponentsInChildren(true, simulationBehaviourBuffer); + + // sort scripts in a descending way + var networkScripts = simulationBehaviourBuffer.Select(x => new { + Path = pathCache.Create(x.transform), + Script = x + }).OrderBy(x => x.Path).ToList(); + + // start from the leaves + for (int i = 0; i < networkObjects.Count; ++i) { + var entry = networkObjects[i]; + var objActive = entry.Object.gameObject.activeInHierarchy; + int objExecutionOrder = 0; + if (!objActive) { + objExecutionOrder = GetExecutionOrderWarnIfFailed(entry.Object); + } + + // find nested behaviours + networkBehaviourBuffer.Clear(); + simulationBehaviourBuffer.Clear(); + + string entryPath = entry.Path.ToString(); + for (int scriptIndex = networkScripts.Count - 1; scriptIndex >= 0; --scriptIndex) { + var scriptEntry = networkScripts[scriptIndex]; + var scriptPath = scriptEntry.Path.ToString(); + + if (entry.Path.IsEqualOrAncestorOf(scriptEntry.Path)) { + var script = scriptEntry.Script; + if (script is NetworkBehaviour networkBehaviour) { + networkBehaviour.Object = entry.Object; + networkBehaviourBuffer.Add(networkBehaviour); + } else { + simulationBehaviourBuffer.Add(script); + } + networkScripts.RemoveAt(scriptIndex); + + if (!objActive) { + // check if execution order is ok + int scriptExecutionOrder = GetExecutionOrderWarnIfFailed(script); + + if (objExecutionOrder <= scriptExecutionOrder) { + Debug.LogWarning($"{entry.Object?.GetType().FullName} execution order is less or equal than of the script {script?.GetType().FullName}. " + + $"This will result in Spawned callback being invoked before the script's Awake.", script); + } + } + + + } else if (entry.Path.CompareTo(scriptEntry.Path) < 0) { + // can't discard it yet + } else { + Debug.Assert(entry.Path.CompareTo(scriptEntry.Path) > 0); + break; + } + } + + networkBehaviourBuffer.Reverse(); + dirty |= Set(entry.Object, ref entry.Object.NetworkedBehaviours, networkBehaviourBuffer, setDirty); + + simulationBehaviourBuffer.Reverse(); + dirty |= Set(entry.Object, ref entry.Object.SimulationBehaviours, simulationBehaviourBuffer, setDirty); + + // handle flags + + var flags = entry.Object.Flags; + + if (!flags.IsVersionCurrent()) { + flags = flags.SetCurrentVersion(); + } + + if (prefabGuid == null) { + if (flags.IsPrefab()) { + dirty |= Set(entry.Object, ref entry.Object.NetworkGuid, default, setDirty); + } + flags = flags.SetType(NetworkObjectFlags.TypeSceneObject); + if (guidProvider == null) { + throw new ArgumentNullException(nameof(guidProvider)); + } + dirty |= Set(entry.Object, ref entry.Object.NetworkGuid, guidProvider(entry.Object), setDirty); + } else { + flags = flags.SetType(entry.Path.Depth == 1 ? NetworkObjectFlags.TypePrefab : NetworkObjectFlags.TypePrefabChild); + if (entry.Path.Depth > 1) { + dirty |= Set(entry.Object, ref entry.Object.NetworkGuid, NetworkObjectGuid.Empty, setDirty); + } else { + if (prefabGuid?.IsValid != true) { + throw new ArgumentException($"Invalid value: {prefabGuid}", nameof(prefabGuid)); + } + + dirty |= Set(entry.Object, ref entry.Object.NetworkGuid, prefabGuid.Value, setDirty); + } + } + + flags = flags.SetActivatedByUser(entry.Object.gameObject.activeInHierarchy == false); + dirty |= Set(entry.Object, ref entry.Object.Flags, flags, setDirty); + } + + // what's left is nested network objects resolution + for (int i = 0; i < networkObjects.Count; ++i) { + var entry = networkObjects[i]; + networkObjectsBuffer.Clear(); + + // collect descendants; descendants should be continous without gaps here + int j = i - 1; + for (; j >= 0 && entry.Path.IsAncestorOf(networkObjects[j].Path); --j) { + networkObjectsBuffer.Add(networkObjects[j].Object); + } + + int descendantsBegin = j + 1; + Debug.Assert(networkObjectsBuffer.Count == i - descendantsBegin); + + dirty |= Set(entry.Object, ref entry.Object.NestedObjects, networkObjectsBuffer, setDirty); + } + } + + return dirty; + } + + static string GetLoadInfoString(NetworkObject prefab) { + if (NetworkProjectConfigUtilities.TryGetPrefabSource(prefab.NetworkGuid, out INetworkPrefabSource prefabAsset)) { + return prefabAsset.EditorSummary; + } + return "Null"; + } + + + public override void OnInspectorGUI() { + + FusionEditorGUI.InjectPropertyDrawers(serializedObject); + FusionEditorGUI.ScriptPropertyField(serializedObject); + + // these properties' isExpanded are going to be used for foldouts; that's the easiest + // way to get quasi-persistent foldouts + var guidProperty = serializedObject.FindPropertyOrThrow(nameof(NetworkObject.NetworkGuid)); + var flagsProperty = serializedObject.FindPropertyOrThrow(nameof(NetworkObject.Flags)); + var obj = (NetworkObject)base.target; + var netObjType = typeof(NetworkObject); + + if (targets.Length == 1) { + if (AssetDatabase.IsMainAsset(obj.gameObject) || PrefabStageUtility.GetPrefabStage(obj.gameObject)?.prefabContentsRoot == obj.gameObject) { + Debug.Assert(!AssetDatabaseUtils.IsSceneObject(obj.gameObject)); + + if (!obj.Flags.IsVersionCurrent() || !obj.Flags.IsPrefab() || !obj.NetworkGuid.IsValid) { + BehaviourEditorUtils.DrawWarnBox("Prefab needs to be reimported.", MessageType.Error); + if (GUILayout.Button("Reimport")) { + AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(obj.gameObject)); + } + } else { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Prefab Settings", EditorStyles.boldLabel); + + // Is Spawnable + { + EditorGUI.BeginChangeCheck(); + + bool spawnable = this.DrawToggleForProperty(nameof(NetworkObject.IsSpawnable), !obj.Flags.IsIgnored(), netObjType); + if (EditorGUI.EndChangeCheck()) { + var value = obj.Flags.SetIgnored(!spawnable); + serializedObject.FindProperty(nameof(NetworkObject.Flags)).intValue = (int)value; + serializedObject.ApplyModifiedProperties(); + } + EditorGUILayout.LabelField("Prefab Source", spawnable ? GetLoadInfoString(obj) : "---"); + } + } + } else if (AssetDatabaseUtils.IsSceneObject(obj.gameObject)) { + if (!obj.Flags.IsVersionCurrent() || !obj.Flags.IsSceneObject() || !obj.NetworkGuid.IsValid) { + if (!EditorApplication.isPlaying) { + BehaviourEditorUtils.DrawWarnBox("This object hasn't been baked yet. Save the scene or enter playmode."); + } + } + } + } + + + if (EditorApplication.isPlaying && targets.Length == 1) { + EditorGUILayout.Space(); + flagsProperty.isExpanded = EditorGUILayout.Foldout(flagsProperty.isExpanded, "Runtime Info"); + if (flagsProperty.isExpanded) { + using (new EditorGUI.IndentLevelScope()) { + + EditorGUILayout.BeginVertical(FusionGUIStyles.GetStyle(FusionGUIStyles.GroupBoxType.Outline)); + { + EditorGUILayout.Toggle("Is Valid", obj.IsValid); + if (obj.IsValid) { + //EditorGUILayout.LabelField("Id", obj.Id.ToString()); + this.DrawLabelForProperty(nameof(NetworkObject.Id), obj.Id.ToString(), netObjType); + + EditorGUILayout.IntField("Word Count", NetworkObject.GetWordCount(obj)); + this.DrawToggleForProperty(nameof(NetworkObject.IsSceneObject), obj.IsSceneObject, netObjType); + + bool headerIsNull = obj.Header == null; + this.DrawLabelForProperty(nameof(NetworkObjectHeader.NestingRoot), headerIsNull ? "---" : obj.Header->NestingRoot.ToString(), typeof(NetworkObjectHeader)); + this.DrawLabelForProperty(nameof(NetworkObjectHeader.NestingKey), headerIsNull ? "---" : obj.Header->NestingKey.ToString(), typeof(NetworkObjectHeader)); + + this.DrawLabelForProperty(nameof(NetworkObject.InputAuthority), obj.InputAuthority.ToString(), netObjType); + this.DrawLabelForProperty(nameof(NetworkObject.StateAuthority), obj.StateAuthority.ToString(), netObjType); + + this.DrawToggleForProperty(nameof(NetworkObject.HasInputAuthority), obj.HasInputAuthority, netObjType); + this.DrawToggleForProperty(nameof(NetworkObject.HasStateAuthority), obj.HasStateAuthority, netObjType); + this.DrawToggleForProperty(nameof(NetworkObject.InSimulation), obj.InSimulation, netObjType); + + EditorGUILayout.Toggle("Is Local PlayerObject", obj == obj.Runner.GetPlayerObject(obj.Runner.LocalPlayer)); + + } + } + EditorGUILayout.EndVertical(); + + } + } + } + + EditorGUI.BeginChangeCheck(); + + var config = NetworkProjectConfig.Global; + var isPlaying = EditorApplication.isPlaying; + var ecIsEnabled = config.Simulation.ReplicationMode == SimulationConfig.StateReplicationModes.EventualConsistency; + + using (new EditorGUI.DisabledScope(isPlaying)) { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Shared Mode Settings", EditorStyles.boldLabel); + + var destroyWhenStateAuthLeaves = serializedObject.FindProperty(nameof(NetworkObject.DestroyWhenStateAuthorityLeaves)); + EditorGUILayout.PropertyField(destroyWhenStateAuthLeaves); + + var allowStateAuthorityOverride = serializedObject.FindProperty(nameof(NetworkObject.AllowStateAuthorityOverride)); + EditorGUILayout.PropertyField(allowStateAuthorityOverride); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Interest Management Settings", EditorStyles.boldLabel); + + if (ecIsEnabled) { + + var objectInterest = serializedObject.FindProperty(nameof(NetworkObject.ObjectInterest)); + EditorGUILayout.PropertyField(objectInterest); + + if (objectInterest.intValue == (int)NetworkObject.ObjectInterestModes.AreaOfInterest) { + EditorGUILayout.PropertyField(serializedObject.FindProperty(nameof(NetworkObject.AoiPositionSource))); + } + + using (new EditorGUI.IndentLevelScope()) { + EditorGUILayout.PropertyField(serializedObject.FindPropertyOrThrow(nameof(NetworkObject.DefaultInterestGroups))); + } + } else { + BehaviourEditorUtils.DrawWarnBox($"Interest Management requires Eventual Consistency to be enabled in {nameof(NetworkProjectConfig)}.", MessageType.Info); + } + } + + if (EditorGUI.EndChangeCheck()) { + serializedObject.ApplyModifiedProperties(); + } + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + guidProperty.isExpanded = EditorGUILayout.Foldout(guidProperty.isExpanded, "Baked Data", EditorStyles.foldoutHeader); + if (guidProperty.isExpanded) { + + using (new EditorGUILayout.VerticalScope(new GUIStyle(FusionGUIStyles.GetStyle(FusionGUIStyles.GroupBoxType.Outline)))) { + using (new EditorGUI.DisabledScope(true)) { + using (new EditorGUI.IndentLevelScope()) { + using (new FusionEditorGUI.ShowMixedValueScope(flagsProperty.hasMultipleDifferentValues)) { + FusionEditorGUI.LayoutSelectableLabel(EditorGUIUtility.TrTextContent(nameof(obj.Flags)), obj.Flags.ToString()); + } + EditorGUILayout.PropertyField(serializedObject.FindPropertyOrThrow(nameof(NetworkObject.NetworkGuid))); + using (new EditorGUI.IndentLevelScope()) { + + EditorGUILayout.PropertyField(serializedObject.FindPropertyOrThrow(nameof(NetworkObject.NestedObjects))); + EditorGUILayout.PropertyField(serializedObject.FindPropertyOrThrow(nameof(NetworkObject.SimulationBehaviours))); + EditorGUILayout.PropertyField(serializedObject.FindPropertyOrThrow(nameof(NetworkObject.NetworkedBehaviours))); + } + } + } + } + + } + } + + private static bool Set(UnityEngine.Object host, ref T field, T value, Action setDirty) { + if (!EqualityComparer.Default.Equals(field, value)) { + Trace($"Object dirty: {host} ({field} vs {value})"); + setDirty?.Invoke(host); + field = value; + return true; + } else { + return false; + } + } + + private static bool Set(UnityEngine.Object host, ref T[] field, List value, Action setDirty) { + var comparer = EqualityComparer.Default; + if (field == null || field.Length != value.Count || !field.SequenceEqual(value, comparer)) { + Trace($"Object dirty: {host} ({field} vs {value})"); + setDirty?.Invoke(host); + field = value.ToArray(); + return true; + } else { + return false; + } + } + + [System.Diagnostics.Conditional("FUSION_EDITOR_TRACE")] + private static void Trace(string msg) { + Debug.Log($"[Fusion/NetworkObjectEditor] {msg}"); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkObjectPostprocessor.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using UnityEditor; + using UnityEditor.Build; + using UnityEditor.Build.Reporting; + using UnityEditor.SceneManagement; + using UnityEngine; + using UnityEngine.SceneManagement; + + public class NetworkObjectPostprocessor : AssetPostprocessor, + UnityEditor.Build.IPreprocessBuildWithReport, + UnityEditor.Build.IProcessSceneWithReport, + UnityEditor.Build.IPostprocessBuildWithReport { + + static NetworkObjectPostprocessor() { + EditorSceneManager.sceneSaving += OnSceneSaving; + EditorApplication.playModeStateChanged += OnPlaymodeChange; + } + + int IOrderedCallback.callbackOrder => 0; + + private static HashSet _pendingPrefabImports = new HashSet(); + private static string _prefabBeingBaked = null; + + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + FusionEditorLog.TraceImport($"Postprocessing imported assets [{importedAssets.Length}]:\n{string.Join("\n", importedAssets)}"); + + bool configPossiblyDirty = false; + + foreach (var path in importedAssets) { + if (!path.EndsWith(".prefab")) { + continue; + } + + if (!_pendingPrefabImports.Remove(path)) { + FusionEditorLog.TraceImport(path, $"Skipping, not being marked as pending by OnPostprocessPrefab"); + continue; + } + + _prefabBeingBaked = path; + try { + var go = AssetDatabase.LoadAssetAtPath(path); + if (!go) { + continue; + } + + var no = go.GetComponent(); + if (no) { + FusionEditorLog.TraceImport(path, "Was marked as dirty in OnPostprocessPrefab, need to rebake"); + if (BakePrefab(path, out var newRoot)) { +#if FUSION_DEV + Debug.Assert(newRoot != null && newRoot == AssetDatabase.LoadMainAssetAtPath(path)); +#endif + go = newRoot; + no = go.GetComponent(); + } + } + + var isSpawnable = no && no.Flags.IsIgnored() == false; + if (AssetDatabaseUtils.SetLabel(go, NetworkProjectConfigImporter.FusionPrefabTag, isSpawnable)) { + configPossiblyDirty = true; + AssetDatabase.ImportAsset(path); + FusionEditorLog.TraceImport(path, "Labels dirty, going to reimport the config, too"); + } else if (no) { + FusionEditorLog.TraceImport(path, "Labels up to date"); + } + } finally { + _prefabBeingBaked = null; + } + } + + if (configPossiblyDirty) { + // configs needs to be reimported as well + var configPath = NetworkProjectConfigUtilities.GetGlobalConfigPath(createIfMissing: false); + if (!string.IsNullOrEmpty(configPath)) { + AssetDatabase.ImportAsset(configPath); + } + } + } + + void OnPostprocessPrefab(GameObject prefab) { + // prefab is already loaded, so this is a good place to discriminate Fusion and non-Fusion prefabs + if (assetPath == _prefabBeingBaked) { + FusionEditorLog.TraceImport(assetPath, $"Ignoring OnPostprocessPrefab, being imported"); + } else if (prefab.GetComponent() || AssetDatabaseUtils.HasLabel(assetPath, NetworkProjectConfigImporter.FusionPrefabTag)) { + FusionEditorLog.TraceImport(assetPath, $"Fusion prefab detected, going to bake in OnPostprocessAllAssets"); + _pendingPrefabImports.Add(assetPath); + } + } + + + static bool BakePrefab(string prefabPath, out GameObject root) { + + root = null; + + var assetGuid = AssetDatabase.AssetPathToGUID(prefabPath); + if (!NetworkObjectGuid.TryParse(assetGuid, out var guid)) { + FusionEditorLog.ErrorImport(prefabPath, $"Unable to parse guid: \"{assetGuid}\", not going to bake"); + return false; + } + + var stageGo = PrefabUtility.LoadPrefabContents(prefabPath); + if (!stageGo) { + FusionEditorLog.ErrorImport(prefabPath, $"Unable to load prefab contents"); + return false; + } + + var sw = System.Diagnostics.Stopwatch.StartNew(); + + try { + bool dirty = NetworkObjectEditor.BakeHierarchy(stageGo, guid); + FusionEditorLog.TraceImport(prefabPath, $"Baking took {sw.Elapsed}, changed: {dirty}"); + + if (dirty) { + root = PrefabUtility.SaveAsPrefabAsset(stageGo, prefabPath); + return true; + } else { + return false; + } + } finally { + PrefabUtility.UnloadPrefabContents(stageGo); + } + } + + private static Action SetDirty = x => EditorUtility.SetDirty((UnityEngine.Object)x); + private static Dictionary SceneObjectIds = new Dictionary(); + + private static Func GuidProvider = obj => { + var instanceId = obj.GetInstanceID(); + var guid = obj.NetworkGuid; + + if (guid.IsValid) { + if (SceneObjectIds.TryGetValue(guid, out var otherInstanceId)) { + if (otherInstanceId == instanceId) { + // can keep the guid + return guid; + } else { + var otherInstance = EditorUtility.InstanceIDToObject(otherInstanceId); + if (otherInstance == null || otherInstance == obj) { + // fine, can reuse + SceneObjectIds[guid] = instanceId; + return guid; + } else { + // can't reuse + } + } + } else { + // keep and add to cache + SceneObjectIds.Add(guid, instanceId); + return guid; + } + } + + // need a new guid + do { + guid = Guid.NewGuid(); + } while (SceneObjectIds.ContainsKey(guid)); + + SceneObjectIds.Add(guid, instanceId); + return guid; + }; + + public static void BakeSceneObjects(Scene scene) { + var sw = System.Diagnostics.Stopwatch.StartNew(); + + try { + foreach (var root in scene.GetRootGameObjects()) { + NetworkObjectEditor.BakeHierarchy(root, null, SetDirty, guidProvider: GuidProvider); + } + } finally { + FusionEditorLog.TraceImport(scene.path, $"Baking {scene} took: {sw.Elapsed}"); + } + } + + private static void OnPlaymodeChange(PlayModeStateChange change) { + if (change != PlayModeStateChange.ExitingEditMode) { + return; + } + for (int i = 0; i < EditorSceneManager.sceneCount; ++i) { + BakeSceneObjects(EditorSceneManager.GetSceneAt(i)); + } + } + + private static void OnSceneSaving(Scene scene, string path) { + BakeSceneObjects(scene); + } + + [MenuItem("Fusion/Bake Scene Objects")] + public static void BakeSceneObjects() { + for (int i = 0; i < SceneManager.sceneCount; ++i) { + var scene = SceneManager.GetSceneAt(i); + try { + BakeSceneObjects(scene); + } catch (Exception ex) { + Debug.LogError($"Failed to bake scene {scene}: {ex}"); + } + } + } + + void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report) { + SceneObjectIds.Clear(); + } + + void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report) { + SceneObjectIds.Clear(); + } + + void IProcessSceneWithReport.OnProcessScene(Scene scene, BuildReport report) { + if (report == null) { + return; + } + + BakeSceneObjects(scene); + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigAssetEditor.cs + +namespace Fusion.Editor { + using System.Collections.Generic; + using System.IO; + using UnityEditor; + using UnityEngine; + + [CustomEditor(typeof(NetworkProjectConfigAsset))] + public class NetworkProjectConfigAssetEditor : UnityEditor.Editor { + + public override void OnInspectorGUI() { + var config = (NetworkProjectConfigAsset) target; + + EditorGUILayout.HelpBox($"Config format has changed. Click on the button below to convert this config to the new format.", MessageType.Info); + + if (GUILayout.Button("Convert To The New Config Format")) { + Selection.activeObject = Convert(config, true); + } + + GUI.enabled = false; + EditorGUILayout.PropertyField(serializedObject.FindProperty("Config"), true); + GUI.enabled = true; + } + + internal static NetworkProjectConfigImporter Convert(NetworkProjectConfigAsset config, bool deferDelete = false) { +#pragma warning disable CS0618 // Type or member is obsolete + config.Config.AssembliesToWeave = config.AssembliesToWeave; +#pragma warning restore CS0618 // Type or member is obsolete + + var json = EditorJsonUtility.ToJson(config.Config, true); + var path = AssetDatabase.GetAssetPath(config); + var newPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path) + NetworkProjectConfigImporter.Extension); + + File.WriteAllText(newPath, json); + AssetDatabase.ImportAsset(newPath); + + var importer = NetworkProjectConfigUtilities.GlobalConfigImporter; + importer.PrefabAssetsContainerPath = config.PrefabAssetsContainerPath; + EditorUtility.SetDirty(importer); + AssetDatabase.SaveAssets(); + + if (deferDelete) { + EditorApplication.delayCall += () => AssetDatabase.DeleteAsset(path); + } else { + AssetDatabase.DeleteAsset(path); + } + return importer; + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkRunnerEditor.cs + +namespace Fusion.Editor { + using System.Linq; + using UnityEditor; + + [CustomEditor(typeof(NetworkRunner))] + public class NetworkRunnerEditor : BehaviourEditor { + + void Label(string label, T value) { + EditorGUILayout.LabelField(label, (value != null ? value.ToString() : "null")); + } + + public override void OnInspectorGUI() { + base.OnInspectorGUI(); + + var runner = target as NetworkRunner; + if (runner && EditorApplication.isPlaying) { + Label("State", runner.IsRunning ? "Running" : (runner.IsShutdown ? "Shutdown" : "None")); + + if (runner.IsRunning) { + Label("Game Mode", runner.GameMode); + Label("Simulation Mode", runner.Mode); + Label("Is Player", runner.IsPlayer); + Label("Local Player", runner.LocalPlayer); + + var localplayerobj = runner.LocalPlayer.IsValid ? runner.GetPlayerObject(runner.LocalPlayer) : null; + EditorGUILayout.ObjectField("Local PlayerObject", localplayerobj, typeof(NetworkObject), true); + + Label("Active Players", runner.ActivePlayers.Count()); + Label("Is Cloud Ready", runner.IsCloudReady); + Label("Is SinglePlayer", runner.IsSinglePlayer); + Label("Scene Ref", runner.CurrentScene); + Label("Is Shared Mode Master Client", runner.IsSharedModeMasterClient); + Label("UserId", runner.UserId); + Label("AuthenticationValues", runner.AuthenticationValues); + + Label("SessionInfo:IsValid", runner.SessionInfo.IsValid); + Label("SessionInfo:Name", runner.SessionInfo.Name); + Label("SessionInfo:IsVisible", runner.SessionInfo.IsVisible); + Label("SessionInfo:IsOpen", runner.SessionInfo.IsOpen); + Label("SessionInfo:Region", runner.SessionInfo.Region); + + if (runner.IsClient) { + Label("Is Connected To Server", runner.IsConnectedToServer); + Label("Current Connection Type", runner.CurrentConnectionType); + } + } + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/NetworkSceneDebugStartEditor.cs + +// file deleted + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Ordering/OrderWindow.cs + +namespace Fusion.Editor { + +#if UNITY_EDITOR + + using System; + using System.Text; + using UnityEditor; + using UnityEngine; + + public class OrderWindow : EditorWindow { + + private const int CLASS_WIDTH = 100; + private const int COL_WIDTH = 38; + private const int SCRL_WIDTH = 12; + + static bool _errorSuppression; + static OrderNode[] _sortedNodes; + static string[] _stageNames; + static string[] _modeNames; + + Vector2 _scrollPos; + + SimulationModes _modes = (SimulationModes)(-1); + SimulationStages _stages = (SimulationStages)(-1); + + OrderNode _rolloverNode; + OrderNode _prevRolloverNode; + + [UnityEditor.Callbacks.DidReloadScripts] + static void Init() { + + // Don't keep running sorter after a null result. There is a error that is already reported to the log. + if (_errorSuppression) + return; + + var sorter = new OrderSorter(); + _sortedNodes = sorter.Run(); + + if (_sortedNodes == null) { + _errorSuppression = true; + } + } + + static readonly Lazy _beforeIconRollover = new Lazy(() => { + var result = new GUIContent("\u25c4", $"Script contains an [{nameof(OrderBeforeAttribute)}]."); + return result; + }); + static readonly Lazy _beforeIconNoRollover = new Lazy(() => { + var result = new GUIContent("\u25c4", ""); + return result; + }); + + static readonly Lazy _afterIconRollover = new Lazy(() => { + var result = new GUIContent("\u25ba", $"Script contains an [{nameof(OrderAfterAttribute)}]."); + return result; + }); + static readonly Lazy _afterIconNoRollover = new Lazy(() => { + var result = new GUIContent("\u25ba", ""); + return result; + }); + + + static readonly Lazy _simIconRollover = new Lazy(() => { + var result = new GUIContent("\u25cf", $"Script contains a [{nameof(SimulationBehaviourAttribute)}]."); + return result; + }); + static readonly Lazy _simIconNoRollover = new Lazy(() => { + var result = new GUIContent("\u25cb", ""); + return result; + }); + + static readonly Lazy _rowStyle = new Lazy(() => { + var result = new GUIStyle("Label") { padding = new RectOffset(4, 4, 0, 0), margin = new RectOffset(3, 3, 0, 0) }; + return result; + }); + + static readonly Lazy _classLabelButtonStyle = new Lazy(() => { + var result = new GUIStyle((GUIStyle)"toolbarButtonLeft") { + //fixedHeight = 21, + }; + return result; + }); + + static readonly Lazy _classLabelButtonSelectedStyle = new Lazy(() => { + var result = new GUIStyle((GUIStyle)"LargeButtonLeft") { + //fontSize = 11, + //stretchWidth = false, + //alignment = TextAnchor.UpperLeft, + //padding = new RectOffset(5, 5, 4, 2), + //fixedHeight = 21, + //richText = true + }; + return result; + }); + + static readonly Lazy _helpStyle = new Lazy(() => { + var result = new GUIStyle(FusionGUIStyles.GroupBoxType.Info.GetStyle()); + result.fontSize = 10; + return result; + }); + + static readonly Lazy _litIndicatorStyle = new Lazy(() => { + var result = new GUIStyle(_gridLabelStyle.Value); + result.alignment = TextAnchor.MiddleCenter; + result.padding = new RectOffset(0, 0, 0, 0); + result.fontSize = 8; + return result; + }); + + static readonly Lazy _grayIndicatorStyle = new Lazy(() => { + var result = new GUIStyle(_litIndicatorStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.33f, .33f, .33f) : new Color(.66f, .66f, .66f); + //result.alignment = TextAnchor.MiddleLeft; + //result.fontSize = 8; + return result; + }); + + + static readonly Lazy _defaultLabelStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.label) { + fontSize = 11, + alignment = TextAnchor.UpperLeft, + padding = new RectOffset(5, 5, 4, 2), + fixedHeight = 21, + }; + return result; + }); + + static readonly Lazy _sbDerivedLabelStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + //result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.5f, 1, .5f) : new Color(0,0,0); + return result; + }); + + static readonly Lazy _nbDerivedLabelStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.5f, .75f, 1f) : new Color(0,0,.4f); + return result; + }); + + static readonly Lazy _sbStyle = new Lazy(() => { + var result = new GUIStyle(_sbDerivedLabelStyle.Value); + result.fontStyle = FontStyle.Bold; + return result; + }); + + static readonly Lazy _nbStyle = new Lazy(() => { + var result = new GUIStyle(_nbDerivedLabelStyle.Value); + result.fontStyle = FontStyle.Bold; + return result; + }); + + static readonly Lazy _flagsGroupStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.helpBox) { + border = new RectOffset(0,0,0,0), + padding = new RectOffset(3, 3, 3, 3), + margin = new RectOffset(), + alignment = TextAnchor.MiddleCenter, + fontSize = 9 + }; + return result; + }); + + static readonly Lazy _gridLabelStyle = new Lazy(() => { + var result = new GUIStyle("MiniLabel") { + alignment = TextAnchor.UpperCenter, + padding = new RectOffset(0, 0, 4, 0), + margin = new RectOffset(0, 0, 0, 0), + fontSize = 9 + }; + return result; + }); + + static readonly Lazy _fadedGridLabelStyle = new Lazy(() => { + var result = new GUIStyle(_gridLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.4f, .4f, .4f) : new Color(.6f, .6f, .6f); + return result; + }); + + static readonly Lazy _selectedLabelStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(1f, 1f, 1f) : new Color(.0f, .0f, .0f); + result.fontStyle = FontStyle.Bold; + return result; + }); + + static readonly Lazy _fadedLabelStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.4f, .4f, .4f) : new Color(.6f, .6f, .6f); + return result; + }); + + static readonly Lazy _beforeStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.8f, .4f, .4f) : new Color(.1f, .1f, .5f); + result.fontStyle = FontStyle.Bold; + return result; + }); + + static readonly Lazy _afterStyle = new Lazy(() => { + var result = new GUIStyle(_defaultLabelStyle.Value); + result.normal.textColor = EditorGUIUtility.isProSkin ? new Color(.5f, .5f, .8f) : new Color(.5f, .2f, .0f); + result.fontStyle = FontStyle.Bold; + return result; + }); + + + static readonly Lazy _toolbarButtonStyle = new Lazy(()=> { + var result = new GUIStyle("ToolbarButtonFlat"); + return result; + }); + + + [MenuItem("Window/Fusion/Fusion Script Execution Inspector")] + [MenuItem("Fusion/Windows/Fusion Script Execution Inspector")] + public static void ShowWindow() { + var window = GetWindow(typeof(OrderWindow), false, "Fusion Script Execution"); + window.minSize = new Vector2(400, 300); + } + + void CacheEnumNames() { + + var modevals = Enum.GetValues(typeof(SimulationModes)); + _modeNames = new string[modevals.Length]; + for (int i = 0; i < _modeNames.Length; ++i) { + _modeNames[i] = ((SimulationModes)(1 << i)).GetDescription(); + } + + var stagevals = Enum.GetValues(typeof(SimulationStages)); + _stageNames = new string[stagevals.Length]; + for (int i = 0; i < _stageNames.Length; ++i) { + // SimulationStages does not have a 1 flag - workaround with +1 + _stageNames[i] = ((SimulationStages)(1 << (i + 1))).GetDescription(); + } + } + + void OnGUI() { + + if (_sortedNodes == null) + Init(); + + SerializedObject so = new SerializedObject(this); + + EditorGUILayout.LabelField("Use [OrderBefore], [OrderAfter] and [SimulationBehaviour] attributes on SimulationBehaviours to control order and execution.", _helpStyle.Value); + + EditorGUILayout.BeginVertical(_rowStyle.Value); + DrawHeader(); + DrawSubHeader(); + EditorGUILayout.EndVertical(); + + float headWidth = GUILayoutUtility.GetLastRect().width; + + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos); + + if (_sortedNodes == null) { + BehaviourEditorUtils.DrawWarnBox("Conflicts in script OrderBefore and OrderAfter attributes. Check the Unity Debug Log for details on script conflicts.", msgtype: MessageType.Error); + } else { + if (_stageNames == null) { + CacheEnumNames(); + } + foreach (var node in _sortedNodes) { + + var val = node.SimFlags; + + if (node.Type == typeof(SimulationBehaviour) || (val.modes & _modes) != 0 || (val.stages & _stages) != 0) { + DrawRow(node); + } + } + } + EditorGUILayout.EndScrollView(); + } + + private void DrawHeader() { + var style = _gridLabelStyle.Value; + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(" ", GUILayout.ExpandWidth(true), GUILayout.MinWidth(1), GUILayout.MaxHeight(8)); + EditorGUILayout.LabelField("Modes", style, GUILayout.Width(COL_WIDTH * Enum.GetValues(typeof(SimulationModes)).Length)); + EditorGUILayout.LabelField(" ", style, GUILayout.Width(8), GUILayout.MaxHeight(8)); + EditorGUILayout.LabelField("Stages", style, GUILayout.Width(COL_WIDTH * Enum.GetValues(typeof(SimulationStages)).Length)); + EditorGUILayout.LabelField(" ", style, GUILayout.Width(16), GUILayout.MaxHeight(8)); + EditorGUILayout.EndHorizontal(); + } + private void DrawSubHeader() { + + EditorGUILayout.BeginHorizontal(); + + // Master All/None toggle + Rect r = EditorGUILayout.GetControlRect(false, GUILayout.MaxWidth(40)); + if (GUI.Button(r, "All", _toolbarButtonStyle.Value)) { + _modes = (SimulationModes)(-1); + _stages = (SimulationStages)(-1); + } + r = EditorGUILayout.GetControlRect(false, GUILayout.MaxWidth(40)); + if (GUI.Button(r, "None", _toolbarButtonStyle.Value)){ + _modes = 0; + _stages = 0; + } + + // Spacing to align the right aligned check boxes + EditorGUILayout.LabelField(" ", GUILayout.MinWidth(0), GUILayout.ExpandWidth(true)); // GUILayout.Width(rowLabelWidth - 40 - 40)); + + foreach (var flag in Enum.GetValues(typeof(SimulationModes))) { + var m = (SimulationModes)flag; + var curr = (_modes & m) != 0; + bool on = EditorGUILayout.Toggle(curr, GUILayout.Width(COL_WIDTH)); + if (on != curr) { + if (on) + _modes |= m; + else + _modes &= ~m; + } + } + + EditorGUILayout.LabelField(" ", _gridLabelStyle.Value, GUILayout.Width(8)); + + foreach (var flag in Enum.GetValues(typeof(SimulationStages))) { + var s = (SimulationStages)flag; + var curr = (_stages & s) != 0; + bool on = EditorGUILayout.Toggle(curr, GUILayout.Width(COL_WIDTH)); + if (on != curr) { + if (on) + _stages |= s; + else + _stages &= ~s; + } + } + + EditorGUILayout.EndHorizontal(); + } + + private void DrawRow(OrderNode node) { + + EditorGUILayout.BeginHorizontal(_rowStyle.Value, GUILayout.Width(position.width - 20)); + + string name = node.Type.Name; + + var isDefaultModesAndStages = node.SimFlags.modes == OrderNode.ALL_MODES && node.SimFlags.stages == OrderNode.ALL_STAGES; + bool thisItemIsSelected = _rolloverNode == node; + bool selectedIsNull = _rolloverNode == null; + bool isSimulationBehaviour = node.Type == typeof(SimulationBehaviour); + bool isNetworkBehaviour = node.Type == typeof(NetworkBehaviour); + + var labelstyle = + (!selectedIsNull && thisItemIsSelected) ? _selectedLabelStyle.Value : + (!selectedIsNull && _rolloverNode.OrigAfter.Contains(node)) ? _afterStyle.Value : + (!selectedIsNull && _rolloverNode.OrigBefore.Contains(node)) ? _beforeStyle.Value : + (!selectedIsNull) ? _fadedLabelStyle.Value : + isSimulationBehaviour ? _sbStyle.Value : + isNetworkBehaviour ? _nbStyle.Value : + typeof(NetworkBehaviour).IsAssignableFrom(node.Type) ? _nbDerivedLabelStyle.Value : _sbDerivedLabelStyle.Value; + + GUIStyle back = thisItemIsSelected ? _classLabelButtonSelectedStyle.Value : _classLabelButtonStyle.Value; + + Rect r = EditorGUILayout.GetControlRect(GUILayout.MinWidth(CLASS_WIDTH)); + + // Draw backing button for label + if (!isSimulationBehaviour && !isNetworkBehaviour && GUI.Button(new Rect(r) { xMax = r.xMax + 4 }, GUIContent.none, back)) { + // Find cs file location: + var found = AssetDatabase.FindAssets(node.Type.Name); + if (found.Length > 0) { + foreach (var f in found) { + var path = AssetDatabase.GUIDToAssetPath(f); + + // Make sure this file is an exact match, since the search can find subsets + if (!path.Contains("/" + node.Type.Name + ".cs")) + continue; + + var obj = AssetDatabase.LoadAssetAtPath(path, typeof(UnityEngine.Object)) as UnityEngine.Object; + + if (obj != null) + EditorGUIUtility.PingObject(obj); + } + } + _rolloverNode = !thisItemIsSelected ? node : null; + } + + // Draw text label + GUI.Label(r, name, labelstyle); + + // Draw flags + if (!isSimulationBehaviour && !isNetworkBehaviour) { + var gridstyle = selectedIsNull || thisItemIsSelected ? _gridLabelStyle.Value : _fadedGridLabelStyle.Value; + var modes = node.SimFlags.modes; + + using (var h = new EditorGUILayout.HorizontalScope(_flagsGroupStyle.Value, GUILayout.ExpandWidth(false), GUILayout.Width(12 * 3))) { + + EditorGUILayout.LabelField(node.FoundBefores ? _beforeIconRollover.Value : _beforeIconNoRollover.Value, node.FoundBefores ? _litIndicatorStyle.Value : _grayIndicatorStyle.Value, GUILayout.MinWidth(16)); + EditorGUILayout.LabelField(isDefaultModesAndStages ? _simIconNoRollover.Value : _simIconRollover.Value, isDefaultModesAndStages ? _grayIndicatorStyle.Value : _litIndicatorStyle.Value, GUILayout.MinWidth(16)); + EditorGUILayout.LabelField(node.FoundAfters ? _afterIconRollover.Value : _afterIconNoRollover.Value, node.FoundAfters ? _litIndicatorStyle.Value : _grayIndicatorStyle.Value, GUILayout.MinWidth(16)); + } + + using (var h = new EditorGUILayout.HorizontalScope(_flagsGroupStyle.Value, GUILayout.Width(COL_WIDTH * 3))) { + + for (int i = 0; i < _modeNames.Length; ++i) { + var flag = (SimulationModes)(1 << i); + bool used = (modes & flag) != 0; + string lbl = used ? _modeNames[i] : "--"; + EditorGUILayout.LabelField(lbl, gridstyle, GUILayout.Width(COL_WIDTH)); + } + } + + var stages = node.SimFlags.stages; + + using (var h = new EditorGUILayout.HorizontalScope(_flagsGroupStyle.Value, GUILayout.Width(COL_WIDTH * 2))) { + + // Stages is missing a 1 value (due to removal), so there are only the flags 2 and 4. + for (int i = 0; i < _stageNames.Length; ++i) { + var flag = (SimulationStages)(1 << (i + 1)); + + bool used = (stages & flag) != 0; + string lbl = used ? _stageNames[i] : "--"; + EditorGUILayout.LabelField(lbl, gridstyle, GUILayout.Width(COL_WIDTH)); + } + } + } + + EditorGUILayout.EndHorizontal(); + } + } +#endif +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/PhotonAppSettingsEditor.cs + +namespace Fusion.Editor { + using System.Collections; + using System.Collections.Generic; + using UnityEngine; + using UnityEditor; + using Photon.Realtime; + + [CustomEditor(typeof(PhotonAppSettings))] + public class PhotonAppSettingsEditor : Editor { + + public override void OnInspectorGUI() { + FusionEditorGUI.InjectPropertyDrawers(serializedObject, addForAllFields: true); + base.DrawDefaultInspector(); + } + + [MenuItem("Fusion/Realtime Settings", priority = 200)] + public static void PingNetworkProjectConfigAsset() { + EditorGUIUtility.PingObject(PhotonAppSettings.Instance); + Selection.activeObject = PhotonAppSettings.Instance; + } + } + +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/Animator/AnimatorControllerTools.cs + +// --------------------------------------------------------------------------------------------- +// PhotonNetwork Framework for Unity - Copyright (C) 2020 Exit Games GmbH +// developer@exitgames.com +// --------------------------------------------------------------------------------------------- + +namespace Fusion.Editor { + using System.Collections.Generic; + using UnityEngine; + + using UnityEditor.Animations; + using UnityEditor; + + /// + /// Storage type for AnimatorController cached transition data, which is a bit different than basic state hashes + /// + [System.Serializable] + public class TransitionInfo { + public int index; + public int hash; + public int state; + public int destination; + public float duration; + public float offset; + public bool durationIsFixed; + + public TransitionInfo(int index, int hash, int state, int destination, float duration, float offset, bool durationIsFixed) { + this.index = index; + this.hash = hash; + this.state = state; + this.destination = destination; + this.duration = duration; + this.offset = offset; + this.durationIsFixed = durationIsFixed; + } + } + + public static class AnimatorControllerTools { + + //// Attach methods to Fusion.Runtime NetworkedAnimator + //[InitializeOnLoadMethod] + //public static void RegisterFusionDelegates() { + // NetworkedAnimator.GetWordCountDelegate = GetWordCount; + //} + + public static AnimatorController GetController(this Animator a) { + + RuntimeAnimatorController rac = a.runtimeAnimatorController; + AnimatorOverrideController overrideController = rac as AnimatorOverrideController; + + /// recurse until no override controller is found + while (overrideController != null) { + rac = overrideController.runtimeAnimatorController; + overrideController = rac as AnimatorOverrideController; + } + + return rac as AnimatorController; + } + + public static void GetTriggerNames(this AnimatorController ctr, List namelist) { + namelist.Clear(); + + foreach (var p in ctr.parameters) + if (p.type == AnimatorControllerParameterType.Trigger) { + if (namelist.Contains(p.name)) { + Debug.LogWarning("Identical Trigger Name Found. Check animator on '" + ctr.name + "' for repeated trigger names."); + } else + namelist.Add(p.name); + } + } + + public static void GetTriggerNames(this AnimatorController ctr, List hashlist) { + hashlist.Clear(); + + foreach (var p in ctr.parameters) + if (p.type == AnimatorControllerParameterType.Trigger) { + hashlist.Add(Animator.StringToHash(p.name)); + } + } + + /// ------------------------------ STATES -------------------------------------- + + public static void GetStatesNames(this AnimatorController ctr, List namelist) { + namelist.Clear(); + + foreach (var l in ctr.layers) { + var states = l.stateMachine.states; + ExtractNames(ctr, l.name, states, namelist); + + var substates = l.stateMachine.stateMachines; + ExtractSubNames(ctr, l.name, substates, namelist); + } + } + + public static void ExtractSubNames(AnimatorController ctr, string path, ChildAnimatorStateMachine[] substates, List namelist) { + foreach (var s in substates) { + var sm = s.stateMachine; + var subpath = path + "." + sm.name; + + ExtractNames(ctr, subpath, s.stateMachine.states, namelist); + ExtractSubNames(ctr, subpath, s.stateMachine.stateMachines, namelist); + } + } + + public static void ExtractNames(AnimatorController ctr, string path, ChildAnimatorState[] states, List namelist) { + foreach (var st in states) { + string name = st.state.name; + string layerName = path + "." + st.state.name; + if (!namelist.Contains(name)) { + namelist.Add(name); + } + if (namelist.Contains(layerName)) { + Debug.LogWarning("Identical State Name '" + st.state.name + "' Found. Check animator on '" + ctr.name + "' for repeated State names as they cannot be used nor networked."); + } else + namelist.Add(layerName); + } + + } + + public static void GetStatesNames(this AnimatorController ctr, List hashlist) { + hashlist.Clear(); + + foreach (var l in ctr.layers) { + var states = l.stateMachine.states; + ExtractHashes(ctr, l.name, states, hashlist); + + var substates = l.stateMachine.stateMachines; + ExtractSubtHashes(ctr, l.name, substates, hashlist); + } + + } + + public static void ExtractSubtHashes(AnimatorController ctr, string path, ChildAnimatorStateMachine[] substates, List hashlist) { + foreach (var s in substates) { + var sm = s.stateMachine; + var subpath = path + "." + sm.name; + + ExtractHashes(ctr, subpath, sm.states, hashlist); + ExtractSubtHashes(ctr, subpath, sm.stateMachines, hashlist); + } + } + + public static void ExtractHashes(AnimatorController ctr, string path, ChildAnimatorState[] states, List hashlist) { + foreach (var st in states) { + int hash = Animator.StringToHash(st.state.name); + string fullname = path + "." + st.state.name; + int layrhash = Animator.StringToHash(fullname); + if (!hashlist.Contains(hash)) { + hashlist.Add(hash); + } + if (hashlist.Contains(layrhash)) { + Debug.LogWarning("Identical State Name '" + st.state.name + "' Found. Check animator on '" + ctr.name + "' for repeated State names as they cannot be used nor networked."); + } else + hashlist.Add(layrhash); + } + } + + //public static void GetTransitionNames(this AnimatorController ctr, List transInfo) + //{ + // transInfo.Clear(); + + // transInfo.Add("0"); + + // foreach (var l in ctr.layers) + // { + // foreach (var st in l.stateMachine.states) + // { + // string sname = l.name + "." + st.state.name; + + // foreach (var t in st.state.transitions) + // { + // string dname = l.name + "." + t.destinationState.name; + // string name = (sname + " -> " + dname); + // transInfo.Add(name); + // //Debug.Log(sname + " -> " + dname + " " + Animator.StringToHash(sname + " -> " + dname)); + // } + // } + // } + + //} + + + //public static void GetTransitions(this AnimatorController ctr, List transInfo) + //{ + // transInfo.Clear(); + + // transInfo.Add(new TransitionInfo(0, 0, 0, 0, 0, 0, false)); + + // int index = 1; + + // foreach (var l in ctr.layers) + // { + // foreach (var st in l.stateMachine.states) + // { + // string sname = l.name + "." + st.state.name; + // int shash = Animator.StringToHash(sname); + + // foreach (var t in st.state.transitions) + // { + // string dname = l.name + "." + t.destinationState.name; + // int dhash = Animator.StringToHash(dname); + // int hash = Animator.StringToHash(sname + " -> " + dname); + // TransitionInfo ti = new TransitionInfo(index, hash, shash, dhash, t.duration, t.offset, t.hasFixedDuration); + // transInfo.Add(ti); + // //Debug.Log(index + " " + sname + " -> " + dname + " " + Animator.StringToHash(sname + " -> " + dname)); + // index++; + // } + // } + // } + //} + + + const double AUTO_REBUILD_RATE = 10f; + private static List tempNamesList = new List(); + private static List tempHashList = new List(); + + internal static (int, int, int) GetWordCount(this NetworkMecanimAnimator netAnim, AnimatorSyncSettings settings) { + /// always get new Animator in case it has changed. + Animator animator = netAnim.Animator; + if (animator == null) + animator = netAnim.GetComponent(); + + AnimatorController ac = animator.GetController(); + + int param32Count = 0; + int paramBoolcount = 0; + + bool includeI = (settings & AnimatorSyncSettings.ParameterInts) == AnimatorSyncSettings.ParameterInts; + bool includeF = (settings & AnimatorSyncSettings.ParameterFloats) == AnimatorSyncSettings.ParameterFloats; + bool includeB = (settings & AnimatorSyncSettings.ParameterBools) == AnimatorSyncSettings.ParameterBools; + bool includeT = (settings & AnimatorSyncSettings.ParameterTriggers) == AnimatorSyncSettings.ParameterTriggers; + + var parameters = ac.parameters; + for (int i = 0; i < parameters.Length; ++i) { + var param = parameters[i]; + + switch (param.type) { + case AnimatorControllerParameterType.Int: + if (includeI) + param32Count++; + break; + case AnimatorControllerParameterType.Float: + if (includeF) + param32Count++; + break; + case AnimatorControllerParameterType.Bool: + if (includeB) + paramBoolcount++; + break; + case AnimatorControllerParameterType.Trigger: + if (includeT) + paramBoolcount++; + break; + } + } + + int layerCount = ac.layers.Length; + + Debug.Log("Anim Wordcount = " + param32Count + " with bitcount of: " + paramBoolcount); + return (param32Count, paramBoolcount, layerCount); + } + + /// + /// Re-index all of the State and Trigger names in the current AnimatorController. Never hurts to run this (other than hanging the editor for a split second). + /// + internal static void GetHashesAndNames(this NetworkMecanimAnimator netAnim, + List sharedTriggNames, + List sharedStateNames, + ref int[] sharedTriggIndexes, + ref int[] sharedStateIndexes + //ref double lastRebuildTime + ) { + + /// always get new Animator in case it has changed. + Animator animator = netAnim.Animator; + if (animator == null) + animator = netAnim.GetComponent(); + + if (animator == null) { + return; + } + //if (animator && EditorApplication.timeSinceStartup - lastRebuildTime > AUTO_REBUILD_RATE) { + // lastRebuildTime = EditorApplication.timeSinceStartup; + + AnimatorController ac = animator.GetController(); + if (ac != null) { + if (ac.animationClips == null || ac.animationClips.Length == 0) + Debug.LogWarning("'" + animator.name + "' has an Animator with no animation clips. Some Animator Controllers require a restart of Unity, or for a Build to be made in order to initialize correctly."); + + bool haschanged = false; + + ac.GetTriggerNames(tempHashList); + tempHashList.Insert(0, 0); + if (!CompareIntArray(sharedTriggIndexes, tempHashList)) { + sharedTriggIndexes = tempHashList.ToArray(); + haschanged = true; + } + + ac.GetStatesNames(tempHashList); + tempHashList.Insert(0, 0); + if (!CompareIntArray(sharedStateIndexes, tempHashList)) { + sharedStateIndexes = tempHashList.ToArray(); + haschanged = true; + } + + if (sharedTriggNames != null) { + ac.GetTriggerNames(tempNamesList); + tempNamesList.Insert(0, null); + if (!CompareNameLists(tempNamesList, sharedTriggNames)) { + CopyNameList(tempNamesList, sharedTriggNames); + haschanged = true; + } + } + + if (sharedStateNames != null) { + ac.GetStatesNames(tempNamesList); + tempNamesList.Insert(0, null); + if (!CompareNameLists(tempNamesList, sharedStateNames)) { + CopyNameList(tempNamesList, sharedStateNames); + haschanged = true; + } + } + + if (haschanged) { + Debug.Log(animator.name + " has changed. SyncAnimator indexes updated."); + EditorUtility.SetDirty(netAnim); + } + } + //} + } + + private static bool CompareNameLists(List one, List two) { + if (one.Count != two.Count) + return false; + + for (int i = 0; i < one.Count; i++) + if (one[i] != two[i]) + return false; + + return true; + } + + private static bool CompareIntArray(int[] old, List temp) { + if (ReferenceEquals(old, null)) + return false; + + if (old.Length != temp.Count) + return false; + + for (int i = 0; i < old.Length; i++) + if (old[i] != temp[i]) + return false; + + return true; + } + + private static void CopyNameList(List src, List trg) { + trg.Clear(); + for (int i = 0; i < src.Count; i++) + trg.Add(src[i]); + } + + } + +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/AssetDatabaseUtils.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; +#if UNITY_2021_2_OR_NEWER + using UnityEditor.SceneManagement; +#else + using UnityEditor.Experimental.SceneManagement; +#endif + + using UnityEngine; + + public static class AssetDatabaseUtils { + public static T GetSubAsset(GameObject prefab) where T : ScriptableObject { + + if (!AssetDatabase.IsMainAsset(prefab)) { + throw new InvalidOperationException($"Not a main asset: {prefab}"); + } + + string path = AssetDatabase.GetAssetPath(prefab); + if (string.IsNullOrEmpty(path)) { + throw new InvalidOperationException($"Empty path for prefab: {prefab}"); + } + + var subAssets = AssetDatabase.LoadAllAssetsAtPath(path).OfType().ToList(); + if (subAssets.Count > 1) { + Debug.LogError($"More than 1 asset of type {typeof(T)} on {path}, clean it up manually"); + } + + return subAssets.Count == 0 ? null : subAssets[0]; + } + + public static bool IsSceneObject(GameObject go) { + return ReferenceEquals(PrefabStageUtility.GetPrefabStage(go), null) && (PrefabUtility.IsPartOfPrefabAsset(go) == false || PrefabUtility.GetPrefabAssetType(go) == PrefabAssetType.NotAPrefab); + } + + public static bool HasLabel(string assetPath, string label) { + var guidStr = AssetDatabase.AssetPathToGUID(assetPath); + if (!GUID.TryParse(guidStr, out var guid)) { + return false; + } + + var labels = AssetDatabase.GetLabels(guid); + var index = Array.IndexOf(labels, label); + return index >= 0; + } + + public static bool HasLabel(UnityEngine.Object obj, string label) { + var labels = AssetDatabase.GetLabels(obj); + var index = Array.IndexOf(labels, label); + return index >= 0; + } + + public static bool SetLabel(UnityEngine.Object obj, string label, bool present) { + var labels = AssetDatabase.GetLabels(obj); + var index = Array.IndexOf(labels, label); + if (present) { + if (index >= 0) { + return false; + } + ArrayUtility.Add(ref labels, label); + } else { + if (index < 0) { + return false; + } + ArrayUtility.RemoveAt(ref labels, index); + } + + AssetDatabase.SetLabels(obj, labels); + return true; + } + + public static T SetScriptableObjectType(ScriptableObject obj) where T : ScriptableObject { + if (obj.GetType() == typeof(T)) { + return (T)obj; + } + + var tmp = ScriptableObject.CreateInstance(typeof(T)); + try { + using (var dst = new SerializedObject(obj)) { + using (var src = new SerializedObject(tmp)) { + var scriptDst = dst.FindPropertyOrThrow(FusionEditorGUI.ScriptPropertyName); + var scriptSrc = src.FindPropertyOrThrow(FusionEditorGUI.ScriptPropertyName); + Debug.Assert(scriptDst.objectReferenceValue != scriptSrc.objectReferenceValue); + dst.CopyFromSerializedProperty(scriptSrc); + dst.ApplyModifiedPropertiesWithoutUndo(); + return (T)dst.targetObject; + } + } + } finally { + UnityEngine.Object.DestroyImmediate(tmp); + } + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/AutoGUIUtilities.cs + +// Removed May 22 2021 (Alpha 3) + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/EnumEditorUtilities.cs + +namespace Fusion.Editor { + using System; + using System.Linq; + using System.Reflection; + + public static class EnumEditorUtilities { + + public static string GetDescription(this Enum GenericEnum) { + Type genericEnumType = GenericEnum.GetType(); + MemberInfo[] memberInfo = genericEnumType.GetMember(GenericEnum.ToString()); + if ((memberInfo != null && memberInfo.Length > 0)) { + var _Attribs = memberInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false); + if ((_Attribs != null && _Attribs.Count() > 0)) { + return ((System.ComponentModel.DescriptionAttribute)_Attribs.ElementAt(0)).Description; + } + } + return GenericEnum.ToString(); + } + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorGUI.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEngine; + + public static partial class FusionEditorGUI { + + public static void LayoutSelectableLabel(GUIContent label, string contents) { + var rect = EditorGUILayout.GetControlRect(); + rect = EditorGUI.PrefixLabel(rect, label); + using (new EditorGUI.IndentLevelScope(-EditorGUI.indentLevel)) { + EditorGUI.SelectableLabel(rect, contents); + } + } + + public static bool DrawDefaultInspector(SerializedObject obj, bool drawScript = true) { + EditorGUI.BeginChangeCheck(); + obj.UpdateIfRequiredOrScript(); + + // Loop through properties and create one field (including children) for each top level property. + SerializedProperty property = obj.GetIterator(); + bool expanded = true; + while (property.NextVisible(expanded)) { + if ( ScriptPropertyName == property.propertyPath ) { + if (drawScript) { + using (new EditorGUI.DisabledScope("m_Script" == property.propertyPath)) { + EditorGUILayout.PropertyField(property, true); + } + } + } else { + EditorGUILayout.PropertyField(property, true); + } + expanded = false; + } + + obj.ApplyModifiedProperties(); + return EditorGUI.EndChangeCheck(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorGUI.InlineHelp.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using UnityEditor; + using UnityEngine; + + public static partial class FusionEditorGUI { + + static Dictionary> s_codeDocCache = new Dictionary>(); + + static (object, string) s_expandedHelp = default; + + internal static Rect GetInlineHelpButtonRect(InlineHelpButtonPlacement buttonPlacement, Rect position, GUIContent label, bool expectFoldout = true) { + float size = InlineHelpStyle.ButtonSize; + switch (buttonPlacement) { + case InlineHelpButtonPlacement.BeforeLabel: { + var buttonRect = new Rect(position.x - size, position.y + ((16 - size) / 2) + 1, size, size); + using (new EditorGUI.IndentLevelScope(expectFoldout ? -1 : 0)) { + buttonRect.x = EditorGUI.IndentedRect(buttonRect).x; + // give indented items a little extra padding - no need for them to be so crammed + if (buttonRect.x > 8) { + buttonRect.x -= 2; + } + } + return buttonRect; + } + + case InlineHelpButtonPlacement.AfterLabel: { + var labelSize = EditorStyles.label.CalcSize(label); + var buttonRect = new Rect(position.x + labelSize.x, position.y, size, size); + buttonRect = EditorGUI.IndentedRect(buttonRect); + buttonRect.x = Mathf.Min(buttonRect.x + 2, EditorGUIUtility.labelWidth - size); + buttonRect.width = size; + return buttonRect; + } + + case InlineHelpButtonPlacement.BeforeValue: + default: + return new Rect(position.x + EditorGUIUtility.labelWidth - size, position.y, size, size); + } + } + + internal static bool DrawInlineHelpButton(Rect buttonRect, bool state, bool doButton = true, bool doIcon = true) { + bool result = false; + if (doButton) { + EditorGUIUtility.AddCursorRect(buttonRect, MouseCursor.Link); + using (GUI.enabled ? null : new FusionEditorGUI.EnabledScope(true)) { + result = GUI.Button(buttonRect, state ? InlineHelpStyle.HideInlineContent : InlineHelpStyle.ShowInlineContent, GUIStyle.none); + } + } + + if (doIcon) { + // paint over what the inspector has drawn + if (Event.current.type == EventType.Repaint) { + GUI.DrawTexture(buttonRect, state ? FusionGUIStyles.HelpIconSelected : FusionGUIStyles.HelpIconUnselected); + } + } + + return result; + } + + const float SCROLL_WIDTH = 16f; + const float LEFT_HELP_INDENT = 8f; + + internal static Rect GetInlineHelpRect(GUIContent content) { + + // well... we do this, because there's no way of knowing the indent and scroll bar existence + // when property height is calculated + var width = UnityInternal.EditorGUIUtility.contextWidth - /*InlineHelpStyle.MarginOuter -*/ SCROLL_WIDTH - LEFT_HELP_INDENT; + + if (content == null) { + return default; + } + + var height = FusionGUIStyles.GetStyle(FusionGUIStyles.GroupBoxType.InlineHelpInner).CalcHeight(content, width); + + return new Rect(InlineHelpStyle.MarginOuter, 0, width, height); + } + + internal static void DrawInlineHelp(GUIContent help, Rect propertyRect, Rect helpRect) { + using (new FusionEditorGUI.EnabledScope(true)) { + if (Event.current.type == EventType.Repaint) { + + int extraPadding = 0; + // main bar + var rect = new Rect() { + xMin = LEFT_HELP_INDENT, + // This assumes that any style used has equal padding on left and right, and determines the padding/margin from the property xMin + xMax = propertyRect.xMax + propertyRect.xMin - SCROLL_WIDTH, + yMin = propertyRect.yMin, + yMax = propertyRect.yMax, + }; + + // extra space that needs to be accounted for, like when there's no scrollbar + extraPadding = Mathf.FloorToInt(Mathf.Max(0, rect.width - (helpRect.width))); + + var orect = new Rect(rect) { + width = helpRect.width + extraPadding, + yMin = rect.yMin - 2, + }; + + FusionGUIStyles.GetStyle(FusionGUIStyles.GroupBoxType.InlineHelpOuter).Draw(orect, false, false, false, false); + + if (helpRect.height > 0) { + Rect irect = new Rect(helpRect) { + x = rect.x, + y = propertyRect.yMax - helpRect.height, + }; + + var innerStyle = FusionGUIStyles.GetStyle(FusionGUIStyles.GroupBoxType.InlineHelpInner); + if (extraPadding > 0.0f) { + innerStyle.padding.right += extraPadding; + irect.width += extraPadding; + } + try { + innerStyle.Draw(irect, help, false, false, false, false); + } finally { + if (extraPadding > 0.0f) { + innerStyle.padding.right -= extraPadding; + } + } + } + } + } + } + + /// + /// For drawing inline help using cached InlineHelpInfo. + /// + internal static GUIContent DrawInlineHelp(this PropertyInlineHelpInfo help, Rect rect, BehaviourEditor editor) { + if (help.Summary != null) { + string path = help.Label.text; + Rect buttonRect = GetInlineHelpButtonRect(InlineHelpButtonPlacement.BeforeLabel, rect, GUIContent.none, false); + bool wasExpanded = IsHelpExpanded(editor, path); + + if (wasExpanded) { + var helpRect = GetInlineHelpRect(help.Summary); + var r = EditorGUILayout.GetControlRect(false, helpRect.height); + r.y = rect.y; + r.height += rect.height; + DrawInlineHelp(help.Summary, r, helpRect); + } + + if (DrawInlineHelpButton(buttonRect, wasExpanded, doButton: true, doIcon: false)) { + SetHelpExpanded(editor, path, !wasExpanded); + } + + DrawInlineHelpButton(buttonRect, wasExpanded, doButton: false, doIcon: true); + } + return help.Label; + } + + /// + /// For drawing inline help manually with custom editors. + /// + internal static GUIContent DrawInlineHelp(this BehaviourEditor editor, Rect rect, Type monoBehaviourType, string memberName) { + var info = GetInlineHelpInfo(monoBehaviourType, memberName); + return DrawInlineHelp(info, rect, editor); + } + + internal static PropertyInlineHelpInfo GetInlineHelpInfo(this Type monoBehaviourType, string memberName ) { + if (!s_codeDocCache.TryGetValue(monoBehaviourType, out var propertyLookup)) { + propertyLookup = new Dictionary(); + s_codeDocCache.Add(monoBehaviourType, propertyLookup); + } + + // Try and see if we have an entry for this property for this target object type yet. + if (propertyLookup.TryGetValue(memberName, out var helpInfo)) { + return helpInfo; + } + + // Failed to find existing record, do the heavy lifting of extracting it from the XMLDocumentation + MemberInfo minfo = monoBehaviourType.GetMemberIncludingBaseTypes(memberName, stopAtType: typeof(Fusion.Behaviour)); + + string fieldSummary, tooltipSummary; + + if (minfo == null) { + fieldSummary = monoBehaviourType.GetXmlDocSummary(false); + tooltipSummary = monoBehaviourType.GetXmlDocSummary(true); + } else { + + tooltipSummary = XmlDocumentation.GetXmlDocSummary(minfo, true); + fieldSummary = string.Join("\n\n", new[] { + minfo.GetXmlDocSummary(false), + minfo.MemberType == MemberTypes.Field ? GetFormattedTypeSummary((minfo as FieldInfo).FieldType) : + minfo.MemberType == MemberTypes.Property ? GetFormattedTypeSummary((minfo as PropertyInfo).PropertyType) : + null + }.Where(x => !string.IsNullOrEmpty(x))); + } + + helpInfo = new PropertyInlineHelpInfo() { + Label = new GUIContent(ObjectNames.NicifyVariableName(memberName), tooltipSummary), + Summary = string.IsNullOrEmpty(fieldSummary) ? null : new GUIContent(fieldSummary), + }; + + propertyLookup.Add(memberName, helpInfo); + return helpInfo; + } + + public static bool InjectPropertyDrawers(SerializedObject serializedObject, bool addScriptDrawer = true, bool fixArrayHelp = true, bool addForAllFields = false) { + + var rootType = serializedObject.targetObject.GetType(); + var anyInjected = false; + + if (addScriptDrawer) { + var sp = serializedObject.FindPropertyOrThrow(ScriptPropertyName); + Debug.Assert(sp.depth == 0 && rootType.IsSubclassOf(typeof(UnityEngine.Object))); + + if (TryInjectDrawer(sp, null, () => new InlineHelpAttribute(), (existingDrawer) => new InlineHelpAttributeDrawer(existingDrawer), out var injected)) { + var helpAttribute = rootType.GetCustomAttributes(true).OfType().SingleOrDefault(); + var scriptDrawer = new ScriptHeaderAttributeDrawer(); + UnityInternal.PropertyDrawer.SetAttribute(scriptDrawer, new ScriptHeaderAttributeDrawer.Attribute() { + Settings = helpAttribute ?? new ScriptHelpAttribute() + }); + + injected.Chain(scriptDrawer); + if (scriptDrawer.attribute.Settings != null) { + ((InlineHelpAttribute)injected.attribute).ButtonPlacement = InlineHelpButtonPlacement.BeforeLabel; + } + } + } + + if (!fixArrayHelp && !addForAllFields) { + return anyInjected; + } + + + bool enterChildren = true; + for (var sp = serializedObject.GetIterator(); sp.NextVisible(enterChildren); enterChildren = sp.isExpanded) { + + bool acceptProperty = false; + + // early out check + if (sp.IsArrayProperty()) { + acceptProperty = fixArrayHelp; + } else if (sp.name == ScriptPropertyName && sp.depth == 0 && rootType.IsSubclassOf(typeof(UnityEngine.Object))) { + continue; + } else { + acceptProperty = addForAllFields; + } + + if (!acceptProperty) { + continue; + } + + var field = UnityInternal.ScriptAttributeUtility.GetFieldInfoFromProperty(sp, out var type); + + // ignore non-marked fields if attribute not added + if (!addForAllFields && field.GetCustomAttribute() == null) { + continue; + } + + if (TryInjectDrawer(sp, field, () => new InlineHelpAttribute(), (existingDrawer) => new InlineHelpAttributeDrawer(existingDrawer), out var drawer)) { + anyInjected = true; + + if (sp.IsArrayProperty()) { + var handler = UnityInternal.ScriptAttributeUtility.GetHandler(sp); + if (handler.decoratorDrawers == null) { + handler.decoratorDrawers = new List(); + } + var decorator = new ReserveArrayPropertyHeightDecorator(); + handler.decoratorDrawers.Add(decorator); + drawer.ArrayHeightDecorator = decorator; + } + + } + } + + return anyInjected; + } + + private static bool TryInjectDrawer(SerializedProperty property, FieldInfo field, Func attributeFactory, Func drawerFactory, out T injectedDrawer) where T: PropertyDrawer { + + injectedDrawer = null; + + var handler = UnityInternal.ScriptAttributeUtility.GetHandler(property); + if (handler.HasPropertyDrawer()) { + // alrady added + return false; + } + + if (handler.Equals(UnityInternal.ScriptAttributeUtility.sharedNullHandler)) { + // need to add one? + handler = UnityInternal.PropertyHandler.New(); + UnityInternal.ScriptAttributeUtility.propertyHandlerCache.SetHandler(property, handler); + } + + + var attribute = attributeFactory(); + + var drawers = handler.PropertyDrawers.ToList(); + int drawerIndex; + for (drawerIndex = 0; drawerIndex < drawers.Count; ++drawerIndex) { + if (drawers[drawerIndex].attribute == null || drawers[drawerIndex].attribute.order < attribute.order) { + break; + } + } + + var drawerToReplace = drawerIndex < drawers.Count ? drawers[drawerIndex] : null; + DecoratingPropertyAttributeDrawerBase drawerToChainWith = null; + + // workaround for pre 2021, but works with earlier versions as well + if (drawerIndex == 1 && drawers.Count == 1) { + if (drawers[0] is DecoratingPropertyAttributeDrawerBase decoratingDrawer) { + drawerToChainWith = decoratingDrawer; + drawerToReplace = null; + } + } + + injectedDrawer = drawerFactory(drawerToReplace); + UnityInternal.PropertyDrawer.SetAttribute(injectedDrawer, attribute); + UnityInternal.PropertyDrawer.SetFieldInfo(injectedDrawer, field); + + if (drawerToChainWith != null) { + drawerToChainWith.Chain(injectedDrawer); + } else { + if (drawerIndex < drawers.Count) { + drawers[drawerIndex] = injectedDrawer; + } else { + drawers.Add(injectedDrawer); + } + handler.PropertyDrawers = drawers; + } + + return true; + } + + internal static bool IsHelpExpanded(object id, string path) { + return s_expandedHelp == (id, path); + } + + internal static void SetHelpExpanded(object id, string path, bool value) { + if (value) { + s_expandedHelp = (id, path); + } else { + s_expandedHelp = default; + } + } + + private static string GetFormattedTypeSummary(Type type) { + var summary = XmlDocumentation.GetXmlDocSummary(type, false); + if (string.IsNullOrEmpty(summary)) { + return summary; + } + return $"[{type.Name}] {summary}"; + } + + internal static class NetworkedPropertyStyle { + public static LazyAuto NetworkPropertyIcon = LazyAuto.Create(() => { + return Resources.Load("icons/networked-property-icon"); + }); + } + + internal static class InlineHelpStyle { + public const float ButtonSize = 16.0f; + public const float MarginOuter = ButtonSize; + public static GUIContent HideInlineContent = new GUIContent("", "Hide"); + public static GUIContent ShowInlineContent = new GUIContent("", ""); + } + + internal static class LazyAuto { + + public static LazyAuto Create(Func valueFactory) { + return new LazyAuto(valueFactory); + } + } + + internal class LazyAuto : Lazy { + + public LazyAuto(Func valueFactory) : base(valueFactory) { + } + + public static implicit operator T(LazyAuto lazy) { + return lazy.Value; + } + } + + // Cached help info + internal class PropertyInlineHelpInfo { + public GUIContent Summary; + public GUIContent Label; + } + + // Draw label with specified XML Help member + static GUIContent reusableGC = new GUIContent(); + public static void DrawLabelForProperty(this BehaviourEditor editor, string membername, string value, Type memberContainerType = null, int? height = null) { + if (memberContainerType == null) { + memberContainerType = editor.serializedObject.GetType(); + } + var rect = height.HasValue ? EditorGUILayout.GetControlRect(false, height.Value) : EditorGUILayout.GetControlRect(); + reusableGC.text = value; + EditorGUI.LabelField(rect, editor.DrawInlineHelp(rect, memberContainerType, membername), reusableGC); + } + + // Draw readonly toggle with specified XML Help member + public static bool DrawToggleForProperty(this BehaviourEditor editor, string membername, bool value, Type memberContainerType = null, int? height = null) { + if (memberContainerType == null) { + memberContainerType = editor.serializedObject.GetType(); + } + var rect = height.HasValue ? EditorGUILayout.GetControlRect(false, height.Value) : EditorGUILayout.GetControlRect(); + return EditorGUI.Toggle(rect, editor.DrawInlineHelp(rect, memberContainerType, membername), value); + } + } +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorGUI.Odin.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEngine; + +#if ODIN_INSPECTOR && !FUSION_ODIN_DISABLED + using Sirenix.Utilities.Editor; + using Sirenix.OdinInspector.Editor; + using Sirenix.Utilities; +#endif + + public static partial class FusionEditorGUI { + + public static T IfOdin(T ifOdin, T ifNotOdin) { +#if ODIN_INSPECTOR && !FUSION_ODIN_DISABLED + return ifOdin; +#else + return ifNotOdin; +#endif + } + + public static bool ForwardPropertyField(Rect position, SerializedProperty property, GUIContent label, bool includeChildren) { +#if ODIN_INSPECTOR && !FUSION_ODIN_DISABLED + + switch (property.propertyType) { + case SerializedPropertyType.ObjectReference: { + EditorGUI.BeginChangeCheck(); + UnityInternal.ScriptAttributeUtility.GetFieldInfoFromProperty(property, out var fieldType); + var value = SirenixEditorFields.UnityObjectField(position, label, property.objectReferenceValue, fieldType ?? typeof(UnityEngine.Object), true); + if (EditorGUI.EndChangeCheck()) { + property.objectReferenceValue = value; + } + return false; + } + + case SerializedPropertyType.Integer: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.IntField(position, label, property.intValue); + if (EditorGUI.EndChangeCheck()) { + property.intValue = value; + } + return false; + } + + case SerializedPropertyType.Float: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.FloatField(position, label, property.floatValue); + if (EditorGUI.EndChangeCheck()) { + property.floatValue = value; + } + return false; + } + + case SerializedPropertyType.Color: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.ColorField(position, label, property.colorValue); + if (EditorGUI.EndChangeCheck()) { + property.colorValue = value; + } + return false; + } + + case SerializedPropertyType.Vector2: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.Vector2Field(position, label, property.vector2Value); + if (EditorGUI.EndChangeCheck()) { + property.vector2Value = value; + } + return false; + } + + case SerializedPropertyType.Vector3: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.Vector3Field(position, label, property.vector3Value); + if (EditorGUI.EndChangeCheck()) { + property.vector3Value = value; + } + return false; + } + + case SerializedPropertyType.Vector4: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.Vector4Field(position, label, property.vector4Value); + if (EditorGUI.EndChangeCheck()) { + property.vector4Value = value; + } + return false; + } + + case SerializedPropertyType.Quaternion: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.RotationField(position, label, property.quaternionValue, GlobalConfig.Instance.QuaternionDrawMode); + if (EditorGUI.EndChangeCheck()) { + property.quaternionValue = value; + } + return false; + } + + case SerializedPropertyType.String: { + EditorGUI.BeginChangeCheck(); + var value = SirenixEditorFields.TextField(position, label, property.stringValue); + if (EditorGUI.EndChangeCheck()) { + property.stringValue = value; + } + return false; + } + + case SerializedPropertyType.Enum: { + UnityInternal.ScriptAttributeUtility.GetFieldInfoFromProperty(property, out var type); + if (type != null && type.IsEnum) { + EditorGUI.BeginChangeCheck(); + bool flags = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0; + Enum value = SirenixEditorFields.EnumDropdown(position, label, (Enum)Enum.ToObject(type, property.intValue)); + if (EditorGUI.EndChangeCheck()) { + property.intValue = Convert.ToInt32(Convert.ChangeType(value, Enum.GetUnderlyingType(type))); + } + return false; + } + + break; + } + + default: + break; + } +#endif + return EditorGUI.PropertyField(position, property, label, includeChildren); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorGUI.Scopes.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEngine; + + public static partial class FusionEditorGUI { + + public sealed class EnabledScope : GUI.Scope { + private readonly bool value; + + public EnabledScope(bool enabled) { + value = GUI.enabled; + GUI.enabled = enabled; + } + + protected override void CloseScope() { + GUI.enabled = value; + } + } + + public sealed class BackgroundColorScope : GUI.Scope { + private readonly Color value; + + public BackgroundColorScope(Color color) { + value = GUI.backgroundColor; + GUI.backgroundColor = color; + } + + protected override void CloseScope() { + GUI.backgroundColor = value; + } + } + + public sealed class ColorScope : GUI.Scope { + private readonly Color value; + + public ColorScope(Color color) { + value = GUI.color; + GUI.color = color; + } + + protected override void CloseScope() { + GUI.color = value; + } + } + + public sealed class ContentColorScope : GUI.Scope { + private readonly Color value; + + public ContentColorScope(Color color) { + value = GUI.contentColor; + GUI.contentColor = color; + } + + protected override void CloseScope() { + GUI.contentColor = value; + } + } + + public sealed class FieldWidthScope : GUI.Scope { + private float value; + + public FieldWidthScope(float fieldWidth) { + value = EditorGUIUtility.fieldWidth; + EditorGUIUtility.fieldWidth = fieldWidth; + } + + protected override void CloseScope() { + EditorGUIUtility.fieldWidth = value; + } + } + + public sealed class HierarchyModeScope : GUI.Scope { + private bool value; + + public HierarchyModeScope(bool value) { + this.value = EditorGUIUtility.hierarchyMode; + EditorGUIUtility.hierarchyMode = value; + } + + protected override void CloseScope() { + EditorGUIUtility.hierarchyMode = value; + } + } + + public sealed class IndentLevelScope : GUI.Scope { + private readonly int value; + + public IndentLevelScope(int indentLevel) { + value = EditorGUI.indentLevel; + EditorGUI.indentLevel = indentLevel; + } + + protected override void CloseScope() { + EditorGUI.indentLevel = value; + } + } + + public sealed class LabelWidthScope : GUI.Scope { + private float value; + + public LabelWidthScope(float labelWidth) { + value = EditorGUIUtility.labelWidth; + EditorGUIUtility.labelWidth = labelWidth; + } + + protected override void CloseScope() { + EditorGUIUtility.labelWidth = value; + } + } + + public sealed class ShowMixedValueScope : GUI.Scope { + private bool value; + + public ShowMixedValueScope(bool show) { + value = EditorGUI.showMixedValue; + EditorGUI.showMixedValue = show; + } + + protected override void CloseScope() { + EditorGUI.showMixedValue = value; + } + } + + public sealed class PropertyScope : GUI.Scope { + + public PropertyScope(Rect position, GUIContent label, SerializedProperty property) { + EditorGUI.BeginProperty(position, label, property); + } + + protected override void CloseScope() { + EditorGUI.EndProperty(); + } + } + + public sealed class PropertyScopeWithPrefixLabel : GUI.Scope { + private int indent; + + public PropertyScopeWithPrefixLabel(Rect position, GUIContent label, SerializedProperty property, out Rect indentedPosition) { + EditorGUI.BeginProperty(position, label, property); + indentedPosition = EditorGUI.PrefixLabel(position, label); + indent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + } + + protected override void CloseScope() { + EditorGUI.indentLevel = indent; + EditorGUI.EndProperty(); + } + } + + public static bool BeginBox(string headline = null, int indentLevel = 1, bool? foldout = null) { + bool result = true; + GUILayout.BeginVertical(EditorStyles.helpBox); + if (!string.IsNullOrEmpty(headline)) { + if (foldout.HasValue) { + result = EditorGUILayout.Foldout(foldout.Value, headline); + } else { + EditorGUILayout.LabelField(headline, EditorStyles.boldLabel); + } + } + EditorGUI.indentLevel += indentLevel; + return result; + } + + public static void EndBox(int indentLevel = 1) { + EditorGUI.indentLevel -= indentLevel; + GUILayout.EndVertical(); + } + + public sealed class BoxScope : IDisposable { + private readonly SerializedObject _serializedObject; + private readonly Color _backgroundColor; + private readonly int _indentLevel; + + public BoxScope(string headline = null, SerializedObject serializedObject = null, int indentLevel = 1, bool? foldout = null) { + _indentLevel = indentLevel; + _serializedObject = serializedObject; + +#if !UNITY_2019_3_OR_NEWER + _backgroundColor = GUI.backgroundColor; + if (EditorGUIUtility.isProSkin) { + GUI.backgroundColor = Color.grey; + } +#endif + + IsFoldout = BeginBox(headline: headline, indentLevel: indentLevel, foldout: foldout); + + if (_serializedObject != null) { + EditorGUI.BeginChangeCheck(); + } + } + + public bool IsFoldout { get; private set; } + + public void Dispose() { + if (_serializedObject != null && EditorGUI.EndChangeCheck()) { + _serializedObject.ApplyModifiedProperties(); + } + + EndBox(indentLevel: _indentLevel); + +#if !UNITY_2019_3_OR_NEWER + GUI.backgroundColor = _backgroundColor; +#endif + } + } + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorGUI.Utils.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using UnityEditor; + using UnityEngine; + + public static partial class FusionEditorGUI { + + public static readonly GUIContent WhitespaceContent = new GUIContent(" "); + + public const string ScriptPropertyName = "m_Script"; + + private const int IconHeight = 14; + + public static Color PrefebOverridenColor => new Color(1f / 255f, 153f / 255f, 235f / 255f, 0.75f); + + public static Rect Decorate(Rect rect, string tooltip, MessageType messageType, bool hasLabel = false, bool drawBorder = true, bool drawButton = true) { + + if (hasLabel) { + rect.xMin += EditorGUIUtility.labelWidth; + } + + var content = EditorGUIUtility.TrTextContentWithIcon(string.Empty, tooltip, messageType); + var iconRect = rect; + iconRect.width = Mathf.Min(16, rect.width); + iconRect.xMin -= iconRect.width; + + iconRect.y += (iconRect.height - IconHeight) / 2; + iconRect.height = IconHeight; + + if (drawButton) { + using (GUI.enabled ? null : new FusionEditorGUI.EnabledScope(true)) { + GUI.Label(iconRect, content, GUIStyle.none); + } + } + + //GUI.Label(iconRect, content, new GUIStyle()); + + if (drawBorder) { + Color borderColor; + switch (messageType) { + case MessageType.Warning: + borderColor = new Color(1.0f, 0.5f, 0.0f); + break; + case MessageType.Error: + borderColor = new Color(1.0f, 0.0f, 0.0f); + break; + default: + borderColor = Color.white; + break; + } + GUI.DrawTexture(rect, Texture2D.whiteTexture, ScaleMode.StretchToFill, false, 0, borderColor, 1.0f, 1.0f); + } + + return iconRect; + } + + public static void ScriptPropertyField(SerializedObject obj) { + var scriptProperty = obj.FindProperty(ScriptPropertyName); + if (scriptProperty != null) { + using (new EditorGUI.DisabledScope(true)) { + EditorGUILayout.PropertyField(scriptProperty); + } + } + } + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/FusionEditorLog.cs + +namespace Fusion.Editor { + using System; + using UnityEngine; + using ConditionalAttribute = System.Diagnostics.ConditionalAttribute; + + public static class FusionEditorLog { + + const string LogPrefix = "[Fusion/Editor]"; + const string ImportPrefix = "[Fusion/Import]"; + const string ConfigPrefix = "[Fusion/Config]"; + const string InspectorPrefix = "[Fusion/Inspector]"; + + [Conditional("FUSION_EDITOR_TRACE")] + public static void Trace(string msg) { + Log(msg); + } + + public static void Log(string msg) { + Debug.Log($"{LogPrefix} {msg}"); + } + + + [Conditional("FUSION_EDITOR_TRACE")] + public static void TraceConfig(string msg) { + LogConfig(msg); + } + + public static void WarnConfig(string msg) { + Debug.LogWarning($"{ConfigPrefix} {msg}"); + } + + public static void LogConfig(string msg) { + Debug.Log($"{ConfigPrefix} {msg}"); + } + + [Conditional("FUSION_EDITOR_TRACE")] + public static void TraceImport(string assetPath, string msg) { + Debug.Log($"{ImportPrefix} {assetPath}: {msg}"); + } + + [Conditional("FUSION_EDITOR_TRACE")] + public static void TraceImport(string msg) { + Debug.Log($"{ImportPrefix} {msg}"); + } + + public static void ErrorImport(string msg) { + Debug.LogError($"{ImportPrefix} {msg}"); + } + + public static void ErrorImport(string assetPath, string msg) { + Debug.LogError($"{ImportPrefix} {assetPath}: {msg}"); + } + + + internal static void WarnImport(string msg) { + Debug.LogWarning($"{ImportPrefix} {msg}"); + } + + [Conditional("FUSION_EDITOR_TRACE")] + public static void TraceInspector(string msg) { + Debug.Log($"{InspectorPrefix} {msg}"); + } + + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/LogSlider.cs + +namespace Fusion.Editor { + using System; + using UnityEditor; + using UnityEngine; + + public class CustomSliders { + + public static float Log10Slider(Rect r, float value, GUIContent label, float min, float max, float zero, int places, float extraSpace = 0) { + + const int VAL_WIDTH = 58; + const int SPACER = 4; + const float MIN_SLIDER_WIDTH = 64; + + float logmin = (float)Math.Log10(min); + float logmax = (float)Math.Log10(max); + float logzro = (float)Math.Log10(zero); + float logval = (float)Math.Log10(value < zero ? min : value); + + float logres; + Rect sliderect; + float labelWidth; + + bool showSlider = false; + + if (label == null) { + labelWidth = 0; + sliderect = new Rect(r) { xMin = r.xMin + extraSpace, xMax = r.xMax - VAL_WIDTH - SPACER }; + showSlider = sliderect.width > MIN_SLIDER_WIDTH; + // convert to log10 linear just for slider. Then convert result back. + logres = showSlider ? GUI.HorizontalSlider(sliderect, logval, logmax, logmin) : logval; + } + else { + labelWidth = EditorGUIUtility.labelWidth; + Rect labelrect = new Rect(r) { width = labelWidth }; + sliderect = new Rect(r) { xMin = r.xMin + SPACER + EditorGUIUtility.labelWidth + extraSpace, xMax = r.xMax - VAL_WIDTH }; + showSlider = sliderect.width > MIN_SLIDER_WIDTH; + + GUI.Label(labelrect, label); + // convert to log10 linear just for slider. Then convert result back. + logres = showSlider ? GUI.HorizontalSlider(sliderect, logval, logmax, logmin) : logval; + } + + // If slider moved, return the new value. + if (showSlider && logres != logval) { + + float newval = (float)Math.Pow(10, logres); + + if (newval < zero) { + return 0; + } + + float rounded = RoundAndClamp(newval, min, max, places); + return rounded; + } + + float fieldXMin = showSlider ? r.xMax /*+ SPACER */+ SPACER - VAL_WIDTH : r.xMin + labelWidth + /*SPACER +*/ extraSpace; + Rect valuerect = new Rect(r) { xMin = fieldXMin }; + var holdindent = EditorGUI.indentLevel; + EditorGUI.indentLevel = 0; + var val = EditorGUI.FloatField(valuerect, value < zero ? 0 : value); + EditorGUI.indentLevel = holdindent; + return val; + } + + public static float RoundAndClamp(float val, float min, float max, int places) { + // If places were not defined to increment by, use Log to round slider movements to nearest single value. + float rounded; + + places = (int)Math.Log10(val) - places; + if (places > 0) + places = 0; + else if (places < -15) + places = -15; + rounded = (float)Math.Round(val, -places); + + // Then clamp to ensure slider end values are perfect. + if (rounded > max) + rounded = max; + else if (rounded < min) + rounded = min; + + return rounded; + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/NetworkProjectConfigUtilities.cs + +namespace Fusion.Editor { + + using UnityEditor; + using UnityEngine; + using UnityEngine.SceneManagement; + using System.Collections.Generic; + using Fusion.Photon.Realtime; + using System.Linq; + using System.IO; + using System; + + /// + /// Unity handling for post asset processing callback. Checks existence of settings assets every time assets change. + /// + class FusionSettingsPostProcessor : AssetPostprocessor { + private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + NetworkProjectConfigUtilities.RetryEnsurePhotonAppSettingsExists(); + } + } + + /// + /// Editor utilities for creating and managing the singleton. + /// + [InitializeOnLoad] + public static class NetworkProjectConfigUtilities { + + public const string CONFIG_RESOURCE_FOLDER_GUID = "65cf5f43e8c20f941b0bb130b392ec89"; + public const string FALLBACK_CONFIG_FOLDER_PATH = "Assets/Photon/Fusion/Resources"; + + // Constructor runs on project load, allows for startup check for existence of NPC asset. + static NetworkProjectConfigUtilities() { + EnsureAssetExists(); + EditorApplication.playModeStateChanged += (change) => { + if (change == PlayModeStateChange.EnteredEditMode) { + NetworkProjectConfig.UnloadGlobal(); + } + }; + } + + /// + /// Attempts enforce existence of singleton. If Editor is not ready, this method will be deferred one editor update and try again until it succeeds. + /// + [UnityEditor.Callbacks.DidReloadScripts] + private static void EnsureAssetExists() { + RetryEnsureProjectConfigConverted(); + RetryEnsurePhotonAppSettingsExists(); + } + + internal static void RetryEnsureProjectConfigConverted() { + // Keep deferring this check until Unity is ready to deal with asset find/create. + if (EditorApplication.isCompiling || EditorApplication.isUpdating) { + EditorApplication.delayCall += RetryEnsureProjectConfigConverted; + return; + } + EditorApplication.delayCall += EnsureProjectConfigConverted; + } + + internal static void RetryEnsurePhotonAppSettingsExists() { + PhotonAppSettings photonSettings = PhotonAppSettings.Instance; + + if (photonSettings) + return; + + // Keep deferring this check until Unity is ready to deal with asset find/create. + if (EditorApplication.isCompiling || EditorApplication.isUpdating) { + EditorApplication.delayCall += RetryEnsurePhotonAppSettingsExists; + return; + } + EditorApplication.delayCall += EnsurePhotonAppSettingsAssetExists; + } + + static void EnsurePhotonAppSettingsAssetExists() { + GetOrCreatePhotonAppSettingsAsset(); + } + + static void EnsureProjectConfigConverted() { + + var legacyConfigs = AssetDatabase.FindAssets($"t:{nameof(NetworkProjectConfigAsset)}") + .Select(AssetDatabase.GUIDToAssetPath) + .Where(x => Path.GetExtension(x) == ".asset"); + + foreach (var legacyConfigPath in legacyConfigs) { + var legacyConfig = AssetDatabase.LoadAssetAtPath(legacyConfigPath); + try { + var importer = NetworkProjectConfigAssetEditor.Convert(legacyConfig); + Debug.Log($"Converted legacy Fusion config {legacyConfigPath} to {importer.assetPath}"); + } catch (Exception ex) { + Debug.LogError($"Failed to convert legacy Fusion config {legacyConfigPath}: {ex}"); + } + } + } + + static string EnsureConfigFolderExists() { + + string folder = null; + var markerResource = AssetDatabase.GUIDToAssetPath(CONFIG_RESOURCE_FOLDER_GUID); + if (markerResource != null && markerResource != "") { + folder = System.IO.Path.GetDirectoryName(markerResource); + if (folder != null && folder != "") { + return folder; + } + } + + // Unity requires folders to be built one folder at a time, since parent folder must first exist. + folder = FALLBACK_CONFIG_FOLDER_PATH; + if (AssetDatabase.IsValidFolder(folder)) + return folder; + + string parent = ""; + string[] split = folder.Split('\\', '/'); + foreach(var f in split) { + // First folder split should be "Assets", which always exists. + if (f == "Assets" && parent == "") { + parent = "Assets"; + continue; + } + if (AssetDatabase.IsValidFolder(parent + "/" + f) == false) { + AssetDatabase.CreateFolder(parent, f); + } + parent = parent + '/' + f; + } + return folder; + } + + /// + /// Gets the singleton. If none was found, attempts to create one. + /// + /// + public static PhotonAppSettings GetOrCreatePhotonAppSettingsAsset() { + PhotonAppSettings photonConfig; + + photonConfig = PhotonAppSettings.Instance; + + if (photonConfig != null) + return photonConfig; + + // If trying to get instance returned null - create a new asset. + Debug.Log($"{nameof(PhotonAppSettings)} not found. Creating one now."); + + + photonConfig = ScriptableObject.CreateInstance(); + string folder = EnsureConfigFolderExists(); + AssetDatabase.CreateAsset(photonConfig, folder + "/" + PhotonAppSettings.ExpectedAssetName); + PhotonAppSettings.Instance = photonConfig; + EditorUtility.SetDirty(photonConfig); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + +#if FUSION_WEAVER + // QOL: Open the Fusion Hub window, as there will be no App Id yet. + if (photonConfig.AppSettings.AppIdFusion == null) + FusionHubWindow.Open(); +#endif + + return PhotonAppSettings.Instance; + } + + + + [MenuItem("Fusion/Network Project Config", priority = 200)] + static void PingNetworkProjectConfigAsset() { + NetworkProjectConfigUtilities.PingGlobalConfigAsset(true); + } + + + [MenuItem("Fusion/Rebuild Object Table", priority = 100)] + public static void RebuildObjectTable() { + foreach (var prefab in AssetDatabase.FindAssets($"t:prefab") + .Select(AssetDatabase.GUIDToAssetPath) + .Select(x => (GameObject)AssetDatabase.LoadMainAssetAtPath(x))) { + if (prefab.TryGetComponent(out var networkObject) && !networkObject.Flags.IsIgnored()) { + AssetDatabaseUtils.SetLabel(prefab, NetworkProjectConfigImporter.FusionPrefabTag, true); + } else { + AssetDatabaseUtils.SetLabel(prefab, NetworkProjectConfigImporter.FusionPrefabTag, false); + } + + } + + AssetDatabase.Refresh(); + SaveGlobalConfig(); + } + + public static string SaveGlobalConfig() { + return SaveGlobalConfig(NetworkProjectConfig.Global ?? new NetworkProjectConfig()); + } + + public static string SaveGlobalConfig(NetworkProjectConfig config) { + if (config == null) { + throw new ArgumentNullException(nameof(config)); + } + + var json = EditorJsonUtility.ToJson(config, true); + + string path = GetGlobalConfigPath(); + string existingJson = File.ReadAllText(path); + + if (!string.Equals(json, existingJson)) { + AssetDatabase.MakeEditable(path); + File.WriteAllText(path, json); + } + + AssetDatabase.ImportAsset(path); + return PathUtils.MakeSane(path); + } + + public static void PingGlobalConfigAsset(bool select = false) { + var config = AssetDatabase.LoadAssetAtPath(GetGlobalConfigPath()); + if (config != null) { + EditorGUIUtility.PingObject(config); + if (select) { + Selection.activeObject = config; + } + } + } + + public static NetworkProjectConfigImporter GlobalConfigImporter { + get { + return (NetworkProjectConfigImporter)AssetImporter.GetAtPath(GetGlobalConfigPath()); + } + } + + public static bool TryGetPrefabAsset(NetworkObjectGuid guid, out NetworkPrefabAsset prefabAsset) { + prefabAsset = AssetDatabase.LoadAllAssetsAtPath(GetGlobalConfigPath()) + .OfType() + .FirstOrDefault(x => x.AssetGuid == guid); + return prefabAsset; + } + + public static bool TryGetPrefabSource(NetworkObjectGuid guid, out T source) where T : class, INetworkPrefabSource { + if (NetworkProjectConfig.Global.PrefabTable.TryGetPrefabEntry(guid, out var iprefab) && iprefab is T asset) { + source = asset; + return true; + } + source = null; + return false; + } + + public static bool TryResolvePrefab(NetworkObjectGuid guid, out NetworkObject prefab) { + if (TryGetPrefabSource(guid, out NetworkPrefabSourceUnityBase source)) { + try { + prefab = NetworkPrefabSourceFactory.ResolveOrThrow(source); + return true; + } catch (Exception ex) { + FusionEditorLog.Trace(ex.ToString()); + } + } + + prefab = null; + return false; + } + + internal static string GetGlobalConfigPath(bool createIfMissing = true) { + var candidates = AssetDatabase.FindAssets($"glob:\"*{NetworkProjectConfigImporter.Extension}\"") + .Select(AssetDatabase.GUIDToAssetPath) + .ToArray(); + + if (candidates.Length == 0) { + // try with a regular file api, as maybe the file has not been imported yet + candidates = Directory.GetFiles("Assets/", $"*{NetworkProjectConfigImporter.Extension}", SearchOption.AllDirectories); + + if (candidates.Length > 0) { + FusionEditorLog.WarnConfig($"AssetDatabase did not find any config, but a raw glob found these:\n{string.Join("\n", candidates)}"); + for (int i = 0; i < candidates.Length; ++i) { + candidates[i] = PathUtils.MakeSane(candidates[i]); + } + } + } + + if (candidates.Length == 0) { + if (createIfMissing) { + + var defaultPath = EnsureConfigFolderExists() + "/" + NetworkProjectConfig.DefaultResourceName + NetworkProjectConfigImporter.Extension; + + if (AssetDatabase.IsAssetImportWorkerProcess()) { + FusionEditorLog.WarnConfig($"Creating a new config at {defaultPath}, but an import is already taking place. " + + $"AssetDatabase will \"see\" the config after the current import is over."); + } else { + FusionEditorLog.LogConfig($"Creating new config at {defaultPath}"); + } + + var json = EditorJsonUtility.ToJson(CreateDefaultConfig()); + File.WriteAllText(defaultPath, json); + AssetDatabase.ImportAsset(defaultPath); + + return defaultPath; + } else { + return string.Empty; + } + } + if (candidates.Length > 1) { + FusionEditorLog.WarnConfig($"There are multiple configs, choosing the first one: {(string.Join("\n", candidates))}"); + } + return candidates[0]; + } + + // invoked by reflection, don't remove + private static NetworkProjectConfigAsset EditTimeLoadGlobalConfigWrapper() { + var path = GetGlobalConfigPath(); + + FusionEditorLog.TraceConfig($"Loading Global config from {path}"); + + var config = NetworkProjectConfigImporter.LoadConfigFromFile(path); + var wrapper = AssetDatabase.LoadAssetAtPath(path); + + if (!wrapper) { + // well, try reimporting? + FusionEditorLog.TraceConfig($"Failed to load config at first attempt, reimporting and trying again."); + AssetDatabase.ImportAsset(path); + wrapper = AssetDatabase.LoadAssetAtPath(path); + } + + if (!wrapper) { + if (AssetDatabase.IsAssetImportWorkerProcess()) { + FusionEditorLog.WarnConfig($"Config created/dirty during import, this is not supported"); + } else { + FusionEditorLog.WarnConfig($"Failed to load config with regular AssetDatabase, " + + $"prefab assets disabled until next reimport."); + } + + wrapper = ScriptableObject.CreateInstance(); + } + + // overwrite imported config with raw one, in case there's an import lagging behind + wrapper.Config = config; + return wrapper; + } + + private static NetworkProjectConfig CreateDefaultConfig() { + return new NetworkProjectConfig() { + }; + } + + private static string[] GetEnabledBuildScenes() { + var scenes = new List(); + + for (int i = 0; i < EditorBuildSettings.scenes.Length; ++i) { + var scene = EditorBuildSettings.scenes[i]; + if (scene.enabled && string.IsNullOrEmpty(scene.path) == false) { + scenes.Add(scene.path); + } + } + + return scenes.ToArray(); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/NetworkRunnerUtilities.cs + +namespace Fusion.Editor { + + using System.Collections.Generic; + using UnityEngine; + using UnityEditor; + using Fusion; + + public static class NetworkRunnerUtilities { + + [InitializeOnLoadMethod] + static void ListenToPlaymodeChanges() { + EditorApplication.playModeStateChanged += mode => { + if (mode == PlayModeStateChange.ExitingPlayMode) { + foreach (var instance in NetworkRunner.Instances) { + instance.NotifyEditorPlayModeExit(); + } + } + }; + } + + static List reusableRunnerList = new List(); + + public static NetworkRunner[] FindActiveRunners() { + var runners = Object.FindObjectsOfType(); + reusableRunnerList.Clear(); + for (int i = 0; i < runners.Length; ++i) { + if (runners[i].IsRunning) + reusableRunnerList.Add(runners[i]); + } + if (reusableRunnerList.Count == runners.Length) + return runners; + + return reusableRunnerList.ToArray(); + } + + public static void FindActiveRunners(List nonalloc) { + var runners = Object.FindObjectsOfType(); + nonalloc.Clear(); + for (int i = 0; i < runners.Length; ++i) { + if (runners[i].IsRunning) + nonalloc.Add(runners[i]); + } + } + + } +} + + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/PathUtils.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + public static class PathUtils { + public static bool MakeRelativeToFolder(String path, String folder, out String result) { + result = String.Empty; + var formattedPath = MakeSane(path); + if (formattedPath.Equals(folder, StringComparison.Ordinal) || + formattedPath.EndsWith("/" + folder)) { + return true; + } + var index = formattedPath.IndexOf(folder + "/", StringComparison.Ordinal); + var size = folder.Length + 1; + if (index >= 0 && formattedPath.Length >= size) { + result = formattedPath.Substring(index + size, formattedPath.Length - index - size); + return true; + } + return false; + } + + public static string MakeSane(String path) { + return path.Replace("\\", "/").Replace("//", "/").TrimEnd('\\', '/').TrimStart('\\', '/'); + } + + public static string GetPathWithoutExtension(string path) { + if (path == null) + return (string)null; + int length; + if ((length = path.LastIndexOf('.')) == -1) + return path; + return path.Substring(0, length); + } + + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/ReflectionUtils.cs + +namespace Fusion.Editor { + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + + public static class ReflectionUtils { + public const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; + + public static Type GetUnityLeafType(this Type type) { + if (type.HasElementType) { + type = type.GetElementType(); + } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { + type = type.GetGenericArguments()[0]; + } + return type; + } + +#if UNITY_EDITOR + + public static T CreateEditorMethodDelegate(string editorAssemblyTypeName, string methodName, BindingFlags flags = DefaultBindingFlags) where T : Delegate { + return CreateMethodDelegate(typeof(UnityEditor.Editor).Assembly, editorAssemblyTypeName, methodName, flags); + } + + public static Delegate CreateEditorMethodDelegate(string editorAssemblyTypeName, string methodName, BindingFlags flags, Type delegateType) { + return CreateMethodDelegate(typeof(UnityEditor.Editor).Assembly, editorAssemblyTypeName, methodName, flags, delegateType); + } + +#endif + + public static T CreateMethodDelegate(this Type type, string methodName, BindingFlags flags = DefaultBindingFlags) where T : Delegate { + try { + return CreateMethodDelegateInternal(type, methodName, flags); + } catch (System.Exception ex) { + throw new InvalidOperationException(CreateMethodExceptionMessage(type.Assembly, type.FullName, methodName, flags), ex); + } + } + + public static Delegate CreateMethodDelegate(this Type type, string methodName, BindingFlags flags, Type delegateType) { + try { + return CreateMethodDelegateInternal(type, methodName, flags, delegateType); + } catch (System.Exception ex) { + throw new InvalidOperationException(CreateMethodExceptionMessage(type.Assembly, type.FullName, methodName, flags, delegateType), ex); + } + } + + public static T CreateMethodDelegate(Assembly assembly, string typeName, string methodName, BindingFlags flags = DefaultBindingFlags) where T : Delegate { + try { + var type = assembly.GetType(typeName, true); + return CreateMethodDelegateInternal(type, methodName, flags); + } catch (System.Exception ex) { + throw new InvalidOperationException(CreateMethodExceptionMessage(assembly, typeName, methodName, flags), ex); + } + } + + public static Delegate CreateMethodDelegate(Assembly assembly, string typeName, string methodName, BindingFlags flags, Type delegateType) { + try { + var type = assembly.GetType(typeName, true); + return CreateMethodDelegateInternal(type, methodName, flags, delegateType); + } catch (System.Exception ex) { + throw new InvalidOperationException(CreateMethodExceptionMessage(assembly, typeName, methodName, flags, delegateType), ex); + } + } + + public static T CreateMethodDelegate(this Type type, string methodName, BindingFlags flags, Type delegateType, params DelegateSwizzle[] fallbackSwizzles) where T : Delegate { + try { + MethodInfo method = GetMethodOrThrow(type, methodName, flags, delegateType, fallbackSwizzles, out var swizzle); + + var delegateParameters = typeof(T).GetMethod("Invoke").GetParameters(); + var parameters = new List(); + + for (int i = 0; i < delegateParameters.Length; ++i) { + parameters.Add(Expression.Parameter(delegateParameters[i].ParameterType, $"param_{i}")); + } + + var convertedParameters = new List(); + { + var methodParameters = method.GetParameters(); + if (swizzle == null) { + for (int i = 0, j = method.IsStatic ? 0 : 1; i < methodParameters.Length; ++i, ++j) { + convertedParameters.Add(Expression.Convert(parameters[j], methodParameters[i].ParameterType)); + } + } else { + var swizzledParameters = swizzle.Swizzle(parameters.ToArray()); + for (int i = 0, j = method.IsStatic ? 0 : 1; i < methodParameters.Length; ++i, ++j) { + convertedParameters.Add(Expression.Convert(swizzledParameters[j], methodParameters[i].ParameterType)); + } + } + } + + MethodCallExpression callExpression; + if (method.IsStatic) { + callExpression = Expression.Call(method, convertedParameters); + } else { + var instance = Expression.Convert(parameters[0], method.DeclaringType); + callExpression = Expression.Call(instance, method, convertedParameters); + } + + var l = Expression.Lambda(typeof(T), callExpression, parameters); + var del = l.Compile(); + return (T)del; + } catch (Exception ex) { + throw new InvalidOperationException(CreateMethodExceptionMessage(type.Assembly, type.FullName, methodName, flags), ex); + } + } + + public static T CreateConstructorDelegate(this Type type, BindingFlags flags, Type delegateType, params DelegateSwizzle[] fallbackSwizzles) where T : Delegate { + try { + var constructor = GetConstructorOrThrow(type, flags, delegateType, fallbackSwizzles, out var swizzle); + + var delegateParameters = typeof(T).GetMethod("Invoke").GetParameters(); + var parameters = new List(); + + for (int i = 0; i < delegateParameters.Length; ++i) { + parameters.Add(Expression.Parameter(delegateParameters[i].ParameterType, $"param_{i}")); + } + + var convertedParameters = new List(); + { + var constructorParameters = constructor.GetParameters(); + if (swizzle == null) { + for (int i = 0, j = 0; i < constructorParameters.Length; ++i, ++j) { + convertedParameters.Add(Expression.Convert(parameters[j], constructorParameters[i].ParameterType)); + } + } else { + var swizzledParameters = swizzle.Swizzle(parameters.ToArray()); + for (int i = 0, j = 0; i < constructorParameters.Length; ++i, ++j) { + convertedParameters.Add(Expression.Convert(swizzledParameters[j], constructorParameters[i].ParameterType)); + } + } + } + + NewExpression newExpression = Expression.New(constructor, convertedParameters); + var l = Expression.Lambda(typeof(T), newExpression, parameters); + var del = l.Compile(); + return (T)del; + } catch (Exception ex) { + throw new InvalidOperationException(CreateConstructorExceptionMessage(type.Assembly, type.FullName, flags), ex); + } + } + + /// + /// Returns the first found member of the given name. Includes private members. + /// + public static MemberInfo GetMemberIncludingBaseTypes(this Type type, string memberName, BindingFlags flags = DefaultBindingFlags, Type stopAtType = null) { + var members = type.GetMember(memberName, flags); + if (members.Length > 0) + return members[0]; + + type = type.BaseType; + + // loop as long as we have a parent class to search. + while (type != null) { + + // No point recursing into the abstracts. + if (type == stopAtType) + break; + + members = type.GetMember(memberName, flags); + if (members.Length > 0) + return members[0]; + + type = type.BaseType; + } + return null; + } + + /// + /// Normal reflection GetField() won't find private fields in parents (only will find protected). So this recurses the hard to find privates. + /// This is needed since Unity serialization does find inherited privates. + /// + public static FieldInfo GetFieldIncludingBaseTypes(this Type type, string fieldName, BindingFlags flags = DefaultBindingFlags, Type stopAtType = null) { + var field = type.GetField(fieldName, flags); + if (field != null) + return field; + + type = type.BaseType; + + // loop as long as we have a parent class to search. + while (type != null) { + + // No point recursing into the abstracts. + if (type == stopAtType) + break; + + field = type.GetField(fieldName, flags); + if (field != null) + return field; + + type = type.BaseType; + } + return null; + } + + public static FieldInfo GetFieldOrThrow(this Type type, string fieldName, BindingFlags flags = DefaultBindingFlags) { + var field = type.GetField(fieldName, flags); + if (field == null) { + throw new ArgumentOutOfRangeException(nameof(fieldName), CreateFieldExceptionMessage(type.Assembly, type.FullName, fieldName, flags)); + } + return field; + } + + public static FieldInfo GetFieldOrThrow(this Type type, string fieldName, BindingFlags flags = DefaultBindingFlags) { + return GetFieldOrThrow(type, fieldName, typeof(T), flags); + } + + public static FieldInfo GetFieldOrThrow(this Type type, string fieldName, Type fieldType, BindingFlags flags = DefaultBindingFlags) { + var field = type.GetField(fieldName, flags); + if (field == null) { + throw new ArgumentOutOfRangeException(nameof(fieldName), CreateFieldExceptionMessage(type.Assembly, type.FullName, fieldName, flags)); + } + if (field.FieldType != fieldType) { + throw new InvalidProgramException($"Field {type.FullName}.{fieldName} is of type {field.FieldType}, not expected {fieldType}"); + } + return field; + } + + public static PropertyInfo GetPropertyOrThrow(this Type type, string propertyName, BindingFlags flags = DefaultBindingFlags) { + return GetPropertyOrThrow(type, propertyName, typeof(T), flags); + } + + public static PropertyInfo GetPropertyOrThrow(this Type type, string propertyName, Type propertyType, BindingFlags flags = DefaultBindingFlags) { + var property = type.GetProperty(propertyName, flags); + if (property == null) { + throw new ArgumentOutOfRangeException(nameof(propertyName), CreateFieldExceptionMessage(type.Assembly, type.FullName, propertyName, flags)); + } + if (property.PropertyType != propertyType) { + throw new InvalidProgramException($"Property {type.FullName}.{propertyName} is of type {property.PropertyType}, not expected {propertyType}"); + } + return property; + } + + public static ConstructorInfo GetConstructorInfoOrThrow(this Type type, Type[] types, BindingFlags flags = DefaultBindingFlags) { + var constructor = type.GetConstructor(flags, null, types, null); + if (constructor == null) { + throw new ArgumentOutOfRangeException(nameof(types), CreateConstructorExceptionMessage(type.Assembly, type.FullName, types, flags)); + } + return constructor; + } + + public static Type GetNestedTypeOrThrow(this Type type, string name, BindingFlags flags) { + var result = type.GetNestedType(name, flags); + if (result == null) { + throw new ArgumentOutOfRangeException(nameof(name), CreateFieldExceptionMessage(type.Assembly, type.FullName, name, flags)); + } + return result; + } + + public static InstanceAccessor CreateFieldAccessor(this Type type, string fieldName, Type expectedFieldType = null, BindingFlags flags = DefaultBindingFlags) { + var field = type.GetFieldOrThrow(fieldName, expectedFieldType ?? typeof(FieldType), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return CreateAccessorInternal(field); + } + + public static StaticAccessor CreateStaticFieldAccessor(this Type type, string fieldName, Type expectedFieldType = null) { + return CreateStaticFieldAccessor(type, fieldName, expectedFieldType); + } + + public static StaticAccessor CreateStaticFieldAccessor(this Type type, string fieldName, Type expectedFieldType = null) { + var field = type.GetFieldOrThrow(fieldName, expectedFieldType ?? typeof(FieldType), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + return CreateStaticAccessorInternal(field); + } + + public static InstanceAccessor CreatePropertyAccessor(this Type type, string fieldName, Type expectedPropertyType = null, BindingFlags flags = DefaultBindingFlags) { + var field = type.GetPropertyOrThrow(fieldName, expectedPropertyType ?? typeof(PropertyType), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + return CreateAccessorInternal(field); + } + + public static StaticAccessor CreateStaticPropertyAccessor(this Type type, string fieldName, Type expectedFieldType = null) { + return CreateStaticPropertyAccessor(type, fieldName, expectedFieldType); + } + + public static StaticAccessor CreateStaticPropertyAccessor(this Type type, string fieldName, Type expectedFieldType = null) { + var field = type.GetPropertyOrThrow(fieldName, expectedFieldType ?? typeof(FieldType), BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + return CreateStaticAccessorInternal(field); + } + + private static string CreateMethodExceptionMessage(Assembly assembly, string typeName, string methodName, BindingFlags flags) { + return CreateMethodExceptionMessage(assembly, typeName, methodName, flags, typeof(T)); + } + + private static string CreateMethodExceptionMessage(Assembly assembly, string typeName, string methodName, BindingFlags flags, Type delegateType) { + return $"{assembly.FullName}.{typeName}.{methodName} with flags: {flags} and type: {delegateType}"; + } + + private static string CreateFieldExceptionMessage(Assembly assembly, string typeName, string fieldName, BindingFlags flags) { + return $"{assembly.FullName}.{typeName}.{fieldName} with flags: {flags}"; + } + + private static string CreateConstructorExceptionMessage(Assembly assembly, string typeName, BindingFlags flags) { + return $"{assembly.FullName}.{typeName}() with flags: {flags}"; + } + + private static string CreateConstructorExceptionMessage(Assembly assembly, string typeName, Type[] types, BindingFlags flags) { + return $"{assembly.FullName}.{typeName}({(string.Join(", ", types.Select(x => x.FullName)))}) with flags: {flags}"; + } + + private static T CreateMethodDelegateInternal(this Type type, string name, BindingFlags flags) where T : Delegate { + return (T)CreateMethodDelegateInternal(type, name, flags, typeof(T)); + } + + private static Delegate CreateMethodDelegateInternal(this Type type, string name, BindingFlags flags, Type delegateType) { + MethodInfo method = GetMethodOrThrow(type, name, flags, delegateType); + return System.Delegate.CreateDelegate(delegateType, null, method); + } + + private static MethodInfo GetMethodOrThrow(Type type, string name, BindingFlags flags, Type delegateType) { + return GetMethodOrThrow(type, name, flags, delegateType, Array.Empty(), out _); + } + + private static MethodInfo FindMethod(Type type, string name, BindingFlags flags, Type returnType, params Type[] parameters) { + var method = type.GetMethod(name, flags, null, parameters, null); + + if (method == null) { + return null; + } + + if (method.ReturnType != returnType) { + return null; + } + + return method; + } + + private static ConstructorInfo GetConstructorOrThrow(Type type, BindingFlags flags, Type delegateType, DelegateSwizzle[] swizzles, out DelegateSwizzle firstMatchingSwizzle) { + var delegateMethod = delegateType.GetMethod("Invoke"); + + var allDelegateParameters = delegateMethod.GetParameters().Select(x => x.ParameterType).ToArray(); + + var constructor = type.GetConstructor(flags, null, allDelegateParameters, null); + if (constructor != null) { + firstMatchingSwizzle = null; + return constructor; + } + + if (swizzles != null) { + foreach (var swizzle in swizzles) { + Type[] swizzled = swizzle.Swizzle(allDelegateParameters); + constructor = type.GetConstructor(flags, null, swizzled, null); + if (constructor != null) { + firstMatchingSwizzle = swizzle; + return constructor; + } + } + } + + var constructors = type.GetConstructors(flags); + throw new ArgumentOutOfRangeException(nameof(delegateType), $"No matching constructor found for {type}, " + + $"signature \"{delegateType}\", " + + $"flags \"{flags}\" and " + + $"params: {string.Join(", ", allDelegateParameters.Select(x => x.FullName))}" + + $", candidates are\n: {(string.Join("\n", constructors.Select(x => x.ToString())))}"); + } + + private static MethodInfo GetMethodOrThrow(Type type, string name, BindingFlags flags, Type delegateType, DelegateSwizzle[] swizzles, out DelegateSwizzle firstMatchingSwizzle) { + var delegateMethod = delegateType.GetMethod("Invoke"); + + var allDelegateParameters = delegateMethod.GetParameters().Select(x => x.ParameterType).ToArray(); + + var method = FindMethod(type, name, flags, delegateMethod.ReturnType, flags.HasFlag(BindingFlags.Static) ? allDelegateParameters : allDelegateParameters.Skip(1).ToArray()); + if (method != null) { + firstMatchingSwizzle = null; + return method; + } + + if (swizzles != null) { + foreach (var swizzle in swizzles) { + Type[] swizzled = swizzle.Swizzle(allDelegateParameters); + if (!flags.HasFlag(BindingFlags.Static) && swizzled[0] != type) { + throw new InvalidOperationException(); + } + method = FindMethod(type, name, flags, delegateMethod.ReturnType, flags.HasFlag(BindingFlags.Static) ? swizzled : swizzled.Skip(1).ToArray()); + if (method != null) { + firstMatchingSwizzle = swizzle; + return method; + } + } + } + + var methods = type.GetMethods(flags); + throw new ArgumentOutOfRangeException(nameof(name), $"No method found matching name \"{name}\", " + + $"signature \"{delegateType}\", " + + $"flags \"{flags}\" and " + + $"params: {string.Join(", ", allDelegateParameters.Select(x => x.FullName))}" + + $", candidates are\n: {(string.Join("\n", methods.Select(x => x.ToString())))}"); + } + + public static bool IsArrayOrList(this Type listType) { + if (listType.IsArray) { + return true; + } else if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>)) { + return true; + } + return false; + } + + public static Type GetArrayOrListElementType(this Type listType) { + if (listType.IsArray) { + return listType.GetElementType(); + } else if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>)) { + return listType.GetGenericArguments()[0]; + } + return null; + } + + public static Type MakeFuncType(params Type[] types) { + return GetFuncType(types.Length).MakeGenericType(types); + } + + private static Type GetFuncType(int argumentCount) { + switch (argumentCount) { + case 1: return typeof(Func<>); + case 2: return typeof(Func<,>); + case 3: return typeof(Func<,,>); + case 4: return typeof(Func<,,,>); + case 5: return typeof(Func<,,,,>); + case 6: return typeof(Func<,,,,,>); + default: throw new ArgumentOutOfRangeException(nameof(argumentCount)); + } + } + + public static Type MakeActionType(params Type[] types) { + if (types.Length == 0) return typeof(Action); + return GetActionType(types.Length).MakeGenericType(types); + } + + private static Type GetActionType(int argumentCount) { + switch (argumentCount) { + case 1: return typeof(Action<>); + case 2: return typeof(Action<,>); + case 3: return typeof(Action<,,>); + case 4: return typeof(Action<,,,>); + case 5: return typeof(Action<,,,,>); + case 6: return typeof(Action<,,,,,>); + default: throw new ArgumentOutOfRangeException(nameof(argumentCount)); + } + } + + private static StaticAccessor CreateStaticAccessorInternal(MemberInfo fieldOrProperty) { + try { + var valueParameter = Expression.Parameter(typeof(T), "value"); + bool canWrite = true; + + UnaryExpression valueExpression; + MemberExpression memberExpression; + if (fieldOrProperty is PropertyInfo property) { + valueExpression = Expression.Convert(valueParameter, property.PropertyType); + memberExpression = Expression.Property(null, property); + canWrite = property.CanWrite; + } else { + var field = (FieldInfo)fieldOrProperty; + valueExpression = Expression.Convert(valueParameter, field.FieldType); + memberExpression = Expression.Field(null, field); + canWrite = field.IsInitOnly == false; + } + + Func getter; + var getExpression = Expression.Convert(memberExpression, typeof(T)); + var getLambda = Expression.Lambda>(getExpression); + getter = getLambda.Compile(); + + Action setter = null; + if (canWrite) { + var setExpression = Expression.Assign(memberExpression, valueExpression); + var setLambda = Expression.Lambda>(setExpression, valueParameter); + setter = setLambda.Compile(); + } + + return new StaticAccessor() { + GetValue = getter, + SetValue = setter + }; + } catch (Exception ex) { + throw new InvalidOperationException($"Failed to create accessor for {fieldOrProperty.DeclaringType}.{fieldOrProperty.Name}", ex); + } + } + + private static InstanceAccessor CreateAccessorInternal(MemberInfo fieldOrProperty) { + try { + var instanceParameter = Expression.Parameter(typeof(object), "instance"); + var instanceExpression = Expression.Convert(instanceParameter, fieldOrProperty.DeclaringType); + + var valueParameter = Expression.Parameter(typeof(T), "value"); + bool canWrite = true; + + UnaryExpression valueExpression; + MemberExpression memberExpression; + if (fieldOrProperty is PropertyInfo property) { + valueExpression = Expression.Convert(valueParameter, property.PropertyType); + memberExpression = Expression.Property(instanceExpression, property); + canWrite = property.CanWrite; + } else { + var field = (FieldInfo)fieldOrProperty; + valueExpression = Expression.Convert(valueParameter, field.FieldType); + memberExpression = Expression.Field(instanceExpression, field); + canWrite = field.IsInitOnly == false; + } + + Func getter; + + var getExpression = Expression.Convert(memberExpression, typeof(T)); + var getLambda = Expression.Lambda>(getExpression, instanceParameter); + getter = getLambda.Compile(); + + Action setter = null; + if (canWrite) { + var setExpression = Expression.Assign(memberExpression, valueExpression); + var setLambda = Expression.Lambda>(setExpression, instanceParameter, valueParameter); + setter = setLambda.Compile(); + } + + return new InstanceAccessor() { + GetValue = getter, + SetValue = setter + }; + } catch (Exception ex) { + throw new InvalidOperationException($"Failed to create accessor for {fieldOrProperty.DeclaringType}.{fieldOrProperty.Name}", ex); + } + } + + public struct InstanceAccessor { + public Func GetValue; + public Action SetValue; + } + + public struct StaticAccessor { + public Func GetValue; + public Action SetValue; + } + + public class DelegateSwizzle { + private int[] _args; + + public int Count => _args.Length; + + public DelegateSwizzle(params int[] args) { + _args = args; + } + + public T[] Swizzle(T[] inputTypes) { + T[] result = new T[_args.Length]; + + for (int i = 0; i < _args.Length; ++i) { + result[i] = inputTypes[_args[i]]; + } + + return result; + } + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/SerializedPropertyUtilities.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text.RegularExpressions; + using UnityEditor; + using UnityEngine; + + public static class SerializedPropertyUtilities { + + public static SerializedProperty FindPropertyOrThrow(this SerializedObject so, string propertyPath) { + var result = so.FindProperty(propertyPath); + if (result == null) + throw new ArgumentOutOfRangeException($"Property not found: {propertyPath}"); + return result; + } + + public static SerializedProperty FindPropertyRelativeOrThrow(this SerializedProperty sp, string relativePropertyPath) { + var result = sp.FindPropertyRelative(relativePropertyPath); + if (result == null) + throw new ArgumentOutOfRangeException($"Property not found: {relativePropertyPath}"); + return result; + } + + + public static SerializedProperty FindPropertyRelativeToParentOrThrow(this SerializedProperty property, string relativePath) { + var result = FindPropertyRelativeToParent(property, relativePath); + if (result == null) { + throw new ArgumentOutOfRangeException($"Property relative to the parent of \"{property.propertyPath}\" not found: {relativePath}"); + } + + return result; + } + + static readonly Regex _arrayElementRegex = new Regex(@"\.Array\.data\[\d+\]$", RegexOptions.Compiled); + + static SerializedProperty FindPropertyRelativeToParent(SerializedProperty property, string relativePath) { + SerializedProperty otherProperty; + + var path = property.propertyPath; + + // array element? + if (path.EndsWith("]")) { + var match = _arrayElementRegex.Match(path); + if (match.Success) { + path = path.Substring(0, match.Index); + } + } + + var lastDotIndex = path.LastIndexOf('.'); + if (lastDotIndex < 0) { + otherProperty = property.serializedObject.FindProperty(relativePath); + } else { + otherProperty = property.serializedObject.FindProperty(path.Substring(0, lastDotIndex) + "." + relativePath); + } + + return otherProperty; + } + + /// + /// Returns a long representation of a SerializedProperty's value. Bools convert to 0 and 1. UnityEngine.Objects convert to GetInstanceId(). + /// + public static double GetValueAsDouble(this SerializedProperty property) { + + return + property.propertyType == SerializedPropertyType.Boolean ? (property.boolValue ? 1 : 0) : + property.propertyType == SerializedPropertyType.ObjectReference ? (property.objectReferenceValue == null ? 0 : property.objectReferenceValue.GetInstanceID()) : + property.propertyType == SerializedPropertyType.Float ? (double)property.floatValue : + property.longValue; + } + + /// + /// Returns the value of a field, property, or method in the form of an object. For non-ref types unboxing will be required. + /// + /// + /// Name of a Field, Property or parameterless Method member of the target object + /// + [System.Obsolete("Cache this delegate")] + public static object GetValueFromMember(this UnityEngine.Object obj, string name) { + + if (name == null || name == "") + return null; + + var type = obj.GetType(); + + var members = type.GetMember(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); + + if (members.Length == 0) + return null; + + foreach (var m in members) { + switch (m) { + case FieldInfo finfo: { + return finfo.GetValue(obj); + } + case PropertyInfo pinfo: { + return pinfo.GetValue(obj); + } + + case MethodInfo minfo: { + try { + return minfo.Invoke(obj, null); + } catch { + continue; + } + } + + default: { + break; + } + } + } + return null; + } + + /// + /// Returns a double representation of a boxed value if possible. Bools convert to 0 and 1. UnityEngine.Objects convert to GetInstanceId(). GetHashCode() value is final fallback. + /// + public static double GetObjectValueAsDouble(this object valueObj) { + if (valueObj == null) + return 0; + + var type = valueObj.GetType(); + + if (type.IsByRef) { + var objObj = valueObj as UnityEngine.Object; + if (objObj != null) + return objObj.GetInstanceID(); + + } else { + + if (type.IsEnum) + type = type.GetEnumUnderlyingType(); + + if (type == typeof(bool)) + return ((bool)valueObj) ? 1 : 0; + + if (type == typeof(int)) + return (int)valueObj; + + if (type == typeof(uint)) + return (uint)valueObj; + + if (type == typeof(float)) + return (float)valueObj; + + if (type == typeof(long)) + return (long)valueObj; + + if (type == typeof(ulong)) + return (long)(ulong)valueObj; + + if (type == typeof(byte)) + return (byte)valueObj; + + if (type == typeof(sbyte)) + return (sbyte)valueObj; + + if (type == typeof(short)) + return (short)valueObj; + + if (type == typeof(ushort)) + return (ushort)valueObj; + } + + // This is a last resort fallback... probably useless. + return valueObj.GetHashCode(); + } + + public class SerializedPropertyEqualityComparer : IEqualityComparer { + + public static SerializedPropertyEqualityComparer Instance = new SerializedPropertyEqualityComparer(); + + public bool Equals(SerializedProperty x, SerializedProperty y) { + return SerializedProperty.DataEquals(x, y); + } + + public int GetHashCode(SerializedProperty p) { + + bool enterChildren; + bool isFirst = true; + int hashCode = 0; + int minDepth = p.depth + 1; + + do { + + enterChildren = false; + + switch (p.propertyType) { + case SerializedPropertyType.Integer: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.intValue); break; + case SerializedPropertyType.Boolean: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.boolValue.GetHashCode()); break; + case SerializedPropertyType.Float: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.floatValue.GetHashCode()); break; + case SerializedPropertyType.String: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.stringValue.GetHashCode()); break; + case SerializedPropertyType.Color: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.colorValue.GetHashCode()); break; + case SerializedPropertyType.ObjectReference: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.objectReferenceInstanceIDValue); break; + case SerializedPropertyType.LayerMask: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.intValue); break; + case SerializedPropertyType.Enum: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.intValue); break; + case SerializedPropertyType.Vector2: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.vector2Value.GetHashCode()); break; + case SerializedPropertyType.Vector3: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.vector3Value.GetHashCode()); break; + case SerializedPropertyType.Vector4: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.vector4Value.GetHashCode()); break; + case SerializedPropertyType.Vector2Int: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.vector2IntValue.GetHashCode()); break; + case SerializedPropertyType.Vector3Int: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.vector3IntValue.GetHashCode()); break; + case SerializedPropertyType.Rect: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.rectValue.GetHashCode()); break; + case SerializedPropertyType.RectInt: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.rectIntValue.GetHashCode()); break; + case SerializedPropertyType.ArraySize: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.intValue); break; + case SerializedPropertyType.Character: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.intValue.GetHashCode()); break; + case SerializedPropertyType.AnimationCurve: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.animationCurveValue.GetHashCode()); break; + case SerializedPropertyType.Bounds: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.boundsValue.GetHashCode()); break; + case SerializedPropertyType.BoundsInt: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.boundsIntValue.GetHashCode()); break; + case SerializedPropertyType.ExposedReference: hashCode = HashCodeUtilities.CombineHashCodes(hashCode, p.exposedReferenceValue.GetHashCode()); break; + default: { + enterChildren = true; + break; + } + } + + if (isFirst) { + if (!enterChildren) { + // no traverse needed + return hashCode; + } + + // since property is going to be traversed, a copy needs to be made + p = p.Copy(); + isFirst = false; + } + } while (p.Next(enterChildren) && p.depth >= minDepth); + + return hashCode; + } + } + + /// + /// Get the actual object instance that a serialized property belongs to. This can be expensive reflection and string manipulation, so be sure to cache this result. + /// + public static object GetParent(this SerializedProperty sp) { + var path = sp.propertyPath; + object obj = sp.serializedObject.targetObject; + + // Shortcut if this looks like its not a child of any kind, and target object is our real object. + if (path.Contains(".") == false) + return obj; + + path = path.Replace(".Array.data[", "["); + var elements = path.Split('.'); + foreach (var element in elements.Take(elements.Length - 1)) { + if (element.Contains("[")) { + var elementName = element.Substring(0, element.IndexOf("[")); + var index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", "")); + obj = GetValue(obj, elementName, index); + } else { + obj = GetValue(obj, element); + } + } + return obj; + } + + /// + /// Will attempt to find the index of a property drawer item. Returns -1 if it appears to not be an array type. + /// + /// + /// + public static int GetIndexOfDrawerObject(this SerializedProperty property, bool reportError = true) { + string path = property.propertyPath; + + int start = path.IndexOf("[") + 1; + int len = path.IndexOf("]") - start; + + if (len < 1) + return -1; + + int index = -1; + if ((len > 0 && Int32.TryParse(path.Substring(path.IndexOf("[") + 1, len), out index)) == false && reportError) { + UnityEngine.Debug.Log("Attempted to find the index of a non-array serialized property."); + } + return index; + } + + private static object GetValue(object source, string name) { + if (source == null) + return null; + var type = source.GetType(); + var f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + if (f == null) { + var p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (p == null) + return null; + return p.GetValue(source, null); + } + return f.GetValue(source); + } + + private static object GetValue(object source, string name, int index) { + var enumerable = GetValue(source, name) as System.Collections.IEnumerable; + var enm = enumerable.GetEnumerator(); + while (index-- >= 0) + enm.MoveNext(); + return enm.Current; + } + + public static void DrawPropertyUsingFusionAttributes(this SerializedProperty property, Rect position, GUIContent label, FieldInfo fieldInfo) { + + var unitAttribute = fieldInfo.GetCustomAttribute(); + if (unitAttribute != null) { + UnitAttributeDecoratorDrawer.DrawUnitsProperty(position, property, label, unitAttribute); + } else { + EditorGUI.PropertyField(position, property, label, property.isExpanded); + } + + } + + public static bool IsArrayElement(this SerializedProperty sp) { + return sp.propertyPath.EndsWith("]"); + } + + public static bool IsArrayProperty(this SerializedProperty sp) { + return sp.isArray && sp.propertyType != SerializedPropertyType.String; + } + + private static int[] _temp = Array.Empty(); + + internal static bool UpdateFixedBuffer(this SerializedProperty sp, Action fill, Action update, bool write, bool force = false) { + int count = sp.fixedBufferSize; + Array.Resize(ref _temp, Math.Max(_temp.Length, count)); + + // need to get to the first property... `GetFixedBufferElementAtIndex` is slow and allocs + + var element = sp.Copy(); + element.Next(true); // .Array + element.Next(true); // .Array.size + element.Next(true); // .Array.data[0] + + unsafe { + fixed (int* p = _temp) { + Unity.Collections.LowLevel.Unsafe.UnsafeUtility.MemClear(p, count * sizeof(int)); + } + + fill(_temp, count); + + int i = 0; + if (!force) { + // find the first difference + for (; i < count; ++i, element.Next(true)) { + Debug.Assert(element.propertyType == SerializedPropertyType.Integer); + if (element.intValue != _temp[i]) { + break; + } + } + } + + if (i < count) { + // update data + if (write) { + for (; i < count; ++i, element.Next(true)) { + element.intValue = _temp[i]; + } + } else { + for (; i < count; ++i, element.Next(true)) { + _temp[i] = element.intValue; + } + } + + // update surrogate + update(_temp, count); + return true; + } else { + return false; + } + } + + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/TransformPath.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Text; + using UnityEngine; + + public unsafe struct TransformPath : IComparable, IEquatable { + public const int MaxDepth = 10; + public ushort Depth; + public fixed ushort Indices[MaxDepth]; + public TransformPath* Next; + + public int CompareTo(TransformPath other) { + var diff = CompareToDepthUnchecked(&other, Mathf.Min(Depth, other.Depth)); + if (diff != 0) { + return diff; + } + + return Depth - other.Depth; + } + + public bool Equals(TransformPath other) { + if (Depth != other.Depth) { + return false; + } + + return CompareToDepthUnchecked(&other, Depth) == 0; + } + + public override bool Equals(object obj) { + return obj is TransformPath other ? Equals(other) : false; + } + + public override int GetHashCode() { + int hash = Depth; + return GetHashCode(hash); + } + + public bool IsAncestorOf(TransformPath other) { + if (Depth >= other.Depth) { + return false; + } + + return CompareToDepthUnchecked(&other, Depth) == 0; + } + + public bool IsEqualOrAncestorOf(TransformPath other) { + if (Depth > other.Depth) { + return false; + } + + return CompareToDepthUnchecked(&other, Depth) == 0; + } + + public override string ToString() { + var builder = new StringBuilder(); + fixed (ushort* levels = Indices) { + for (int i = 0; i < Depth && i < MaxDepth; ++i) { + if (i > 0) { + builder.Append("/"); + } + builder.Append(levels[i]); + } + } + + if (Depth > MaxDepth) { + Debug.Assert(Next != null); + builder.Append("/"); + builder.Append(Next->ToString()); + } + + return builder.ToString(); + } + + private int CompareToDepthUnchecked(TransformPath* other, int depth) { + fixed (ushort* indices = Indices) { + for (int i = 0; i < depth && i < MaxDepth; ++i) { + int diff = (int)indices[i] - (int)other->Indices[i]; + if (diff != 0) { + return diff; + } + } + } + + if (depth > MaxDepth) { + Debug.Assert(Next != null); + Debug.Assert(other->Next != null); + Next->CompareToDepthUnchecked(other->Next, depth - MaxDepth); + } + + return 0; + } + private int GetHashCode(int hash) { + fixed (ushort* indices = Indices) { + for (int i = 0; i < Depth && i < MaxDepth; ++i) { + hash = hash * 31 + indices[i]; + } + } + + if (Depth > MaxDepth) { + Debug.Assert(Next != null); + hash = Next->GetHashCode(hash); + } + + return hash; + } + } + + public sealed unsafe class TransformPathCache : IDisposable { + public List _allocs = new List(); + public Dictionary _cache = new Dictionary(); + public List _siblingIndexStack = new List(); + + public TransformPath Create(Transform transform) { + if (_cache.TryGetValue(transform, out var existing)) { + return existing; + } + + _siblingIndexStack.Clear(); + for (var tr = transform; tr != null; tr = tr.parent) { + _siblingIndexStack.Add(checked((ushort)tr.GetSiblingIndex())); + } + _siblingIndexStack.Reverse(); + + TransformPath result = new TransformPath() { + Depth = checked((ushort)_siblingIndexStack.Count) + }; + + TransformPath* current = &result; + for (int i = 0, j = 0; i < _siblingIndexStack.Count; ++i, ++j) { + Debug.Assert(j <= TransformPath.MaxDepth, $"{j}"); + + if (j >= TransformPath.MaxDepth) { + Debug.Assert(current->Next == null); + current->Next = Native.MallocAndClear(); + current = current->Next; + current->Depth = checked((ushort)(_siblingIndexStack.Count - i)); + _allocs.Add(new IntPtr(current)); + j = 0; + } + current->Indices[j] = _siblingIndexStack[i]; + } + + _cache.Add(transform, result); + return result; + } + + public void Dispose() { + foreach (var ptr in _allocs) { + Native.Free((void*)ptr); + } + _allocs.Clear(); + _cache.Clear(); + } + } +} + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Utilities/UnityInternal.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using System.Text; + using System.Threading.Tasks; + using UnityEngine; + + using static Fusion.Editor.ReflectionUtils; + using InitializeOnLoad = UnityEditor.InitializeOnLoadAttribute; + + + public static class UnityInternal { + + [InitializeOnLoad] + public static class Editor { + public delegate bool DoDrawDefaultInspectorDelegate(UnityEditor.SerializedObject obj); + public static readonly DoDrawDefaultInspectorDelegate DoDrawDefaultInspector = typeof(UnityEditor.Editor).CreateMethodDelegate(nameof(DoDrawDefaultInspector)); + } + + + [InitializeOnLoad] + public static class EditorGUI { + public delegate Rect MultiFieldPrefixLabelDelegate(Rect totalPosition, int id, GUIContent label, int columns); + public static readonly MultiFieldPrefixLabelDelegate MultiFieldPrefixLabel = typeof(UnityEditor.EditorGUI).CreateMethodDelegate("MultiFieldPrefixLabel"); + + public delegate string TextFieldInternalDelegate(int id, Rect position, string text, GUIStyle style); + public static readonly TextFieldInternalDelegate TextFieldInternal = typeof(UnityEditor.EditorGUI).CreateMethodDelegate("TextFieldInternal"); + + public delegate string DelayedTextFieldInternalDelegate(Rect position, int id, GUIContent label, string value, string allowedLetters, GUIStyle style); + public static readonly DelayedTextFieldInternalDelegate DelayedTextFieldInternal = typeof(UnityEditor.EditorGUI).CreateMethodDelegate(nameof(DelayedTextFieldInternal)); + + private static readonly FieldInfo s_TextFieldHash = typeof(UnityEditor.EditorGUI).GetFieldOrThrow(nameof(s_TextFieldHash)); + public static int TextFieldHash => (int)s_TextFieldHash.GetValue(null); + + private static readonly FieldInfo s_DelayedTextFieldHash = typeof(UnityEditor.EditorGUI).GetFieldOrThrow(nameof(s_DelayedTextFieldHash)); + public static int DelayedTextFieldHash => (int)s_DelayedTextFieldHash.GetValue(null); + + private static readonly StaticAccessor s_indent = typeof(UnityEditor.EditorGUI).CreateStaticPropertyAccessor(nameof(indent)); + internal static float indent => s_indent.GetValue(); + + public static readonly Action EndEditingActiveTextField = typeof(UnityEditor.EditorGUI).CreateMethodDelegate(nameof(EndEditingActiveTextField)); + } + + [InitializeOnLoad] + public static class DecoratorDrawer { + + static InstanceAccessor m_Attribute = typeof(UnityEditor.DecoratorDrawer).CreateFieldAccessor(nameof(m_Attribute)); + + public static void SetAttribute(UnityEditor.DecoratorDrawer drawer, UnityEngine.PropertyAttribute attribute) { + m_Attribute.SetValue(drawer, attribute); + } + } + + [InitializeOnLoad] + public static class PropertyDrawer { + + static InstanceAccessor m_Attribute = typeof(UnityEditor.PropertyDrawer).CreateFieldAccessor(nameof(m_Attribute)); + static InstanceAccessor m_FieldInfo = typeof(UnityEditor.PropertyDrawer).CreateFieldAccessor(nameof(m_FieldInfo)); + + public static void SetAttribute(UnityEditor.PropertyDrawer drawer, UnityEngine.PropertyAttribute attribute) { + m_Attribute.SetValue(drawer, attribute); + } + public static void SetFieldInfo(UnityEditor.PropertyDrawer drawer, FieldInfo fieldInfo) { + m_FieldInfo.SetValue(drawer, fieldInfo); + } + } + + [InitializeOnLoad] + public static class EditorGUIUtility { + private static readonly StaticAccessor s_LastControlID = typeof(UnityEditor.EditorGUIUtility).CreateStaticFieldAccessor(nameof(s_LastControlID)); + public static int LastControlID => s_LastControlID.GetValue(); + + private static readonly StaticAccessor _contentWidth = typeof(UnityEditor.EditorGUIUtility).CreateStaticPropertyAccessor(nameof(contextWidth)); + public static float contextWidth => _contentWidth.GetValue(); + } + + [InitializeOnLoad] + public static class ScriptAttributeUtility { + public static readonly Type InternalType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ScriptAttributeUtility", true); + + public delegate FieldInfo GetFieldInfoFromPropertyDelegate(UnityEditor.SerializedProperty property, out Type type); + public static readonly GetFieldInfoFromPropertyDelegate GetFieldInfoFromProperty = + CreateEditorMethodDelegate( + "UnityEditor.ScriptAttributeUtility", + "GetFieldInfoFromProperty", + BindingFlags.Static | BindingFlags.NonPublic); + + public delegate Type GetDrawerTypeForTypeDelegate(Type type); + public static readonly GetDrawerTypeForTypeDelegate GetDrawerTypeForType = + CreateEditorMethodDelegate( + "UnityEditor.ScriptAttributeUtility", + "GetDrawerTypeForType", + BindingFlags.Static | BindingFlags.NonPublic); + + private delegate object GetHandlerDelegate(UnityEditor.SerializedProperty property); + private static readonly GetHandlerDelegate _GetHandler = InternalType.CreateMethodDelegate("GetHandler", BindingFlags.NonPublic | BindingFlags.Static, + MakeFuncType(typeof(UnityEditor.SerializedProperty), PropertyHandler.InternalType) + ); + + public delegate List GetFieldAttributesDelegate(FieldInfo field); + public static readonly GetFieldAttributesDelegate GetFieldAttributes = InternalType.CreateMethodDelegate(nameof(GetFieldAttributes)); + + public static PropertyHandler GetHandler(UnityEditor.SerializedProperty property) => PropertyHandler.Wrap(_GetHandler(property)); + + private static readonly StaticAccessor _propertyHandlerCache = InternalType.CreateStaticPropertyAccessor(nameof(propertyHandlerCache), PropertyHandlerCache.InternalType); + public static PropertyHandlerCache propertyHandlerCache => new PropertyHandlerCache { _instance = _propertyHandlerCache.GetValue() }; + + private static readonly StaticAccessor s_SharedNullHandler = InternalType.CreateStaticFieldAccessor("s_SharedNullHandler", PropertyHandler.InternalType); + public static PropertyHandler sharedNullHandler => PropertyHandler.Wrap(s_SharedNullHandler.GetValue()); + + + + } + + public struct PropertyHandlerCache { + + [InitializeOnLoad] + static class Statics { + public static readonly Type InternalType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.PropertyHandlerCache", true); + public static readonly GetPropertyHashDelegate GetPropertyHash = InternalType.CreateMethodDelegate(nameof(GetPropertyHash)); + + public static readonly GetHandlerDelegate GetHandler = InternalType.CreateMethodDelegate(nameof(GetHandler), BindingFlags.NonPublic | BindingFlags.Instance, + MakeFuncType(InternalType, typeof(UnityEditor.SerializedProperty), PropertyHandler.InternalType)); + + public static readonly SetHandlerDelegate SetHandler = InternalType.CreateMethodDelegate(nameof(SetHandler), BindingFlags.NonPublic | BindingFlags.Instance, + MakeActionType(InternalType, typeof(UnityEditor.SerializedProperty), PropertyHandler.InternalType)); + } + + public static Type InternalType => Statics.InternalType; + + public delegate int GetPropertyHashDelegate(UnityEditor.SerializedProperty property); + public delegate object GetHandlerDelegate(object instance, UnityEditor.SerializedProperty property); + public delegate void SetHandlerDelegate(object instance, UnityEditor.SerializedProperty property, object handlerInstance); + + public object _instance; + + public PropertyHandler GetHandler(UnityEditor.SerializedProperty property) { + return new PropertyHandler() { _instance = Statics.GetHandler(_instance, property) }; + } + + public void SetHandler(UnityEditor.SerializedProperty property, PropertyHandler newHandler) { + Statics.SetHandler(_instance, property, newHandler._instance); + } + } + + public struct PropertyHandler : IEquatable { + + [InitializeOnLoad] + static class Statics { + public static readonly Type InternalType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.PropertyHandler", true); + public static readonly InstanceAccessor> m_DecoratorDrawers = InternalType.CreateFieldAccessor>(nameof(m_DecoratorDrawers)); + +#if UNITY_2021_1_OR_NEWER + public static readonly InstanceAccessor> m_PropertyDrawers = InternalType.CreateFieldAccessor>(nameof(m_PropertyDrawers)); +#else + public static readonly InstanceAccessor m_PropertyDrawer = InternalType.CreateFieldAccessor(nameof(m_PropertyDrawer)); +#endif + } + + + + public static Type InternalType => Statics.InternalType; + + public object _instance; + + internal static PropertyHandler Wrap(object instance) => new PropertyHandler() { _instance = instance }; + + public static PropertyHandler New() { + return PropertyHandler.Wrap(Activator.CreateInstance(InternalType)); + } + +#if UNITY_2021_1_OR_NEWER + public List m_PropertyDrawers { + get => Statics.m_PropertyDrawers.GetValue(_instance); + set => Statics.m_PropertyDrawers.SetValue(_instance, value); + } +#else + public UnityEditor.PropertyDrawer m_PropertyDrawer { + get => Statics.m_PropertyDrawer.GetValue(_instance); + set => Statics.m_PropertyDrawer.SetValue(_instance, value); + } +#endif + + public bool HasPropertyDrawer() where T : UnityEditor.PropertyDrawer { +#if UNITY_2021_1_OR_NEWER + return m_PropertyDrawers?.Any(x => x is T) ?? false; +#else + return m_PropertyDrawer is T; +#endif + } + + public IEnumerable PropertyDrawers { +#if UNITY_2021_1_OR_NEWER + get => m_PropertyDrawers ?? Enumerable.Empty(); + set => m_PropertyDrawers = value.ToList(); +#else + get => m_PropertyDrawer != null ? new[] { m_PropertyDrawer } : Enumerable.Empty(); + set => m_PropertyDrawer = value.SingleOrDefault(); +#endif + } + + public bool Equals(PropertyHandler other) { + return _instance == other._instance; + } + + public override int GetHashCode() { + return _instance?.GetHashCode() ?? 0; + } + + public override bool Equals(object obj) { + return (obj is PropertyHandler h) ? Equals(h) : false; + } + + public List decoratorDrawers { + get => Statics.m_DecoratorDrawers.GetValue(_instance); + set => Statics.m_DecoratorDrawers.SetValue(_instance, value); + } + } + + [InitializeOnLoad] + public static class EditorApplication { + public static readonly Action Internal_CallAssetLabelsHaveChanged = typeof(UnityEditor.EditorApplication).CreateMethodDelegate(nameof(Internal_CallAssetLabelsHaveChanged)); + } + + public struct ObjectSelector { + [InitializeOnLoad] + static class Statics { + public static readonly Type InternalType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ObjectSelector", true); + public static readonly StaticAccessor _tooltip = InternalType.CreateStaticPropertyAccessor(nameof(isVisible)); + public static readonly StaticAccessor _get = InternalType.CreateStaticPropertyAccessor(nameof(get), InternalType); + public static readonly InstanceAccessor _searchFilter = InternalType.CreatePropertyAccessor(nameof(searchFilter)); + } + + private UnityEditor.EditorWindow _instance; + + public static bool isVisible => Statics._tooltip.GetValue(); + + public static ObjectSelector get => new ObjectSelector() { _instance = Statics._get.GetValue() }; + + public string searchFilter { + get => Statics._searchFilter.GetValue(_instance); + set => Statics._searchFilter.SetValue(_instance, value); + } + + private static readonly InstanceAccessor _objectSelectorID = Statics.InternalType.CreateFieldAccessor(nameof(objectSelectorID)); + public int objectSelectorID => _objectSelectorID.GetValue(_instance); + } + } +} + + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Wizard/scripts/WizardWindow.cs + +// Renamed and moved Jun 14 2021 + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/Wizard/scripts/WizardWindowUtils.cs + +// Renamed and moved Jun 14 2021 + +#endregion + + +#region Assets/Photon/Fusion/Scripts/Editor/XmlDocumentation.cs + +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Reflection; + using System.Text.RegularExpressions; + using System.Threading.Tasks; + using System.Xml; + using UnityEngine; + + public static class XmlDocumentation { + static HashSet loadedAssemblies = new HashSet(); + static Dictionary loadedXmlSummaries = new Dictionary(); + + public static string GetXmlDocSummary(this MemberInfo member, bool forTooltip = false) { + if (member == null) { + return null; + //throw new ArgumentNullException(nameof(member)); + } + + var type = member as Type ?? member.DeclaringType; + Debug.Assert(type != null); + + LoadCodeDoc(type.Assembly); + var key = GetCodeDocMemberKey(member); + + if (loadedXmlSummaries.TryGetValue(key, out var entry)) { + return forTooltip ? entry.Tooltip : entry.Summary; + } else { + return null; + } + } + + static string GetCodeDocMemberKey(MemberInfo member) { + switch (member) { + case FieldInfo f: return $"F:{SanitizeTypeName(f.DeclaringType)}.{f.Name}"; + case PropertyInfo p: return $"P:{SanitizeTypeName(p.DeclaringType)}.{p.Name}"; + case MethodInfo m: return $"M:{SanitizeTypeName(m.DeclaringType)}.{m.Name}"; + case Type t: return $"T:{SanitizeTypeName(t)}"; + default: + throw new NotSupportedException($"{member.GetType()}"); + } + } + + + static string GetDirectoryPath(this Assembly assembly) { + string codeBase = assembly.CodeBase; + UriBuilder uri = new UriBuilder(codeBase); + string path = Uri.UnescapeDataString(uri.Path); + return Path.GetDirectoryName(path); + } + + static void LoadCodeDoc(Assembly assembly) { + if (!loadedAssemblies.Add(assembly)) { + // already load or attempted to load + return; + } + + if (assembly.GetName().Name.StartsWith("UnityEngine")) { + return; + } + + string directoryPath = assembly.GetDirectoryPath(); + string xmlFilePath = Path.Combine(directoryPath, assembly.GetName().Name + ".xml"); + string xmlContents; + + if (File.Exists(xmlFilePath)) { + xmlContents = File.ReadAllText(xmlFilePath); + } else { + // located in resources + var asset = Resources.Load(assembly.GetName().Name); + if (asset != null) { + xmlContents = asset.text; + } else { + return; + } + } + + var sw = System.Diagnostics.Stopwatch.StartNew(); + ParseCodeDoc(xmlContents, UnityEditor.EditorGUIUtility.isProSkin); + FusionEditorLog.TraceInspector($"Parsing codedoc for {assembly.GetName().Name}: {sw.Elapsed}"); + } + + + + // https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/october/csharp-accessing-xml-documentation-via-reflection + static void ParseCodeDoc(string xmlDocumentation, bool proSkin) { + + var result = new List<(string, Entry)>(); + + var xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(xmlDocumentation); + + var members = xmlDoc.DocumentElement.SelectSingleNode("members"); + var nodes = members.ChildNodes.Cast() + .Where(node => node.NodeType == XmlNodeType.Element && node.Name == "member"); + + Parallel.ForEach(nodes, new ParallelOptions() { + MaxDegreeOfParallelism = 1 + }, + () => new List<(string, Entry)>(), + (node, _, list) => { + + var name = node.Attributes["name"].Value; + var summary = node.SelectSingleNode("summary")?.InnerXml.Trim(); + + if (summary != null) { + + // remove generic indicator + summary = summary.Replace("`1", ""); + // remove Fusion namespace + summary = summary.Replace(":Fusion.", ":"); + + // fork tooltip and help summaries + var help = Reformat(summary, proSkin, false); + var ttip = Reformat(summary, proSkin, true); + + list.Add((name, new Entry() { Summary = help, Tooltip = ttip })); + } + return list; + }, + (list) => { + lock (loadedXmlSummaries) { + //result.AddRange(list); + foreach (var e in list) { + var (name, entry) = e; + if (loadedXmlSummaries.TryGetValue(name, out var existing)) { + FusionEditorLog.Trace($"XmlDoc conflict {name}: {entry} vs {existing}"); + } else { + loadedXmlSummaries.Add(name, entry); + } + } + } + } + ); + } + + + + static class Regexes { + public static readonly Regex SeeWithCref = new Regex(@"", RegexOptions.None); + public static readonly Regex See = new Regex(@"([\w\.\d]*)<\/see\w*>", RegexOptions.None); + public static readonly Regex WhitespaceString = new Regex(@"\s+"); + public static readonly Regex SquareBracketsWithContents = new Regex(@"\[.*\]"); + public static readonly Regex XmlCodeBracket = new Regex(@"([\s\S]*?)"); + public static readonly Regex XmlEmphasizeBrackets = new Regex(@"<\w>([\s\S]*?)"); + } + + // (Inline help summary, Tooltip summary) + private static string Reformat(string summary, bool proSkin, bool forTooltip) { + + // Tooltips don't support formatting tags. Inline help does. + if (forTooltip) { + summary = Regexes.SeeWithCref.Replace(summary, "$1"); + summary = Regexes.See.Replace(summary, "$1"); + summary = Regexes.XmlEmphasizeBrackets.Replace(summary, "$1"); + + } else { + string colorstring = proSkin ? "$1" : "$1"; + summary = Regexes.SeeWithCref.Replace(summary, colorstring); + summary = Regexes.See.Replace(summary, colorstring); + } + + summary = Regexes.XmlCodeBracket.Replace(summary, "$1"); + + // Reduce all sequential whitespace characters into a single space. + summary = Regexes.WhitespaceString.Replace(summary, " "); + + // Turn and
into line breaks + summary = Regex.Replace(summary, @"
\s?", "\n\n"); // prevent back to back paras from producing 4 line returns. + summary = Regex.Replace(summary, @"\s?", "\n\n"); + summary = Regex.Replace(summary, @"\s?", "\n\n"); + + summary = summary.Trim(); + + return summary; + } + + static string SanitizeTypeName(Type type) { + return Regexes.SquareBracketsWithContents.Replace(type.FullName, string.Empty).Replace('+', '.'); + } + + struct Entry { + public string Summary; + public string Tooltip; + + public override string ToString() { + return $"{{ {Summary}; {Tooltip} }}"; + } + } + } +} + +#endregion + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs.meta b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs.meta new file mode 100644 index 0000000..b3aa35e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Fusion.Editor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91a915d06029929418c86dcc2003f785 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub.meta new file mode 100644 index 0000000..d9cd2bb --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 11c1a7075c193b74a8d36f63cf5600e5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources.meta new file mode 100644 index 0000000..36ef56a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b6c8bb440da09c0449958166b8afa66b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons.meta new file mode 100644 index 0000000..64c5a78 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15b92e6da5496974ca88ea26142af229 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt new file mode 100644 index 0000000..55acb02 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt @@ -0,0 +1 @@ +Icons by Fat Cow Hosting (http://www.fatcow.com/free-icons). Licensed under a Creative Commons Attribution 3.0 License. \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt.meta new file mode 100644 index 0000000..027db68 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/ATTRIBUTION.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3c0522d6e35e17249b5660e2309a73b3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png new file mode 100644 index 0000000..563c364 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png.meta new file mode 100644 index 0000000..5046b62 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bugtracker.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 64e7c71a0c9e4e6448ad9b0d1a3679da +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png new file mode 100644 index 0000000..d448caa Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png.meta new file mode 100644 index 0000000..8cd2a78 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_black.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: c83dbbdef4273964a811b82a5dc79a76 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png new file mode 100644 index 0000000..060af25 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png.meta new file mode 100644 index 0000000..255c835 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_down.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: c2ec9e3971ca75947a8d29e18fcbfb65 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png new file mode 100644 index 0000000..f74a914 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png.meta new file mode 100644 index 0000000..600fe75 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/bullet_green.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 1d169d036034e0844963db107478241f +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png new file mode 100644 index 0000000..61ae64b Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png.meta new file mode 100644 index 0000000..b8c7f60 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/comments.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 21f0de4935626ab4ab7767cf3978d93e +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png new file mode 100644 index 0000000..e67fe63 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png.meta new file mode 100644 index 0000000..cfd0ba4 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/community.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 80faca055f57d5b449f78efe31607c57 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png new file mode 100644 index 0000000..1086fbe Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png.meta new file mode 100644 index 0000000..e7e5e6d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/documentation.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: ac3bb1641dbebe441be81b7b3af3606c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png new file mode 100644 index 0000000..e329708 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png.meta new file mode 100644 index 0000000..0ea450b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-icon.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: ee9f419d0e0655a4882d707f86b99f2c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png new file mode 100644 index 0000000..7b36705 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png.meta new file mode 100644 index 0000000..6943680 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/fusion-logo.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 3ffdd609e3329cb46a9ac335d4539399 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png new file mode 100644 index 0000000..93c67f2 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png.meta new file mode 100644 index 0000000..f99bb16 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/information.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 0a86c2478d62ef74bb46b7906cac7fb2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png new file mode 100644 index 0000000..6fffc1b Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png.meta new file mode 100644 index 0000000..c9fede9 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/photon-cloud-32-dark.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 43725de47547036498287ad504fe9f9d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png new file mode 100644 index 0000000..669b150 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png.meta new file mode 100644 index 0000000..d1ff08d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubIcons/samples.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 24a22d21fe312f44892796563f01c302 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons.meta new file mode 100644 index 0000000..8daaf5d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fb9160b551f3fc6489f2b6d54d64fbde +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png new file mode 100644 index 0000000..9dcea07 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png.meta new file mode 100644 index 0000000..4b4be38 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSampleIcons/tanknarok-logo.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 98358aa88aea883458fa24b6502d1da0 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin.meta new file mode 100644 index 0000000..8e0a86f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e2a62c17e34242145af2dbb601a85b72 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin new file mode 100644 index 0000000..0372bc2 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin @@ -0,0 +1,1692 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12001, guid: 0000000000000000e000000000000000, type: 0} + m_Name: FusionHubSkin + m_EditorClassIdentifier: + m_Font: {fileID: 0} + m_box: + m_Name: box + m_Normal: + m_Background: {fileID: 2800000, guid: 5c322c17376df3f448fb66679ef2af5d, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 12 + m_Right: 12 + m_Top: 12 + m_Bottom: 12 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_button: + m_Name: button + m_Normal: + m_Background: {fileID: 2800000, guid: 7c5bca332d2e7d548a317592d570b483, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8584906, g: 0.8584906, b: 0.8584906, a: 1} + m_Hover: + m_Background: {fileID: 2800000, guid: 28ab15b073656894582b34905fc3ef9b, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8773585, g: 0.8773585, b: 0.8773585, a: 1} + m_Active: + m_Background: {fileID: 2800000, guid: d0575c14294b39f4aa7849bf1172ec95, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 7 + m_Right: 7 + m_Top: 7 + m_Bottom: 7 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 12 + m_Right: 12 + m_Top: 12 + m_Bottom: 12 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_Alignment: 3 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_toggle: + m_Name: toggle + m_Normal: + m_Background: {fileID: 11018, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.89112896, g: 0.89112896, b: 0.89112896, a: 1} + m_Hover: + m_Background: {fileID: 11014, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11013, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11016, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8901961, g: 0.8901961, b: 0.8901961, a: 1} + m_OnHover: + m_Background: {fileID: 11015, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11017, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 14 + m_Right: 0 + m_Top: 14 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 15 + m_Right: 0 + m_Top: 3 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: 0 + m_Top: -4 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_label: + m_Name: label + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 13 + m_FontStyle: 0 + m_Alignment: 3 + m_WordWrap: 1 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textField: + m_Name: textfield + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 12 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 3 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textArea: + m_Name: textarea + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_window: + m_Name: window + m_Normal: + m_Background: {fileID: 2800000, guid: 8acec593dd6702e4e872b506285ffc4d, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11022, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 8 + m_Right: 8 + m_Top: 8 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 0 + m_horizontalSlider: + m_Name: horizontalslider + m_Normal: + m_Background: {fileID: 11009, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 3 + m_Right: 3 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -2 + m_Bottom: -3 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSliderThumb: + m_Name: horizontalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 7 + m_Right: 7 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalSlider: + m_Name: verticalslider + m_Normal: + m_Background: {fileID: 11021, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Overflow: + m_Left: -2 + m_Right: -3 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalSliderThumb: + m_Name: verticalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 7 + m_Bottom: 7 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_horizontalScrollbar: + m_Name: horizontalscrollbar + m_Normal: + m_Background: {fileID: 11008, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 9 + m_Right: 9 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 1 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 15 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarThumb: + m_Name: horizontalscrollbarthumb + m_Normal: + m_Background: {fileID: 11007, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: 1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 13 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarLeftButton: + m_Name: horizontalscrollbarleftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarRightButton: + m_Name: horizontalscrollbarrightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbar: + m_Name: verticalscrollbar + m_Normal: + m_Background: {fileID: 11020, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 9 + m_Bottom: 9 + m_Margin: + m_Left: 1 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 1 + m_Bottom: 1 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarThumb: + m_Name: verticalscrollbarthumb + m_Normal: + m_Background: {fileID: 11019, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 6 + m_Bottom: 6 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalScrollbarUpButton: + m_Name: verticalscrollbarupbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarDownButton: + m_Name: verticalscrollbardownbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_ScrollView: + m_Name: scrollview + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_CustomStyles: + - m_Name: thumb + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: leftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: rightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: SteelBox + m_Normal: + m_Background: {fileID: 2800000, guid: 0566916d8e2825c4b8ca741167d4ddda, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8867924, g: 0.8867924, b: 0.8867924, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 8 + m_Bottom: 8 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 16 + m_Right: 16 + m_Top: 16 + m_Bottom: 16 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 3 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: HeaderLogo + m_Normal: + m_Background: {fileID: 2800000, guid: 3ffdd609e3329cb46a9ac335d4539399, type: 3} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8867924, g: 0.8867924, b: 0.8867924, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 8 + m_Bottom: 8 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 16 + m_Right: 16 + m_Top: 16 + m_Bottom: 16 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_Settings: + m_DoubleClickSelectsWord: 1 + m_TripleClickSelectsLine: 1 + m_CursorColor: {r: 1, g: 1, b: 1, a: 1} + m_CursorFlashSpeed: -1 + m_SelectionColor: {r: 1, g: 0.38403907, b: 0, a: 0.7} diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin.meta new file mode 100644 index 0000000..2bbc1c7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/FusionHubSkin.guiskin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db84c499838991343a5a0a419616fcbe +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png new file mode 100644 index 0000000..e45ccac Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png.meta new file mode 100644 index 0000000..77bf0f7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-box.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 5c322c17376df3f448fb66679ef2af5d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png new file mode 100644 index 0000000..4ac8abe Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png.meta new file mode 100644 index 0000000..0071a44 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-active.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: d0575c14294b39f4aa7849bf1172ec95 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png new file mode 100644 index 0000000..489d192 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png.meta new file mode 100644 index 0000000..a8079c4 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button-hover.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 28ab15b073656894582b34905fc3ef9b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png new file mode 100644 index 0000000..669e921 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png.meta new file mode 100644 index 0000000..d6a5c99 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-button.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 7c5bca332d2e7d548a317592d570b483 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png new file mode 100644 index 0000000..a859680 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png.meta new file mode 100644 index 0000000..59ae4a6 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-sand.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 808bf90f5f24df3419fc47e88bc82168 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png new file mode 100644 index 0000000..932ec09 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png.meta new file mode 100644 index 0000000..ebfa037 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-steel.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 0566916d8e2825c4b8ca741167d4ddda +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png new file mode 100644 index 0000000..4782375 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png differ diff --git a/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png.meta b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png.meta new file mode 100644 index 0000000..3ef24b8 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/FusionHub/Resources/FusionHubSkin/fusion-hub-skin-window.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 8acec593dd6702e4e872b506285ffc4d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs new file mode 100644 index 0000000..4d4b40e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs @@ -0,0 +1,177 @@ +namespace Fusion.Editor { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.AssetImporters; + using UnityEngine; + + [ScriptedImporter(1, ExtensionWithoutDot, ImportQueueOffset)] + public class NetworkProjectConfigImporter : ScriptedImporter { + + public const string ExtensionWithoutDot = "fusion"; + public const string Extension = "." + ExtensionWithoutDot; + public const int ImportQueueOffset = 1000; + + public string PrefabAssetsContainerPath = string.Empty; + public const string FusionPrefabTag = "FusionPrefab"; + public const string FusionPrefabTagSearchTerm = "l:FusionPrefab"; + + + const string MissingPrefabPrefix = "~MISSING~"; + + public override void OnImportAsset(AssetImportContext ctx) { + + FusionEditorLog.TraceImport(assetPath, "Staring scripted import"); + + NetworkProjectConfig.UnloadGlobal(); + NetworkProjectConfig config = LoadConfigFromFile(ctx.assetPath); + + var root = ScriptableObject.CreateInstance(); + root.Config = config; + ctx.AddObjectToAsset("root", root); + + root.Prefabs = DiscoverPrefabs(ctx); + RefreshPrefabAssets(root); + } + + public static NetworkProjectConfig LoadConfigFromFile(string path) { + var config = new NetworkProjectConfig(); + try { + var text = File.ReadAllText(path); + if (string.IsNullOrWhiteSpace(text)) { + throw new System.ArgumentException("Empty string"); + } + EditorJsonUtility.FromJsonOverwrite(text, config); + } catch (System.ArgumentException ex) { + throw new System.ArgumentException($"Failed to parse {path}: {ex.Message}"); + } + return config; + } + + private void RefreshPrefabAssets(NetworkProjectConfigAsset config) { +#if !FUSION_UNITY_DISABLE_PREFAB_ASSETS + if (string.IsNullOrEmpty(PrefabAssetsContainerPath)) { + return; + } + + bool hadChanges = false; + + var mainAsset = AssetDatabase.LoadMainAssetAtPath(PrefabAssetsContainerPath); + var root = mainAsset as NetworkPrefabAssetCollection; + + var existingPrefabs = AssetDatabase.LoadAllAssetsAtPath(PrefabAssetsContainerPath) + .OfType() + .ToList(); + + var remainingPrefabs = config.Prefabs + .GroupBy(x => x.AssetGuid) + .ToDictionary(x => x.Key, x => x.First()); + + if (mainAsset == null) { + root = ScriptableObject.CreateInstance(); + root.name = Path.GetFileNameWithoutExtension(PrefabAssetsContainerPath); + AssetDatabase.CreateAsset(root, PrefabAssetsContainerPath); + hadChanges = true; + } else if (root == null) { + FusionEditorLog.WarnImport($"{nameof(PrefabAssetsContainerPath)} needs to point to an asset that is not {typeof(NetworkPrefabAssetCollection)} type: {mainAsset.GetType()}"); + return; + } + + foreach (var existingPrefab in existingPrefabs) { + if (remainingPrefabs.TryGetValue(existingPrefab.AssetGuid, out var source)) { + // keep + remainingPrefabs.Remove(existingPrefab.AssetGuid); + var asset = AssetDatabaseUtils.SetScriptableObjectType(existingPrefab); + if (asset.name != source.name) { + FusionEditorLog.TraceImport(PrefabAssetsContainerPath, $"Noticed renamed or restored: {asset.name}"); + hadChanges = true; + asset.name = source.name; + } + } else { + // remove + var asset = AssetDatabaseUtils.SetScriptableObjectType(existingPrefab); + if (!asset.name.StartsWith(MissingPrefabPrefix)) { + FusionEditorLog.TraceImport(PrefabAssetsContainerPath, $"Noticed missing: {asset.name}"); + asset.name = MissingPrefabPrefix + asset.name; + hadChanges = true; + } + } + } + + foreach (var source in remainingPrefabs) { + // new + var asset = ScriptableObject.CreateInstance(); + asset.name = source.Value.name; + asset.AssetGuid = source.Key; + AssetDatabase.AddObjectToAsset(asset, PrefabAssetsContainerPath); + + FusionEditorLog.TraceImport(PrefabAssetsContainerPath, $"Noticed new {asset.name}"); + hadChanges = true; + } + + if (hadChanges) { + AssetDatabase.SaveAssets(); + } +#endif + } + + private NetworkPrefabSourceUnityBase[] DiscoverPrefabs(AssetImportContext ctx) { + + var result = new List(); + + var guids = AssetDatabase.FindAssets($"l:{FusionPrefabTag} t:prefab"); + FusionEditorLog.TraceImport(assetPath, $"Found {guids.Length} assets marked with {FusionPrefabTag} label"); + + var detailsLog = new System.Text.StringBuilder(); + + foreach (var guid in guids) { + var assetPath = AssetDatabase.GUIDToAssetPath(guid); + + NetworkPrefabSourceUnityBase prefabSource; + try { + prefabSource = NetworkPrefabSourceFactory.Create(assetPath); +#if FUSION_EDITOR_TRACE + detailsLog.AppendLine($"{assetPath} -> {((INetworkPrefabSource)prefabSource).EditorSummary}"); +#endif + } catch (Exception ex) { + ctx.LogImportWarning($"Unable to create prefab asset for {assetPath}: {ex}"); + continue; + } + + prefabSource.name = Path.GetFileNameWithoutExtension(assetPath); + prefabSource.hideFlags = HideFlags.HideInHierarchy; + prefabSource.AssetGuid = NetworkObjectGuid.Parse(guid); + + ctx.DependsOnSourceAsset(assetPath); + ctx.AddObjectToAsset(guid, prefabSource); + + result.Add(prefabSource); + } + + FusionEditorLog.TraceImport($"Discover prefabs details [{result.Count}] :\n{detailsLog}"); + + result.Sort((a, b) => a.AssetGuid.CompareTo(b.AssetGuid)); + return result.ToArray(); + } + + class Postprocessor : AssetPostprocessor { + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { + + foreach (var path in deletedAssets) { + if (path.EndsWith(Extension)) { + NetworkProjectConfig.UnloadGlobal(); + break; + } + } + foreach (var path in movedAssets) { + if (path.EndsWith(Extension)) { + NetworkProjectConfig.UnloadGlobal(); + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs.meta b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs.meta new file mode 100644 index 0000000..1c18099 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66a64a17d0b40f34f9224317a5a84bf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs new file mode 100644 index 0000000..3051919 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs @@ -0,0 +1,115 @@ +namespace Fusion.Editor { + + using System; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.AssetImporters; + using UnityEngine; + + [CustomEditor(typeof(NetworkProjectConfigImporter))] + internal class NetworkProjectConfigImporterEditor : ScriptedImporterEditor { + + private Exception _initializeException; + + private static bool _versionExpanded; + private static string _version; + private static string _allVersionInfo; + + public override bool showImportedObject => false; + + protected override Type extraDataType => typeof(NetworkProjectConfigAsset); + + public override void OnInspectorGUI() { + + try { + if (_initializeException != null) { + EditorGUILayout.HelpBox(_initializeException.ToString(), MessageType.Error, true); + } else { + + FusionEditorGUI.InjectPropertyDrawers(extraDataSerializedObject); + FusionEditorGUI.ScriptPropertyField(extraDataSerializedObject); + + VersionInfoGUI(); + + using (new EditorGUI.DisabledScope(HasModified())) { + if (GUILayout.Button("Rebuild Object Table (Slow)")) { + NetworkProjectConfigUtilities.RebuildObjectTable(); + } + } + + FusionEditorGUI.DrawDefaultInspector(extraDataSerializedObject, drawScript: false); + } + } finally { + ApplyRevertGUI(); + } + } + + private static void VersionInfoGUI() { + if (_allVersionInfo == null || _allVersionInfo == "") { + var asms = System.AppDomain.CurrentDomain.GetAssemblies(); + for (int i = 0; i < asms.Length; ++i) { + var asm = asms[i]; + var asmname = asm.FullName; + if (asmname.StartsWith("Fusion.Runtime,")) { + _version = NetworkRunner.BuildType + ": " + System.Diagnostics.FileVersionInfo.GetVersionInfo(asm.Location).ProductVersion; + } + if (asmname.StartsWith("Fusion.") || asmname.StartsWith("Fusion,")) { + string fvi = System.Diagnostics.FileVersionInfo.GetVersionInfo(asm.Location).ToString(); + _allVersionInfo += asmname.Substring(0, asmname.IndexOf(",")) + ": " + fvi + " " + "\n"; + } + } + } + + + var r = EditorGUILayout.GetControlRect(); + _versionExpanded = EditorGUI.Foldout(r, _versionExpanded, ""); + EditorGUI.LabelField(r, "Fusion Version", _version); + + if (_versionExpanded) { + EditorGUILayout.HelpBox(_allVersionInfo, MessageType.None); + } + } + + protected override void Apply() { + base.Apply(); + + if (assetTarget != null) { + for (int i = 0; i < extraDataTargets.Length; ++i) { + var importer = GetImporter(i); + var wrapper = GetConfigWrapper(i); + + importer.PrefabAssetsContainerPath = wrapper.PrefabAssetsContainerPath; + EditorUtility.SetDirty(importer); + + var json = EditorJsonUtility.ToJson(wrapper.Config, true); + File.WriteAllText(importer.assetPath, json); + } + } + } + + protected override void InitializeExtraDataInstance(UnityEngine.Object extraData, int targetIndex) { + try { + var importer = GetImporter(targetIndex); + var extra = (NetworkProjectConfigAsset)extraData; + extra.Config = NetworkProjectConfigImporter.LoadConfigFromFile(importer.assetPath); + extra.PrefabAssetsContainerPath = importer.PrefabAssetsContainerPath; + extra.Prefabs = AssetDatabase.LoadAllAssetsAtPath(importer.assetPath) + .OfType() + .ToArray(); + + _initializeException = null; + } catch (Exception ex) { + _initializeException = ex; + } + } + + private NetworkProjectConfigImporter GetImporter(int i) { + return (NetworkProjectConfigImporter)targets[i]; + } + + private NetworkProjectConfigAsset GetConfigWrapper(int i) { + return (NetworkProjectConfigAsset)extraDataTargets[i]; + } + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs.meta b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs.meta new file mode 100644 index 0000000..60fa9a4 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/NetworkProjectConfigImporterEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e94c67db235c29f4891980a488edca07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/Windows.meta b/Assets/Photon/Fusion/Scripts/Editor/Windows.meta new file mode 100644 index 0000000..4d0e82d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a71fe8bd58507f34cb8e8da2e77a9c84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs b/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs new file mode 100644 index 0000000..10ec7dc --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs @@ -0,0 +1,257 @@ +namespace Fusion.Editor { + using System; + using System.Reflection; + using UnityEngine; + using UnityEditor; + + public class RunnerVisibilityControlsWindow : EditorWindow { + + const int WINDOW_MIN_W = 82; + const int WINDOW_MIN_H = 48; + + const int STATS_BTTN_WIDE = 66; + const int STATS_BTTN_SLIM = 24; + const int RUNNR_BTTN_WIDE = 60; + const int RUNNR_BTTN_SLIM = 24; + const int FONT_SIZE = 9; + + const float TEXT_SWITCH_WIDTH = 200; + const float WIDE_SWITCH_WIDTH = 380; + + const double REFRESH_RATE = 1f; + + private static Lazy s_labelStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.label); + result.fontSize = FONT_SIZE; + return result; + }); + + private static Lazy s_labelTinyStyle = new Lazy(() => { + var result = new GUIStyle(s_labelStyle.Value); + result.fontSize = FONT_SIZE - 1; + return result; + }); + + private static Lazy s_labelCenterStyle = new Lazy(() => { + var result = new GUIStyle(s_labelStyle.Value); + result.alignment = TextAnchor.MiddleCenter; + return result; + }); + + private static Lazy s_buttonStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.miniButton); + result.fontSize = FONT_SIZE; + return result; + }); + + private static Lazy s_helpboxStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.helpBox); + result.fontSize = FONT_SIZE; + result.alignment = TextAnchor.MiddleCenter; + result.padding = new RectOffset(6, 6, 6, 6); + return result; + }); + + static Lazy s_invisibleButtonStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.label); + result.fontSize = FONT_SIZE; + result.padding = new RectOffset(); + return result; + }); + static Lazy s_invisibleButtonGrayStyle = new Lazy(() => { + var result = new GUIStyle(EditorStyles.label); + result.fontSize = FONT_SIZE; + result.normal.textColor = Color.gray; + result.active.textColor = Color.gray; + result.hover.textColor = Color.gray; + result.focused.textColor = Color.gray; + result.padding = new RectOffset(); + return result; + }); + + private static Lazy Dark = new Lazy(() => { + return EditorGUIUtility.isProSkin ? "d_" : ""; + }); + + private static Lazy s_toggleGC = new Lazy(() => { + return new GUIContent("", "Toggles IsVisible for this Runner. [Shift + Click] will solo the selected runner."); + }); + + static Lazy s_visibleIcon = new Lazy(() => { + return new GUIContent(EditorGUIUtility.FindTexture(Dark.Value + "scenevis_visible_hover@2x"), "Click to toggle this NetworkRunner visibility,"); + }); + + static Lazy s_hiddenIcon = new Lazy(() => { + return new GUIContent(EditorGUIUtility.FindTexture(Dark.Value + "scenevis_hidden@2x"), "Click to toggle this NetworkRunner visibility,"); + }); + + static Lazy s_inputIconLong = new Lazy(() => { + return new GUIContent("\u2002Providing Inputs", EditorGUIUtility.FindTexture(Dark.Value + "UnityEditor.GameView@2x"), ""); + }); + + static Lazy s_inputIconShort = new Lazy(() => { + return new GUIContent(null, EditorGUIUtility.FindTexture(Dark.Value + "UnityEditor.GameView@2x"), ""); + }); + + static Lazy s_noInputIconLong = new Lazy(() => { + return new GUIContent("\u2002(No Inputs)", EditorGUIUtility.FindTexture(Dark.Value + "Toolbar Minus@2x"), ""); + }); + + static Lazy s_noInputIconShort = new Lazy(() => { + return new GUIContent(null, EditorGUIUtility.FindTexture(Dark.Value + "Toolbar Minus@2x"), ""); + }); + + // EditorGUIUtility.FindTexture( Dark + "UnityEditor.GameView@2x" ) + + public static RunnerVisibilityControlsWindow Instance { get; private set; } + + Vector2 _scrollPosition; + double _lastRepaintTime; + + [MenuItem("Window/Fusion/Runner Visibility Controls")] + [MenuItem("Fusion/Windows/Runner Visibility Controls")] + public static void ShowWindow() { + var window = GetWindow(typeof(RunnerVisibilityControlsWindow), false, "Runner Visibility Controls"); + window.minSize = new Vector2(WINDOW_MIN_W, WINDOW_MIN_H); + Instance = (RunnerVisibilityControlsWindow)window; + } + + + private void Awake() { + Instance = this; + } + + private void OnEnable() { + Instance = this; + } + + private void OnDestroy() { + Instance = null; + } + + + private void Update() { + // Force a repaint every x seconds in case runner count and runner settings have changed. + if ((Time.realtimeSinceStartup - _lastRepaintTime) > REFRESH_RATE) + Repaint(); + } + + private void OnGUI() { + + _lastRepaintTime = Time.realtimeSinceStartup; + + var currentViewWidth = EditorGUIUtility.currentViewWidth; + bool isWide = currentViewWidth > WIDE_SWITCH_WIDTH; + bool shortText = currentViewWidth < TEXT_SWITCH_WIDTH; + + _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition); + + if (!Application.isPlaying) { + EditorGUILayout.LabelField("No Active Runners", s_helpboxStyle.Value); + } else { + var enumerator = NetworkRunner.GetInstancesEnumerator(); + while (enumerator.MoveNext()) { + var runner = enumerator.Current; + + // Only show active runners. + if (!runner || !runner.IsRunning) { + continue; + } + + NetworkProjectConfig config = runner.Config; + + bool isSinglePeer = config.PeerMode == NetworkProjectConfig.PeerModes.Single; + + EditorGUILayout.BeginHorizontal(); + { + var lclplayer = runner.LocalPlayer; + var lclplayerid = lclplayer.IsValid ? "P" + lclplayer.PlayerId.ToString() : "--"; + + string runnerName = + shortText ? (runner.IsServer ? (runner.IsSinglePlayer ? "SP" : runner.IsPlayer ? "H" : "S") : "C") : + runner.name; + + var toggleGuiContent = s_toggleGC.Value; + + + // Draw Runner Names/Buttons + toggleGuiContent.text = runnerName; + var runnerrect = EditorGUILayout.GetControlRect(GUILayout.ExpandWidth(true), GUILayout.MinWidth(isWide ? RUNNR_BTTN_WIDE : RUNNR_BTTN_SLIM)); + if (GUI.Button(runnerrect, s_toggleGC.Value, s_buttonStyle.Value)) { + EditorGUIUtility.PingObject(runner); + Selection.activeGameObject = runner.gameObject; + } + + + if (shortText == false) { + + // Draw PlayerRef Id / Local Player Object buttons + var playerRefRect = EditorGUILayout.GetControlRect(GUILayout.Width(isWide ? 38 : 38)); + var playerObj = runner.GetPlayerObject(lclplayer); + using (new EditorGUI.DisabledGroupScope(playerObj == false)) { + + if (GUI.Button(playerRefRect, lclplayerid, s_buttonStyle.Value)) { + if (playerObj) { + EditorGUIUtility.PingObject(runner.GetPlayerObject(lclplayer)); + } + } + } + } + + + // Draw Visibility Icons + using (new EditorGUI.DisabledGroupScope(isSinglePeer)) { + toggleGuiContent.text = ""; + var togglerect = EditorGUILayout.GetControlRect(GUILayout.Width(18)); + + if (GUI.Button(togglerect, runner.IsVisible ? s_visibleIcon.Value : s_hiddenIcon.Value, s_invisibleButtonStyle.Value)) { + runner.IsVisible = !runner.IsVisible; + } + }; + + + // Draw Provide Input icon/text + using (new EditorGUI.DisabledGroupScope(runner.Mode == SimulationModes.Server)) { + var inputToggleRect = EditorGUILayout.GetControlRect(GUILayout.Width(isWide ? 106 : 18)); + var inputContent = isWide ? + (runner.ProvideInput ? s_inputIconLong.Value : s_noInputIconLong.Value) : + (runner.ProvideInput ? s_inputIconShort.Value : s_noInputIconShort.Value); + + if (GUI.Button(inputToggleRect,inputContent, runner.ProvideInput ? s_invisibleButtonStyle.Value : s_invisibleButtonGrayStyle.Value)) { + runner.ProvideInput = !runner.ProvideInput; + } + }; + + + // Draw runtime stats creation buttons. Reflection used since this namespace can't see FusionStats. + + if (currentViewWidth >= WINDOW_MIN_W + 10) { + var statsleftrect = EditorGUILayout.GetControlRect(GUILayout.Width(isWide ? STATS_BTTN_WIDE : STATS_BTTN_SLIM)); + var statsrghtrect = EditorGUILayout.GetControlRect(GUILayout.Width(isWide ? STATS_BTTN_WIDE : STATS_BTTN_SLIM)); + if (GUI.Button(statsleftrect, isWide ? "<< Stats" : "<<", s_buttonStyle.Value)) { + var stats = Type.GetType("FusionStats, Assembly-CSharp").GetMethod("CreateInternal", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { runner, 1, null, null }); + EditorGUIUtility.PingObject(((UnityEngine.Component)stats).gameObject); + Selection.activeObject = ((UnityEngine.Component)stats).gameObject; + } + if (GUI.Button(statsrghtrect, isWide ? "Stats >>" : ">>", s_buttonStyle.Value)) { + var stats = Type.GetType("FusionStats, Assembly-CSharp").GetMethod("CreateInternal", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[] { runner, 2, null, null }); + EditorGUIUtility.PingObject(((UnityEngine.Component)stats).gameObject); + Selection.activeObject = ((UnityEngine.Component)stats).gameObject; + } + } + + // Draw UserID + if (currentViewWidth > 600) { + using (new EditorGUI.DisabledGroupScope(true)) { + var userIdRect = EditorGUILayout.GetControlRect(GUILayout.MinWidth(40), GUILayout.ExpandWidth(true)); + GUI.Label(userIdRect, "UserID: " + ((runner.UserId == null) ? " --" : runner.UserId.ToString()), s_labelTinyStyle.Value); + } + } + } + EditorGUILayout.EndHorizontal(); + } + } + EditorGUILayout.EndScrollView(); + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs.meta b/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs.meta new file mode 100644 index 0000000..576592b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Windows/RunnerVisibilityControlsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0b9919e3f6641c4dbd365521e52c809 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs b/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs new file mode 100644 index 0000000..343b3ff --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs @@ -0,0 +1 @@ +// Deleted May 22 2021 diff --git a/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs.meta b/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs.meta new file mode 100644 index 0000000..c3a796a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Editor/Windows/SharedSceneControlsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c089eaf95973e634da6889705dc2b897 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs b/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs new file mode 100644 index 0000000..01b5037 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs @@ -0,0 +1,145 @@ +#if FUSION_BURST +using Fusion; +using Fusion.Sockets; +using Unity.Burst; +using UnityEngine; + +[BurstCompile] +public static unsafe class FusionBurstIntegration { + public delegate void PackDelegate(int* current, int* shared, int words, NetBitBuffer* buffer); + + public delegate void UnpackDelegate(int* target, int bitCount, NetBitBuffer* buffer); + + class DeltaCompressor : Simulation.IDeltaCompressor { + public Simulation.IDeltaCompressor Default; + + public PackDelegate PackDelegate; + public UnpackDelegate UnpackDelegate; + + void Simulation.IDeltaCompressor.Pack(int* current, int* shared, int words, NetBitBuffer* buffer) { + PackDelegate(current, shared, words, buffer); + } + + void Simulation.IDeltaCompressor.Unpack(int* target, int bitCount, NetBitBuffer* buffer) { + Default.Unpack(target, bitCount, buffer); + } + } + + [RuntimeInitializeOnLoadMethod] + public static void Init() { + DeltaCompressor compressor; + + compressor = new DeltaCompressor(); + compressor.Default = Simulation.GetDefaultDeltaCompressor(); + compressor.PackDelegate = BurstCompiler.CompileFunctionPointer(Pack).Invoke; + compressor.UnpackDelegate = BurstCompiler.CompileFunctionPointer(Unpack).Invoke; + + // set on runner + NetworkRunner.BurstDeltaCompressor = compressor; + + // burst aoi resolver + Simulation.AreaOfInterest.BurstInsertAndResolve = BurstCompiler.CompileFunctionPointer(BurstAreaOfInterestInsertAndResovleDelegate).Invoke; + } + + static readonly byte[] _debruijnTable32 = { + 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 + }; + + static int BitScanReverse(uint v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return _debruijnTable32[(v * 0x07C4ACDDU) >> 27]; + } + + static readonly int[] _debruijnTable64 = new int[128] { + 0, + 48, -1, -1, 31, -1, 15, 51, -1, 63, 5, -1, -1, -1, 19, -1, + 23, 28, -1, -1, -1, 40, 36, 46, -1, 13, -1, -1, -1, 34, -1, 58, + -1, 60, 2, 43, 55, -1, -1, -1, 50, 62, 4, -1, 18, 27, -1, 39, + 45, -1, -1, 33, 57, -1, 1, 54, -1, 49, -1, 17, -1, -1, 32, -1, + 53, -1, 16, -1, -1, 52, -1, -1, -1, 64, 6, 7, 8, -1, 9, -1, + -1, -1, 20, 10, -1, -1, 24, -1, 29, -1, -1, 21, -1, 11, -1, -1, + 41, -1, 25, 37, -1, 47, -1, 30, 14, -1, -1, -1, -1, 22, -1, -1, + 35, 12, -1, -1, -1, 59, 42, -1, -1, 61, 3, 26, 38, 44, -1, 56 + }; + + static int BitScanReverse(ulong v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return _debruijnTable64[(v * 0x6c04f118e9966f6bUL) >> 57]; + } + + const int BITCOUNT = 64; + const int USEDMASK = BITCOUNT - 1; + const int INDEXSHIFT = 6; + const ulong MAXVALUE = ulong.MaxValue; + + + static void WriteUInt32VarLength(NetBitBuffer* b, uint value, int blockSize) { + // how many blocks + var blocks = (BitScanReverse(value) + blockSize) / blockSize; + + // write data + Write(b, 1U << (blocks - 1), blocks); + Write(b, value, blocks * blockSize); + } + + static void WriteUInt64VarLength(NetBitBuffer* b, ulong value, int blockSize) { + // how many blocks + var blocks = (BitScanReverse(value) + blockSize) / blockSize; + + // write data + Write(b, 1U << (blocks - 1), blocks); + Write(b, value, blocks * blockSize); + } + + static void Write(NetBitBuffer* buffer, ulong value, int bits) { + var bitsUsed = buffer->_offsetBits & USEDMASK; + var bitsFree = BITCOUNT - bitsUsed; + + var b = buffer->_data + (buffer->_offsetBits >> INDEXSHIFT); + + *b = (*b & ((1UL << bitsUsed) - 1UL)) | (value << bitsUsed); + + if (bitsFree < bits) { + *(b + 1) = value >> bitsFree; + } + + buffer->_offsetBits += bits; + } + + [BurstCompile] + public static void BurstAreaOfInterestInsertAndResovleDelegate(Simulation.AreaOfInterest* aoi, Accuracy* accuracy, Allocator* allocator, NetworkObjectRefMapPtr* map) { + Simulation.AreaOfInterest.InsertObjects(aoi, *accuracy, allocator, map); + Simulation.AreaOfInterest.Resolve(aoi, allocator); + } + + [BurstCompile] + public static void Pack(int* current, int* shared, int words, NetBitBuffer* buffer) { + var offset = 0; + + for (int i = 0; i < words; ++i) { + if (shared[i] != current[i]) { + var d = current[i] - (long) shared[i]; + + WriteUInt32VarLength(buffer, unchecked((uint) (i - offset)), Simulation.DEFAULT_COMPRESSOR_OFFSET_BLOCK_SIZE); + WriteUInt64VarLength(buffer, unchecked((ulong) ((d >> 63) ^ (d << 1))), Simulation.DEFAULT_COMPRESSOR_VALUE_BLOCK_SIZE); + + offset = i; + } + } + } + + [BurstCompile] + public static void Unpack(int* target, int bitCount, NetBitBuffer* buffer) { + } +} +#endif \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs.meta b/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs.meta new file mode 100644 index 0000000..84183b3 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionBurstIntegration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f88402df611061e4ab248f4b04ee65c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionGraph.cs b/Assets/Photon/Fusion/Scripts/FusionGraph.cs new file mode 100644 index 0000000..8497508 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionGraph.cs @@ -0,0 +1,936 @@ +using System; +using UnityEngine; +using Fusion; +using UI = UnityEngine.UI; +using Stats = Fusion.Simulation.Statistics; +using Fusion.StatsInternal; +using System.Collections.Generic; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +/// +/// Individual graph components generated and used by . +/// +public class FusionGraph : FusionGraphBase { + + public enum Layouts { + Auto, + FullAuto, + FullNoOverlap, + CenteredAuto, + CenteredNoGraph, + CenteredNoOverlap, + CompactAuto, + CompactNoGraph, + } + + public enum ShowGraphOptions { + Never, + OverlayOnly, + Always, + } + + enum ShaderType { + None, + Overlay, + GameObject + } + + const int GRPH_TOP_PAD = 36; + const int GRPH_BTM_PAD = 36; + const int HIDE_XTRAS_WDTH = 200; + const int INTERMITTENT_DATA_ARRAYSIZE = 128; + + const int EXPAND_GRPH_THRESH = GRPH_BTM_PAD + GRPH_TOP_PAD + 40; + const int COMPACT_THRESH = GRPH_BTM_PAD + GRPH_TOP_PAD - 20; + + static Shader Shader { + get => Resources.Load("FusionGraphShader"); + } + + // Not sure height is 0f much use anymore. + [SerializeField] [HideInInspector] public float Height = 50; + + /// + /// Select between automatic formating (based on size and aspect ratio of the graph) and manual selection of the various graph layouts available. + /// + [InlineHelp] + [SerializeField] + [Header("Graph Layout")] + Layouts _layout; + public Layouts Layout { + get => _layout; + set { + _layout = value; + CalculateLayout(); + } + } + + /// + /// Controls if the graph shader will render. Currently the graph shader only works in Overlay mode, so if forced to show Always here, it will not render correctly in 3d space. + /// + [InlineHelp] + [SerializeField] + ShowGraphOptions _showGraph = ShowGraphOptions.Always; + public ShowGraphOptions ShowGraph { + get => _showGraph; + set { + _showGraph = value; + CalculateLayout(); + _layoutDirty = true; + } + } + + /// + /// Padding added to text and other layout objects. + /// + [InlineHelp] + public float Padding = 5f; + + /// + /// The graph shaded area (which is only visible in Overlay mode) will expand to the full extends of the component when this is enabled, regardless of size. + /// When false, the graph will automatically expand only as needed. + /// + [InlineHelp] + [SerializeField] + bool _alwaysExpandGraph; + public bool AlwaysExpandGraph { + get => _alwaysExpandGraph; + set { + _alwaysExpandGraph = value; + CalculateLayout(); + _layoutDirty = true; + } + } + + + /// + /// Exposes the UI labels and controls of , so they may be modified if customizing this graph. + /// + [InlineHelp] + [SerializeField] + bool _showUITargets; + + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Image GraphImg; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Text LabelMin; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Text LabelMax; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Text LabelAvg; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Text LabelLast; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Text LabelPer; + + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Dropdown _viewDropdown; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public UI.Button _avgBttn; + + float _min; + float _max; + float[] _values; + float[] _intensity; + float[] _histogram; + +#if UNITY_EDITOR + + + protected override void OnValidate() { + base.OnValidate(); + if (Application.isPlaying == false) { + //This is here so when changes are made that affect graph names/colors they get applied immediately. + TryConnect(); + } + +#if UNITY_EDITOR + if (Selection.activeGameObject == gameObject) { + UnityEditor.EditorApplication.delayCall += CalculateLayout; + } +#endif + _layoutDirty = true; + } +#endif + + List DropdownLookup = new List(); + + protected override bool TryConnect() { + + bool isConnected = base.TryConnect(); + + if (isConnected) { + + var flags = _statsBuffer.VisualizationFlags; + + DropdownLookup.Clear(); + _viewDropdown.ClearOptions(); + for (int i = 0; i < 16; ++i) { + if (((int)flags & (1 << i)) != 0) { + DropdownLookup.Add(1 << i); + _viewDropdown.options.Add(new UI.Dropdown.OptionData(FusionStatsUtilities.CachedTelemetryNames[i + 1])); + if ((1 << i & (int)_statsBuffer.DefaultVisualization) != 0) { + _viewDropdown.value = i - 1; + } + } + } + SetPerText(); + return true; + } + return false; + } + + [InlineHelp] + FusionGraphVisualization _graphVisualization; + public FusionGraphVisualization GraphVisualization { + set { + _graphVisualization = value; + Reset(); + } + } + + private void Reset() { + _values = null; + _histogram = null; + _intensity = null; + _min = 0; + _max = 0; + + ResetGraphShader(); + } + + public void Clear() { + if (_values != null && _values.Length > 0) { + Array.Clear(_values, 0, _values.Length); + Array.Clear(_histogram, 0, _histogram.Length); + for (int i = 0; i < _intensity.Length; ++i) { + _intensity[i] = -2; + } + _min = 0; + _max = 0; + _histoHighestUsedBucketIndex = 0; + _histoAvg = 0; + _histoAvgSampleCount = 0; + } + } + + public override void Initialize() { + + _viewDropdown?.onValueChanged.AddListener(OnDropdownChanged); + _avgBttn?.onClick.AddListener(CyclePer); + } + + public void OnDropdownChanged(int value) { + GraphVisualization = (FusionGraphVisualization)DropdownLookup[value]; + SetPerText(); + } + + [BehaviourButtonAction("ResetShader")] + void ResetShaderButton() { + //TEST + _intensity = new float[200]; + _values = new float[200]; + for (int i = 0; i < _values.Length; ++i) { + _values[i] = (float)i / _values.Length; + _intensity[i] = (float)i / 200; ; + } + + GraphImg.material.SetFloat("_ZeroCenter", .3f); + GraphImg.material.SetFloatArray("_Data", _values); + GraphImg.material.SetFloatArray("_Intensity", _intensity); + GraphImg.material.SetInt("_Count", _values.Length); + } + + ShaderType _currentShader; + + void ResetGraphShader() { + if (GraphImg) { + ShaderType desiredShader = LocateParentFusionStats() != null ? (_fusionStats.CanvasType == FusionStats.StatCanvasTypes.GameObject ? ShaderType.GameObject : ShaderType.Overlay) : ShaderType.None; + //if (_currentShader != desiredShader) { + // GraphImg.material = desiredShader == ShaderType.GameObject ? new Material(Shader3D) : new Material(Shader); + // _currentShader = desiredShader; + //} + //GraphImg.material.renderQueue = 3000; + GraphImg.material = new Material(Shader); + GraphImg.material.SetColor("_GoodColor", _fusionStats.GraphColorGood); + GraphImg.material.SetColor("_WarnColor", _fusionStats.GraphColorWarn); + GraphImg.material.SetColor("_BadColor", _fusionStats.GraphColorBad); + GraphImg.material.SetColor("_FlagColor", _fusionStats.GraphColorFlag); + GraphImg.material.SetInt("_ZWrite", (desiredShader == ShaderType.GameObject ? 1 : 0)); + } + } + + public override void CyclePer() { + if (_graphVisualization != FusionGraphVisualization.CountHistogram && _graphVisualization != FusionGraphVisualization.ValueHistogram) { + base.CyclePer(); + SetPerText(); + } + } + + void SetPerText() { + // TODO: Temporary - here to avoid breaking existing implementations. Can be removed. Added Dec 15 2021 + if (LabelPer == null) { + var prt = LabelAvg.rectTransform.parent.CreateRectTransform("Per") + .SetAnchors(0.3f, 0.7f, 0.0f, 0.125f) + .SetOffsets(MRGN, -MRGN, MRGN, 0.0f); + LabelPer = prt.AddText("per sample", TextAnchor.LowerCenter, _fusionStats.FontColor); + } + + LabelPer.text = + // Histograms only show avg per sample + (_graphVisualization == FusionGraphVisualization.ValueHistogram | _graphVisualization == FusionGraphVisualization.CountHistogram) ? "avg per Sample" : + CurrentPer == Stats.StatsPer.Second ? "avg per Second" : + CurrentPer == Stats.StatsPer.Tick ? "avg per Tick" : + "avg per Sample"; + } + /// + /// Returns true if the graph rendered. False if the size was too small and the graph was hidden. + /// + /// + public override void Refresh() { + + if (_layoutDirty) { + CalculateLayout(); + } + + var statsBuffer = StatsBuffer; + if (statsBuffer == null || statsBuffer.Count < 1) { + return; + } + + var visualization = _graphVisualization == FusionGraphVisualization.Auto ? _statsBuffer.DefaultVisualization : _graphVisualization; + + // TODO: move initialization of this to TryConnect? + if (_values == null) { + int size = + visualization == FusionGraphVisualization.ContinuousTick ? statsBuffer.Capacity : + visualization == FusionGraphVisualization.ValueHistogram ? StatSourceInfo.HistoBucketCount + 3 :// _histoBucketCount + 3 : + INTERMITTENT_DATA_ARRAYSIZE; + + _values = new float[size]; + _histogram = new float[size]; + _intensity = new float[size]; + } + + + switch (visualization) { + case FusionGraphVisualization.ContinuousTick: { + UpdateContinuousTick(ref statsBuffer); + break; + } + case FusionGraphVisualization.IntermittentTick: { + UpdateIntermittentTick(ref statsBuffer); + break; + } + case FusionGraphVisualization.IntermittentTime: { + UpdateIntermittentTime(ref statsBuffer); + break; + } + case FusionGraphVisualization.CountHistogram: { + //UpdateIntermittentTime(data); + break; + } + case FusionGraphVisualization.ValueHistogram: { + UpdateTickValueHistogram(ref statsBuffer); + break; + } + } + } + + void UpdateContinuousTick(ref IStatsBuffer data) + { + var min = float.MaxValue; + var max = float.MinValue; + var avg = 0f; + var last = 0f; + + for (int i = 0; i < data.Count; ++i) { + var v = (float)(StatSourceInfo.Multiplier * data.GetSampleAtIndex(i).FloatValue); + + min = Math.Min(v, min); + max = Math.Max(v, max); + + if (i >= _values.Length) + Debug.LogWarning(name + " Out of range " + i + " " + _values.Length + " " + data.Count); + _values[i] = last = v; + + avg += v; + } + + avg /= data.Count; + + ApplyScaling(ref min, ref max); + UpdateUiText(min, max, avg, last); + } + + // Intermittent Tick pulls values from a very short buffer, so we collect those values and merge them into a larger cache. + (int tick, float value)[] _cachedValues; + double _lastCachedTickTime; + int _lastCachedTick; + + void UpdateIntermittentTick(ref IStatsBuffer data) { + + if (_cachedValues == null) { + _cachedValues = new (int, float)[INTERMITTENT_DATA_ARRAYSIZE]; + } + + int latestServerStateTick = _fusionStats.Runner.Simulation.LatestServerState.Tick; + + var min = float.MaxValue; + var max = float.MinValue; + var sum = 0f; + var last = 0f; + + var oldestAllowedBufferedTick = latestServerStateTick - INTERMITTENT_DATA_ARRAYSIZE + 1; + + var tailIndex = latestServerStateTick % INTERMITTENT_DATA_ARRAYSIZE; + var headIndex = (tailIndex + 1) % INTERMITTENT_DATA_ARRAYSIZE; + + int gapcheck = _lastCachedTick; + // Copy all data from the buffer into our larger intermediate cached buffer + for (int i = 0; i < data.Count; ++i) { + var sample = data.GetSampleAtIndex(i); + var sampleTick = sample.TickValue; + + // sample on buffer is older than the range we are displaying. + if (sampleTick < oldestAllowedBufferedTick) { + gapcheck = sampleTick; + continue; + } + + // sample on the buffer has already been merged into cached buffer. + if (sampleTick <= _lastCachedTick) { + gapcheck = sampleTick; + continue; + } + + // Fill any gaps in the buffer data + var gap = sampleTick - gapcheck; + for (int g = gapcheck + 1; g < sampleTick; ++g) { + _cachedValues[g % INTERMITTENT_DATA_ARRAYSIZE] = (g, 0); + } + + _lastCachedTick = sampleTick; + _cachedValues[sampleTick % INTERMITTENT_DATA_ARRAYSIZE] = (sampleTick, (float)(StatSourceInfo.Multiplier * sample.FloatValue)); + + gapcheck = sampleTick; + } + + // Loop through once to determine scaling + for (int i = 0; i < INTERMITTENT_DATA_ARRAYSIZE; ++i) { + var sample = _cachedValues[(i + headIndex) % INTERMITTENT_DATA_ARRAYSIZE]; + var v = sample.value; + // Any outdated values are ticks that had no data, set them to zero. + if (sample.tick < oldestAllowedBufferedTick) { + sample.tick = oldestAllowedBufferedTick + i; + sample.value = v = 0; + } + + min = Math.Min(v, min); + max = Math.Max(v, max); + + _values[i] = last = v; + + sum += v; + } + + var avg = GetIntermittentAverageInfo(ref data, sum); + ApplyScaling(ref min, ref max); + UpdateUiText(min, max, avg, last); + + } + + void UpdateIntermittentTime(ref IStatsBuffer data) { + var min = float.MaxValue; + var max = float.MinValue; + var sum = 0f; + var last = 0f; + + for (int i = 0; i < data.Count; ++i) { + var v = (float)(StatSourceInfo.Multiplier * data.GetSampleAtIndex(i).FloatValue); + + min = Math.Min(v, min); + max = Math.Max(v, max); + + _values[i] = last = v; + + sum += v; + } + var avg = GetIntermittentAverageInfo(ref data, sum); + + ApplyScaling(ref min, ref max); + UpdateUiText(min, max, avg, last); + } + + + void ApplyScaling(ref float min, ref float max) { + + if (min > 0) { + min = 0; + } + + if (max > _max) { + _max = max; + } + + if (min < _min) { + _min = min; + } + + var r = _max - _min; + + for (int i = 0, len = _values.Length; i < len; ++i) { + var val = _values[i]; + var intensity = + val < 0 ? -1f : + val >= ErrorThreshold ? 1f : + val >= WarnThreshold ? Mathf.Lerp(.5f, 1f, (val - WarnThreshold) / (ErrorThreshold - WarnThreshold)) : + //val <= ErrorThreshold ? Mathf.Lerp(.5f, 1f, (val / WarnThreshold) - .5f) : // .5f : + 0f; + + _intensity[i] = intensity; + _values[i] = Mathf.Clamp01((val - _min) / r); + } + } + + void UpdateUiText(float min, float max, float avg, float last) { + + var decimals = StatSourceInfo.Decimals; + // TODO: At some point this label null checks should be removed + if (LabelMin) { LabelMin.text = Math.Round(min, decimals).ToString(); } + if (LabelMax) { LabelMax.text = Math.Round(max, decimals).ToString(); } + if (LabelAvg) { LabelAvg.text = Math.Round(avg, decimals).ToString(); } + if (LabelLast) { LabelLast.text = Math.Round(last, decimals).ToString(); } + + if (GraphImg && GraphImg.enabled) { + GraphImg.material.SetFloatArray("_Data", _values); + GraphImg.material.SetFloatArray("_Intensity", _intensity); + GraphImg.material.SetFloat("_Count", _values.Length); + GraphImg.material.SetFloat("_Height", Height); + GraphImg.material.SetFloat("_ZeroCenter", min < 0 ? min / (min - max) : 0); + } + + _min = Mathf.Lerp(_min, 0, Time.deltaTime); + _max = Mathf.Lerp(_max, 1, Time.deltaTime); + } + + + float GetIntermittentAverageInfo(ref IStatsBuffer data, float sum) { + + switch (CurrentPer) { + case Stats.StatsPer.Second: { + var oldestTimeRecord = data.GetSampleAtIndex(0).TimeValue; + var currentTime = (float)_fusionStats.Runner.Simulation.LatestServerState.Time; + var avg = sum / (currentTime - oldestTimeRecord); + return avg; + } + + case Stats.StatsPer.Tick: { + var oldestTickRecord = data.GetSampleAtIndex(0).TickValue; + var currentTick = (float)_fusionStats.Runner.Simulation.LatestServerState.Tick; + var avg = sum / (currentTick - oldestTickRecord); + return avg; + } + + default: { + var avg = sum / _values.Length; // data.Count; + return avg; + } + } + } + + int _histoHighestUsedBucketIndex; + int _histoAvgSampleCount; + double _histoStepInverse; + double _histoAvg; + + void UpdateTickValueHistogram(ref IStatsBuffer data) { + + var histoBucketCount = StatSourceInfo.HistoBucketCount; + var histoMaxValue = StatSourceInfo.HistogMaxValue; + + // Determine histogram bucket sizes if they haven't yet been determined. + if (_histoStepInverse == 0) { + _histoStepInverse = histoBucketCount / StatSourceInfo.HistogMaxValue; + } + + var mostCurrentSample = data.GetSampleAtIndex(data.Count - 1); + var mostCurrentTick = mostCurrentSample.TickValue; + var latestServerState = _fusionStats.Runner.Simulation.LatestServerState; + var usingTick = mostCurrentTick > 0; + double latestServerStateTickTime; + + if (usingTick) { + latestServerStateTickTime = latestServerState.Tick; + double mostCurrentSampleTickTime = mostCurrentTick; + + // count non-existent ticks as zero values. Only for tick based data. + if (mostCurrentSampleTickTime < latestServerStateTickTime) { + int countbackto = Math.Max((int)mostCurrentSampleTickTime, (int)_lastCachedTickTime); + int newZeroCount = (int)latestServerStateTickTime - countbackto; + float zerocountTotal = _histogram[0] + newZeroCount; + _histogram[0] = zerocountTotal; + + if (zerocountTotal > _max) { + _max = zerocountTotal; + } + } + + } else { + latestServerStateTickTime = latestServerState.Time; + } + + var info = StatSourceInfo; + double multiplier = info.Multiplier; + // Read data in stat buffer backwards until we reach a tick already recorded + for (int i = data.Count - 1; i >= 0; --i) { + var v = (float)(multiplier * data.GetSampleAtIndex(i).FloatValue); + + var sample = data.GetSampleAtIndex(i); + double ticktime = usingTick ? sample.TickValue : sample.TimeValue; + + if (ticktime <= _lastCachedTickTime) { + break; + } + + var val = sample.FloatValue * multiplier; + + int bucketIndex; + if (val == 0) { + bucketIndex = 0; + } + else if (val == histoMaxValue) { + bucketIndex = histoBucketCount; + + } + else if (val > histoMaxValue) { + bucketIndex = histoBucketCount + 1; + } + else { + bucketIndex = (int)(val * _histoStepInverse) + 1; + } + + _histoAvg = (_histoAvg * _histoAvgSampleCount + val) / (++_histoAvgSampleCount); + + var newval = _histogram[bucketIndex] + 1; + + if (newval > _max) { + _max = newval; + } + _histogram[bucketIndex] = newval; + + if (bucketIndex > _histoHighestUsedBucketIndex) { + _histoHighestUsedBucketIndex = bucketIndex; + } + //if (val > _histoHighestValue) { + // _histoHighestValue = val; + // _histoHighestUsedBucketIndex = bucketIndex; + //} + } + + int medianIndex = 0; + float mostValues = 0; + { + var r = (_max - _min) * 1.1f; + + // Loop again to apply scaling + for (int i = 0, cnt = _histogram.Length; i < cnt; ++i) { + var value = _histogram[i]; + _intensity[i] = 0; + if (i != 0 && value > mostValues) { + mostValues = value; + medianIndex = i; + } + _values[i] = Mathf.Clamp01((_histogram[i] - _min) / r); + } + } + + // Color the highest bar + _intensity[medianIndex] = 2f; + + _lastCachedTickTime = latestServerStateTickTime; + + if (GraphImg && GraphImg.enabled) { + GraphImg.material.SetFloatArray("_Data", _values); + GraphImg.material.SetFloatArray("_Intensity", _intensity); + GraphImg.material.SetFloat("_Count", _histoHighestUsedBucketIndex + 1); + GraphImg.material.SetFloat("_Height", Height); + } + + _min = 0; + var decimals = info.Decimals; + LabelMax.text = $"{Math.Ceiling((medianIndex + 1) / _histoStepInverse)}"; + LabelAvg.text = Math.Round(_histoAvg, decimals).ToString(); + LabelMin.text = Math.Floor(_min).ToString(); + LabelLast.text = Math.Round((_histoHighestUsedBucketIndex + 1) / _histoStepInverse, decimals).ToString(); + } + + /// + /// Creates a new GameObject with and attaches it to the specified parent. + /// + public static FusionGraph Create(FusionStats iFusionStats, Stats.StatSourceTypes statSourceType, int statId, RectTransform parentRT) { + + var statInfo = Stats.GetDescription(statSourceType, statId); + + var rootRT = parentRT.CreateRectTransform(statInfo.LongName); + var graph = rootRT.gameObject.AddComponent(); + graph._fusionStats = iFusionStats; + graph.Generate(statSourceType, (int)statId, rootRT); + + return graph; + } + + /// + /// Generates the Graph UI for this . + /// + public void Generate(Stats.StatSourceTypes type, int statId, RectTransform root) { + + _statSourceType = type; + + var rt = GetComponent(); + + _statId = statId; + + root.anchorMin = new Vector2(0.5f, 0.5f); + root.anchorMax = new Vector2(0.5f, 0.5f); + root.anchoredPosition3D = default; + + var background = root.CreateRectTransform("Background") + .ExpandAnchor(); + + BackImage = background.gameObject.AddComponent(); + BackImage.color = BackColor; + BackImage.raycastTarget = false; + + var graphRT = background.CreateRectTransform("Graph") + .SetAnchors(0.0f, 1.0f, 0.2f, 0.8f) + .SetOffsets(0.0f, 0.0f, 0.0f, 0.0f); + + GraphImg = graphRT.gameObject.AddComponent(); + GraphImg.raycastTarget = false; + ResetGraphShader(); + + var fontColor = _fusionStats.FontColor; + var fontColorDim = _fusionStats.FontColor * new Color(1, 1, 1, 0.5f); + + var titleRT = root.CreateRectTransform("Title") + .ExpandAnchor() + .SetOffsets(PAD, -PAD, 0.0f, -2.0f); + titleRT.anchoredPosition = new Vector2(0, 0); + + LabelTitle = titleRT.AddText(name, TextAnchor.UpperRight, fontColor); + LabelTitle.resizeTextMaxSize = MAX_FONT_SIZE_WITH_GRAPH; + LabelTitle.raycastTarget = true; + + // Top Left value + var maxRT = root.CreateRectTransform("Max") + .SetAnchors(0.0f, 0.3f, 0.85f, 1.0f) + .SetOffsets(MRGN, 0.0f, 0.0f, -2.0f); + LabelMax = maxRT.AddText("-", TextAnchor.UpperLeft, fontColorDim); + + // Bottom Left value + var minRT = root.CreateRectTransform("Min") + .SetAnchors(0.0f, 0.3f, 0.0f, 0.15f) + .SetOffsets(MRGN, 0.0f, 0.0f, -2.0f); + LabelMin = minRT.AddText("-", TextAnchor.LowerLeft, fontColorDim); + + // Main Center value + var avgRT = root.CreateRectTransform("Avg") + .SetOffsets(0.0f, 0.0f, 0.0f, 0.0f); + avgRT.anchoredPosition = new Vector2(0, 0); + LabelAvg = avgRT.AddText("-", TextAnchor.LowerCenter, fontColor); + LabelAvg.raycastTarget = true; + _avgBttn = avgRT.gameObject.AddComponent(); + + // Main Center value + var perRT = root.CreateRectTransform("Per") + .SetAnchors(0.3f, 0.7f, 0.0f, 0.125f) + .SetOffsets(MRGN, -MRGN, MRGN, 0.0f); + LabelPer = perRT.AddText("avg per Sample", TextAnchor.LowerCenter, fontColor); + + // Bottom Right value + var _lstRT = root.CreateRectTransform("Last") + .SetAnchors(0.7f, 1.0f, 0.0f, 0.15f) + .SetOffsets(PAD, -PAD, 0.0f, -2.0f); + LabelLast = _lstRT.AddText("-", TextAnchor.LowerRight, fontColorDim); + + _viewDropdown = titleRT.CreateDropdown(PAD, fontColor); + + _layoutDirty = true; +#if UNITY_EDITOR + EditorUtility.SetDirty(this); +#endif + + } + + [BehaviourButtonAction("Update Layout")] + public override void CalculateLayout() { + // This Try/Catch is here to prevent errors resulting from a delayCall to this method when entering play mode. + try { + if (gameObject == null) { + return; + } + } catch { + return; + } + + if (gameObject.activeInHierarchy == false) { + return; + } + _layoutDirty = false; + + var rt = GetComponent(); + + if (_statsBuffer == null) { + TryConnect(); + } + ApplyTitleText(); + + bool graphIsValid = StatSourceInfo.InvalidReason == null; + + LabelMin.gameObject.SetActive(graphIsValid); + LabelMax.gameObject.SetActive(graphIsValid); + LabelAvg.gameObject.SetActive(graphIsValid); + LabelPer.gameObject.SetActive(graphIsValid); + + if (!graphIsValid) { + LabelTitle.rectTransform.ExpandAnchor(PAD); + LabelTitle.alignment = TextAnchor.MiddleCenter; + LabelTitle.raycastTarget = false; + _viewDropdown.gameObject.SetActive(false); + return; + } + + GraphImg.material.SetInt("_ZWrite", (_fusionStats.CanvasType == FusionStats.StatCanvasTypes.GameObject ? 1 : 0)); + + + bool isOverlayCanvas = _fusionStats.CanvasType == FusionStats.StatCanvasTypes.GameObject; + bool vrSafe = _fusionStats.NoGraphShader /*&& _fusionStats.CanvasType == FusionStats.StatCanvasTypes.GameObject*/; + + var height = rt.rect.height; + var width = rt.rect.width; + + Layouts layout; + if (_layout != Layouts.Auto) { + layout = _layout; + } else { + if (_fusionStats.DefaultLayout != Layouts.Auto) { + layout = _fusionStats.DefaultLayout; + } else { + if (height < COMPACT_THRESH) { + layout = Layouts.CompactAuto; + } else { + if (width < HIDE_XTRAS_WDTH) { + layout = Layouts.CenteredAuto; + } else { + layout = Layouts.FullAuto; + } + } + } + } + + bool noGraph = vrSafe || layout == Layouts.CompactNoGraph || layout == Layouts.CenteredNoGraph || (_fusionStats.NoTextOverlap && layout == Layouts.CompactAuto); + bool noOverlap = _fusionStats.NoTextOverlap || layout == Layouts.FullNoOverlap || layout == Layouts.CenteredNoOverlap; + bool showGraph = !noGraph && (ShowGraph == ShowGraphOptions.Always || (ShowGraph == ShowGraphOptions.OverlayOnly && isOverlayCanvas)); + + bool expandGraph = !noOverlap && (_alwaysExpandGraph || !showGraph || layout == Layouts.CompactAuto || (!noOverlap && height < EXPAND_GRPH_THRESH)); + bool isSuperShort = height < MRGN * 3; + + var graphRT = GraphImg.rectTransform; + if (graphRT) { + graphRT.gameObject.SetActive(showGraph); + + if (expandGraph) { + graphRT.SetAnchors(0, 1, 0, 1); + } else { + graphRT.SetAnchors(0, 1, .25f, .8f); + } + } + + bool showExtras = layout == Layouts.FullAuto || layout == Layouts.FullNoOverlap /*|| (layout == Layouts.Compact && width > HIDE_XTRAS_WDTH)*/; + + var titleRT = LabelTitle.rectTransform; + var avgRT = LabelAvg.rectTransform; + + // TODO: Temporary - here to avoid breaking existing implementations. Can be removed. Added Dec 15 2021 + if (LabelPer == null) { + var prt = avgRT.parent.CreateRectTransform("Per") + .SetAnchors(0.3f, 0.7f, 0.0f, 0.125f) + .SetOffsets(MRGN, -MRGN, MRGN, 0.0f); + LabelPer = prt.AddText("per sample", TextAnchor.LowerCenter, _fusionStats.FontColor); + } + + var perRT = LabelPer.rectTransform; + + switch (layout) { + case Layouts.FullNoOverlap: + case Layouts.FullAuto: { + titleRT.anchorMin = new Vector2(showExtras ? 0.3f : 0.0f, expandGraph ? 0.5f : 0.8f); + titleRT.anchorMax = new Vector2(1.0f, 1.0f); + titleRT.offsetMin = new Vector2(MRGN, 0); + titleRT.offsetMax = new Vector2(-MRGN, -MRGN); + LabelTitle.alignment = showExtras ? TextAnchor.UpperRight : TextAnchor.UpperCenter; + + avgRT.anchorMin = new Vector2(showExtras ? 0.3f : 0.0f, expandGraph ? 0.15f : 0.10f); + avgRT.anchorMax = new Vector2(showExtras ? 0.7f : 1.0f, expandGraph ? 0.50f : 0.25f); + avgRT.SetOffsets(0.0f, 0.0f, 0.0f, 0.0f); + LabelAvg.alignment = TextAnchor.LowerCenter; + + perRT.SetAnchors(0.3f, 0.7f, 0.0f, expandGraph ? 0.2f : 0.1f); + LabelPer.alignment = TextAnchor.LowerCenter; + + break; + } + case Layouts.CenteredNoOverlap: + case Layouts.CenteredNoGraph: + case Layouts.CenteredAuto: { + titleRT.anchorMin = new Vector2(0.0f, expandGraph ? 0.5f : 0.8f); + titleRT.anchorMax = new Vector2(1.0f, 1.0f); + titleRT.offsetMin = new Vector2( MRGN, 0); + titleRT.offsetMax = new Vector2(-MRGN, -MRGN); + LabelTitle.alignment = TextAnchor.UpperCenter; + + avgRT.anchorMin = new Vector2(0.0f, expandGraph ? 0.15f : 0.10f); + avgRT.anchorMax = new Vector2(1.0f, expandGraph ? 0.50f : 0.25f); + avgRT.SetOffsets(MRGN, -MRGN, 0.0f, 0.0f); + + perRT.SetAnchors(0.0f, 1.0f, 0.0f, expandGraph ? 0.2f : 0.1f); + LabelPer.alignment = TextAnchor.LowerCenter; + + LabelAvg.alignment = TextAnchor.LowerCenter; + break; + } + + case Layouts.CompactNoGraph: + case Layouts.CompactAuto: { + titleRT.anchorMin = new Vector2(0.05f, 0.0f); + titleRT.anchorMax = new Vector2(0.5f, 1.0f); + if (isSuperShort) { + titleRT.SetOffsets(0, 0, 0, 0); + avgRT .SetOffsets(MRGN, -0, 0, 0); + } else { + titleRT.SetOffsets(0, 0, MRGN, -MRGN); + avgRT .SetOffsets(MRGN, -0, MRGN, -MRGN); + } + LabelTitle.alignment = TextAnchor.MiddleLeft; + + avgRT.SetAnchors(0.5f, 0.95f, 0.0f, 1.0f); + + perRT.SetAnchors(0.5f, 0.95f, 0.0f, 0.15f) + .SetOffsets(MRGN, -MRGN * 2, MRGN, 0.0f); + LabelPer.alignment = TextAnchor.LowerRight; + + LabelAvg.alignment = TextAnchor.MiddleRight; + break; + } + } + + LabelMin.enabled = showExtras; + LabelMax.enabled = showExtras; + LabelLast.enabled = showExtras; + } + +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/FusionGraph.cs.meta b/Assets/Photon/Fusion/Scripts/FusionGraph.cs.meta new file mode 100644 index 0000000..0df00ce --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionGraph.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a5d15872b7ea01a4ba287865a146bec2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs b/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs new file mode 100644 index 0000000..6a75dd3 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs @@ -0,0 +1 @@ +// deleted \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs.meta b/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs.meta new file mode 100644 index 0000000..4c4ef35 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionPhotonClientSocket.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2213172f7cee9334d9ff54a765cec7e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionProfiler.cs b/Assets/Photon/Fusion/Scripts/FusionProfiler.cs new file mode 100644 index 0000000..8502e49 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionProfiler.cs @@ -0,0 +1,58 @@ +#if FUSION_PROFILER_INTEGRATION +using Unity.Profiling; +using UnityEngine; + +public static class FusionProfiler { + [RuntimeInitializeOnLoadMethod] + static void Init() { + Fusion.EngineProfiler.InterpolationOffsetCallback = f => InterpolationOffset.Sample(f); + Fusion.EngineProfiler.InterpolationTimeScaleCallback = f => InterpolationTimeScale.Sample(f); + Fusion.EngineProfiler.InterpolationMultiplierCallback = f => InterpolationMultiplier.Sample(f); + Fusion.EngineProfiler.InterpolationUncertaintyCallback = f => InterpolationUncertainty.Sample(f); + + Fusion.EngineProfiler.ResimulationsCallback = i => Resimulations.Sample(i); + Fusion.EngineProfiler.WorldSnapshotSizeCallback = i => WorldSnapshotSize.Sample(i); + + Fusion.EngineProfiler.RoundTripTimeCallback = f => RoundTripTime.Sample(f); + + Fusion.EngineProfiler.InputSizeCallback = i => InputSize.Sample(i); + Fusion.EngineProfiler.InputQueueCallback = i => InputQueue.Sample(i); + + Fusion.EngineProfiler.RpcInCallback = i => RpcIn.Value += i; + Fusion.EngineProfiler.RpcOutCallback = i => RpcOut.Value += i; + + Fusion.EngineProfiler.SimualtionTimeScaleCallback = f => SimulationTimeScale.Sample(f); + + Fusion.EngineProfiler.InputOffsetCallback = f => InputOffset.Sample(f); + Fusion.EngineProfiler.InputOffsetDeviationCallback = f => InputOffsetDeviation.Sample(f); + + Fusion.EngineProfiler.InputRecvDeltaCallback = f => InputRecvDelta.Sample(f); + Fusion.EngineProfiler.InputRecvDeltaDeviationCallback = f => InputRecvDeltaDeviation.Sample(f); + } + + public static readonly ProfilerCategory Category = ProfilerCategory.Scripts; + + public static readonly ProfilerCounter InterpolationOffset = new ProfilerCounter(Category, "Interp Offset", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter InterpolationTimeScale = new ProfilerCounter(Category, "Interp Time Scale", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter InterpolationMultiplier = new ProfilerCounter(Category, "Interp Multiplier", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter InterpolationUncertainty = new ProfilerCounter(Category, "Interp Uncertainty", ProfilerMarkerDataUnit.Undefined); + + public static readonly ProfilerCounter InputSize = new ProfilerCounter(Category, "Client Input Size", ProfilerMarkerDataUnit.Bytes); + public static readonly ProfilerCounter InputQueue = new ProfilerCounter(Category, "Client Input Queue", ProfilerMarkerDataUnit.Count); + + public static readonly ProfilerCounter WorldSnapshotSize = new ProfilerCounter(Category, "Client Snapshot Size", ProfilerMarkerDataUnit.Bytes); + public static readonly ProfilerCounter Resimulations = new ProfilerCounter(Category, "Client Resims", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter RoundTripTime = new ProfilerCounter(Category, "Client RTT", ProfilerMarkerDataUnit.Count); + + public static readonly ProfilerCounterValue RpcIn = new ProfilerCounterValue(Category, "RPCs In", ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + public static readonly ProfilerCounterValue RpcOut = new ProfilerCounterValue(Category, "RPCs Out", ProfilerMarkerDataUnit.Count, ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush); + + public static readonly ProfilerCounter SimulationTimeScale = new ProfilerCounter(Category, "Simulation Time Scale", ProfilerMarkerDataUnit.Count); + + public static readonly ProfilerCounter InputOffset = new ProfilerCounter(Category, "Input Offset", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter InputOffsetDeviation = new ProfilerCounter(Category, "Input Offset Dev", ProfilerMarkerDataUnit.Count); + + public static readonly ProfilerCounter InputRecvDelta = new ProfilerCounter(Category, "Input Recv Delta", ProfilerMarkerDataUnit.Count); + public static readonly ProfilerCounter InputRecvDeltaDeviation = new ProfilerCounter(Category, "Input Recv Delta Dev", ProfilerMarkerDataUnit.Count); +} +#endif \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/FusionProfiler.cs.meta b/Assets/Photon/Fusion/Scripts/FusionProfiler.cs.meta new file mode 100644 index 0000000..6612786 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionProfiler.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6da82913aa7052e4caacb038d2db1f11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats.cs b/Assets/Photon/Fusion/Scripts/FusionStats.cs new file mode 100644 index 0000000..2ade1ad --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats.cs @@ -0,0 +1,1626 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.EventSystems; +using UI = UnityEngine.UI; +using Fusion; +using Stats = Fusion.Simulation.Statistics; +using Fusion.StatsInternal; + +#if UNITY_EDITOR +using UnityEditor; +#endif + +/// +/// Creates and controls a Canvas with one or multiple telemetry graphs. Can be created as a scene object or prefab, +/// or be created at runtime using the methods. If created as the child of a +/// then will automatically be set to true. +/// +[ScriptHelp(BackColor = EditorHeaderBackColor.Olive)] +[ExecuteAlways] +public class FusionStats : Fusion.Behaviour { + +#if UNITY_EDITOR + + [MenuItem("Fusion/Add Fusion Stats", false, 1000)] + [MenuItem("GameObject/Fusion/Add Fusion Stats")] + public static void AddFusionStatsToScene() { + + var selected = Selection.activeGameObject; + + if (selected && PrefabUtility.IsPartOfPrefabAsset(selected)) { + Debug.LogWarning("Open prefabs before running 'Add Fusion Stats' on them."); + return; + } + + var fs = new GameObject("FusionStats"); + + if (selected) { + fs.transform.SetParent(Selection.activeGameObject.transform); + } + + fs.transform.localPosition = default; + fs.transform.localRotation = default; + fs.transform.localScale = Vector3.one; + + fs.AddComponent(); + fs.AddComponent(); + EditorGUIUtility.PingObject(fs.gameObject); + Selection.activeGameObject = fs.gameObject; + } + +#endif + + /// + /// Options for displaying stats as screen overlays or world GameObjects. + /// + public enum StatCanvasTypes { + Overlay, + GameObject, + } + + /// + /// Predefined layout default options. + /// + public enum DefaultLayouts { + Custom, + Left, + Right, + UpperLeft, + UpperRight, + Full, + } + + + // Lookup for all FusionStats associated with active runners. + static Dictionary> _statsForRunnerLookup = new Dictionary>(); + + // Record of active SimStats, used to prevent more than one _guid version from existing (in the case of SimStats existing in a scene that gets cloned in Multi-Peer). + static Dictionary _activeGuids = new Dictionary(); + + // Added to make calling by reflection cleaner internally. Used in RunnerVisibilityControls. + internal static FusionStats CreateInternal(NetworkRunner runner = null, DefaultLayouts layout = DefaultLayouts.Left, Stats.NetStatFlags? netStatsMask = null, Stats.SimStatFlags? simStatsMask = null) { + return Create(null, runner, layout, layout, netStatsMask, simStatsMask); + } + + /// + /// Creates a new GameObject with a component, attaches it to any supplied parent, and generates Canvas/Graphs. + /// + /// + /// Generated FusionStats component and GameObject will be added as a child of this transform. + /// Uses a predefined position. + /// The network stats to be enabled. If left null, default statistics will be used. + /// The simulation stats to be enabled. If left null, default statistics will be used. + /// + public static FusionStats Create(Transform parent = null, NetworkRunner runner = null, DefaultLayouts? screenLayout = null, DefaultLayouts? objectLayout = null, Stats.NetStatFlags? netStatsMask = null, Stats.SimStatFlags? simStatsMask = null) { + + var go = new GameObject($"{nameof(FusionStats)} {(runner ? runner.name : "null")}"); + FusionStats stats; + if (parent) { + go.transform.SetParent(parent); + } + + stats = go.AddComponent(); + + stats.ResetInternal(null, netStatsMask, simStatsMask, objectLayout, screenLayout); + + stats.Runner = runner; + + if (runner != null) { + stats.AutoDestroy = true; + } + return stats; + } + + public static Stats.NetStatFlags DefaultNetStatsMask => Stats.NetStatFlags.RoundTripTime | Stats.NetStatFlags.ReceivedPacketSizes | Stats.NetStatFlags.SentPacketSizes; + + /// + /// The gets the default SimStats. Some are only intended for Fusion internal development and aren't useful to users. + /// +#if FUSION_DEV + public const Stats.SimStatFlags DefaultSimStatsMask = (Stats.SimStatFlags)(-1); +#else + public const Stats.SimStatFlags DefaultSimStatsMask = + ~( + Stats.SimStatFlags.InterpDiff | + Stats.SimStatFlags.InterpUncertainty | + Stats.SimStatFlags.InterpMultiplier | + Stats.SimStatFlags.InputOffsetTarget | + Stats.SimStatFlags.InputOffsetDeviation | + Stats.SimStatFlags.InputReceiveDeltaDeviation + ); +#endif + + + const int SCREEN_SCALE_W = 1080; + const int SCREEN_SCALE_H = 1080; + const float TEXT_MARGIN = 0.25f; + const float TITLE_HEIGHT = 20f; + const int MARGIN = FusionStatsUtilities.MARGIN; + const int PAD = FusionStatsUtilities.PAD; + + const string PLAY_TEXT = "PLAY"; + const string PAUS_TEXT = "PAUSE"; + const string SHOW_TEXT = "SHOW"; + const string HIDE_TEXT = "HIDE"; + const string CLER_TEXT = "CLEAR"; + const string CNVS_TEXT = "CANVAS"; + const string CLSE_TEXT = "CLOSE"; + + const string PLAY_ICON = "\u25ba"; + const string PAUS_ICON = "\u05f0"; + const string HIDE_ICON = "\u25bc"; + const string SHOW_ICON = "\u25b2"; + const string CLER_ICON = "\u1d13"; + const string CNVS_ICON = "\ufb26"; //"\u2261"; + const string CLSE_ICON = "x"; + + // Used by DrawIfAttribute to determine inspector visibility of fields are runtime. + bool ShowColorControls => !Application.isPlaying && _modifyColors; + bool IsPlaying => Application.isPlaying; + + + /// + /// Interval (in seconds) between Graph redraws. Higher values (longer intervals) reduce CPU overhead, draw calls and garbage collection. + /// + [InlineHelp] + [Unit(Units.Seconds, 1f, 0f, DecimalPlaces = 2)] + public float RedrawInterval = .1f; + + + /// + /// Selects between displaying Canvas as screen overlay, or a world GameObject. + /// + [Header("Layout")] + + [InlineHelp] + [SerializeField] + StatCanvasTypes _canvasType; + /// + /// Selects between displaying Canvas as screen overlay, or a world GameObject. + /// + public StatCanvasTypes CanvasType { + get => _canvasType; + set { + _canvasType = value; + //_canvas.enabled = false; + DirtyLayout(2); + } + } + + /// + /// Enables text labels for the control buttons. + /// + [InlineHelp] + [SerializeField] + bool _showButtonLabels = true; + /// + /// Enables text labels for the control buttons. + /// + public bool ShowButtonLabels { + get => _showButtonLabels; + set { + _showButtonLabels = value; + DirtyLayout(); + } + } + + + /// + /// Height of button region at top of the stats panel. Values less than or equal to 0 hide the buttons, and reduce the header size. + /// + [InlineHelp] + [SerializeField] + [Range(0, 200)] + int _maxHeaderHeight = 80; + /// + /// Height of button region at top of the stats panel. Values less than or equal to 0 hide the buttons, and reduce the header size. + /// + public int MaxHeaderHeight { + get => _maxHeaderHeight; + set { + _maxHeaderHeight = value; + DirtyLayout(); + } + } + + /// + /// The size of the canvas when is set to . + /// + [InlineHelp] + [DrawIf(nameof(_canvasType), (long)StatCanvasTypes.GameObject, DrawIfHideType.Hide)] + [Range(0, 20f)] + public float CanvasScale = 5f; + + /// + /// The distance on the Z axis the canvas will be positioned. Allows moving the canvas in front of or behind the parent GameObject. + /// + [InlineHelp] + [DrawIf(nameof(_canvasType), (long)StatCanvasTypes.GameObject, DrawIfHideType.Hide)] + [Range(-10, 10f)] + public float CanvasDistance = 0f; + + /// + /// The Rect which defines the position of the stats canvas on a GameObject. Sizes are normalized percentages.(ranges of 0f-1f). + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(CanvasType), (long)StatCanvasTypes.GameObject, DrawIfHideType.Hide)] + [NormalizedRect(aspectRatio: 1)] + Rect _gameObjectRect = new Rect(0.0f, 0.0f, 0.3f, 1.0f); + public Rect GameObjectRect { + get => _gameObjectRect; + set { + _gameObjectRect = value; + DirtyLayout(); + } + } + + + + /// + /// The Rect which defines the position of the stats canvas overlay on the screen. Sizes are normalized percentages.(ranges of 0f-1f). + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(CanvasType), (long)StatCanvasTypes.Overlay, DrawIfHideType.Hide)] + [NormalizedRect] + Rect _overlayRect = new Rect(0.0f, 0.0f, 0.3f, 1.0f); + public Rect OverlayRect { + get => _overlayRect; + set { + _overlayRect = value; + DirtyLayout(); + } + } + + + /// + /// value which all child components will use if their value is set to Auto. + /// + [Header("Fusion Graphs Layout")] + [InlineHelp] + [SerializeField] + FusionGraph.Layouts _defaultLayout; + public FusionGraph.Layouts DefaultLayout { + get => _defaultLayout; + set { + _defaultLayout = value; + DirtyLayout(); + } + } + + /// + /// UI Text on FusionGraphs can only overlay the bar graph if the canvas is perfectly facing the camera. + /// Any other angles will result in ZBuffer fighting between the text and the graph bar shader. + /// For uses where perfect camera billboarding is not possible (such as VR), this toggle prevents FusionGraph layouts being used where text and graphs overlap. + /// Normally leave this unchecked, unless you are experiencing corrupted text rendering. + /// + [InlineHelp] + [SerializeField] + bool _noTextOverlap; + public bool NoTextOverlap { + get => _noTextOverlap; + set { + _noTextOverlap = value; + DirtyLayout(); + } + } + + /// + /// Disables the bar graph in , and uses a text only layout. + /// Enable this if is not rendering correctly in VR. + /// + [InlineHelp] + [SerializeField] + bool _noGraphShader; + public bool NoGraphShader { + get => _noGraphShader; + set { + _noGraphShader = value; + DirtyLayout(); + } + } + + /// + /// Force graphs layout to use X number of columns. + /// + [InlineHelp] + [Range(0, 16)] + public int GraphColumnCount = 1; + + /// + /// If is set to zero, then columns will automatically be added as needed to limit graphs to this width or less. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(GraphColumnCount), compareToValue: (long)0, DrawIfHideType.ReadOnly)] + [Range(30, SCREEN_SCALE_W)] + int _graphMaxWidth = SCREEN_SCALE_W / 4; + + /// + /// If is set to zero, then columns will automatically be added as needed to limit graphs to this width or less. + /// + public int GraphMaxWidth { + get => _graphMaxWidth; + set { + _graphMaxWidth = value; + DirtyLayout(); + } + } + + /// + /// Enables/Disables all NetworkObject related elements. + /// + [Header("Network Object Stats")] + [InlineHelp] + [SerializeField] + [WarnIf(nameof(ShowMissingNetObjWarning), true, "No NetworkObject found on this GameObject, nor parent. Object stats will be unavailable.")] + bool _enableObjectStats; + public bool EnableObjectStats { + get => _enableObjectStats; + set { + _enableObjectStats = value; + DirtyLayout(); + } + } + + bool ShowMissingNetObjWarning { + get => _enableObjectStats && this.Object == null; + } + + /// + /// The source for any specific telemetry. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(EnableObjectStats), true)] + NetworkObject _object; + public NetworkObject Object { + get { + if (_object == null) { + _object = GetComponentInParent(); + } + return _object; + } + } + + /// + /// Height of Object title region at top of the stats panel. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(EnableObjectStats), true)] + [Range(0, 200)] + int _objectTitleHeight = 48; + public int ObjectTitleHeight { + get => _objectTitleHeight; + set { + _objectTitleHeight = value; + DirtyLayout(); + } + } + + /// + /// Height of Object info region at top of the stats panel. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(EnableObjectStats), true)] + [Range(0, 200)] + int _objectIdsHeight = 60; + public int ObjectIdsHeight { + get => _objectIdsHeight; + set { + _objectIdsHeight = value; + DirtyLayout(); + } + } + + /// + /// Height of Object info region at top of the stats panel. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(EnableObjectStats), true)] + [Range(0, 200)] + int _objectMetersHeight = 90; + public int ObjectMetersHeight { + get => _objectMetersHeight; + set { + _objectIdsHeight = value; + DirtyLayout(); + } + } + + /// + /// The currently associated with this component and graphs. + /// + [Header("Data")] + [SerializeField] + [InlineHelp] + [EditorDisabled] + NetworkRunner _runner; + public NetworkRunner Runner { + get { + + if (Application.isPlaying == false) { + return null; + } + + // If the current runner shutdown, reset the runner so a new one can be found + if (_runner) { + if (_runner.IsShutdown) { + Runner = null; + } else { + return _runner; + } + } + + if (Object) { + var runner = _object.Runner; + + if (runner && (!EnforceSingle || (runner.Mode & ConnectTo) != 0)) { + Runner = runner; + return _runner; + } + } + + FusionStatsUtilities.TryFindActiveRunner(this, out var found, ConnectTo); + + Runner = found; + return found; + } + set { + if (_runner == value) { + return; + } + // Keep track of which runners have active stats windows - needed so pause/unpause can affect all (since pause affects other panels) + DisassociateWithRunner(_runner); + _runner = value; + AssociateWithRunner(value); + + UpdateTitle(); + } + } + + /// + /// Initializes a for all available stats, even if not initially included. + /// If disabled, graphs added after initialization will be added to the bottom of the interface stack. + /// + [InlineHelp] + public bool InitializeAllGraphs; + + /// + /// When is null and no exists in the current scene, FusionStats will continuously attempt to find and connect to an active which matches these indicated modes. + /// + [InlineHelp] + [VersaMask] + public SimulationModes ConnectTo = /*SimulationModes.Host | SimulationModes.Server | */SimulationModes.Client; + + /// + /// Selects which NetworkObject stats should be displayed. + /// + [InlineHelp] + [SerializeField] + [VersaMask] + [DrawIf(nameof(EnableObjectStats), true)] + Stats.ObjStatFlags _includedObjStats; + public Stats.ObjStatFlags IncludedObjectStats { + get => _includedObjStats; + set { + _includedObjStats = value; + _activeDirty = true; + } + } + + /// + /// Selects which NetConnection stats should be displayed. + /// + [InlineHelp] + [SerializeField] + [VersaMask] + Stats.NetStatFlags _includedNetStats; + public Stats.NetStatFlags IncludedNetStats { + get => _includedNetStats; + set { + _includedNetStats = value; + _activeDirty = true; + } + } + + /// + /// Selects which Simulation stats should be displayed. + /// + [InlineHelp] + [SerializeField] + [VersaMask] + Stats.SimStatFlags _includedSimStats; + public Stats.SimStatFlags IncludedSimStats { + get => _includedSimStats; + set { + _includedSimStats = value; + _activeDirty = true; + } + } + + /// + /// Automatically destroys this GameObject if the associated runner is null or inactive. + /// Otherwise attempts will continuously be made to find an new active runner which is running in specified by , and connect to that. + /// + [Header("Life-Cycle")] + [InlineHelp] + [SerializeField] + public bool AutoDestroy; + + /// + /// Only one instance with the can exist. Will destroy any clones on Awake. + /// + [InlineHelp] + [SerializeField] + public bool EnforceSingle = true; + + /// + /// Identifier used to enforce single instances of when running in Multi-Peer mode. + /// When is enabled, only one instance of with this GUID will be active at any time, + /// regardless of the total number of peers running. + /// + [InlineHelp] + [DrawIf(nameof(EnforceSingle), true)] + [SerializeField] + public string Guid; + + [Header("Customization")] + /// + /// Shows/hides controls in the inspector for defining element colors. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(IsPlaying), false, DrawIfHideType.Hide)] + private bool _modifyColors; + public bool ModifyColors => _modifyColors; + + /// + /// The color used for the telemetry graph data. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _graphColorGood = new Color(0.1f, 0.5f, 0.1f, 0.9f); + + /// + /// The color used for the telemetry graph data. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _graphColorWarn = new Color(0.75f, 0.75f, 0.2f, 0.9f); + + /// + /// The color used for the telemetry graph data. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _graphColorBad = new Color(0.9f, 0.2f, 0.2f, 0.9f); + + /// + /// The color used for the telemetry graph data. + /// + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _graphColorFlag = new Color(0.8f, 0.75f, 0.0f, 1.0f); + + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _fontColor = new Color(1.0f, 1.0f, 1.0f, 1f); + + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color PanelColor = new Color(0.3f, 0.3f, 0.3f, 0.9f); + + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _simDataBackColor = new Color(0.1f, 0.08f, 0.08f, 1.0f); + + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _netDataBackColor = new Color(0.15f, 0.14f, 0.09f, 1.0f); + + [InlineHelp] + [SerializeField] + [DrawIf(nameof(ShowColorControls), true, DrawIfHideType.Hide)] + Color _objDataBackColor = new Color(0.0f, 0.2f, 0.4f, 1.0f); + + // IFusionStats interface requirements + public Color FontColor => _fontColor; + public Color GraphColorGood => _graphColorGood; + public Color GraphColorWarn => _graphColorWarn; + public Color GraphColorBad => _graphColorBad; + public Color GraphColorFlag => _graphColorFlag; + public Color SimDataBackColor => _simDataBackColor; + public Color NetDataBackColor => _netDataBackColor; + public Color ObjDataBackColor => _objDataBackColor; + + //[Header("Graph Connections")] + [SerializeField] [HideInInspector] FusionGraph[] _simGraphs; + [SerializeField] [HideInInspector] FusionGraph[] _objGraphs; + [SerializeField] [HideInInspector] FusionGraph[] _netGraphs; + [NonSerialized] List _foundViews; + [NonSerialized] List _foundGraphs; + + [SerializeField] [HideInInspector] UI.Text _titleText; + + [SerializeField] [HideInInspector] UI.Text _clearIcon; + [SerializeField] [HideInInspector] UI.Text _pauseIcon; + [SerializeField] [HideInInspector] UI.Text _togglIcon; + [SerializeField] [HideInInspector] UI.Text _closeIcon; + [SerializeField] [HideInInspector] UI.Text _canvsIcon; + + [SerializeField] [HideInInspector] UI.Text _clearLabel; + [SerializeField] [HideInInspector] UI.Text _pauseLabel; + [SerializeField] [HideInInspector] UI.Text _togglLabel; + [SerializeField] [HideInInspector] UI.Text _closeLabel; + [SerializeField] [HideInInspector] UI.Text _canvsLabel; + [SerializeField] [HideInInspector] UI.Text _objectNameText; + + [SerializeField] [HideInInspector] UI.GridLayoutGroup _graphGridLayoutGroup; + + [SerializeField] [HideInInspector] Canvas _canvas; + [SerializeField] [HideInInspector] RectTransform _canvasRT; + [SerializeField] [HideInInspector] RectTransform _rootPanelRT; + [SerializeField] [HideInInspector] RectTransform _guidesRT; + [SerializeField] [HideInInspector] RectTransform _headerRT; + [SerializeField] [HideInInspector] RectTransform _statsPanelRT; + [SerializeField] [HideInInspector] RectTransform _graphsLayoutRT; + [SerializeField] [HideInInspector] RectTransform _titleRT; + [SerializeField] [HideInInspector] RectTransform _buttonsRT; + [SerializeField] [HideInInspector] RectTransform _objectTitlePanelRT; + [SerializeField] [HideInInspector] RectTransform _objectIdsGroupRT; + [SerializeField] [HideInInspector] RectTransform _objectMetersPanelRT; + [SerializeField] [HideInInspector] RectTransform _clientIdPanelRT; + [SerializeField] [HideInInspector] RectTransform _authorityPanelRT; + + [SerializeField] [HideInInspector] UI.Button _titleButton; + [SerializeField] [HideInInspector] UI.Button _objctButton; + [SerializeField] [HideInInspector] UI.Button _clearButton; + [SerializeField] [HideInInspector] UI.Button _togglButton; + [SerializeField] [HideInInspector] UI.Button _pauseButton; + [SerializeField] [HideInInspector] UI.Button _closeButton; + [SerializeField] [HideInInspector] UI.Button _canvsButton; + + public Rect CurrentRect => _canvasType == StatCanvasTypes.GameObject ? _gameObjectRect : _overlayRect; + + void UpdateTitle() { + var runnername = _runner ? _runner.name : "Disconnected"; + if (_titleText) { + _titleText.text = runnername; + } + } + + Shader Shader { + get => Resources.Load("FusionGraphShader"); + } + + Font _font; + bool _hidden; + bool _paused; + int _layoutDirty; + bool _activeDirty; + + double _currentDrawTime; + double _delayDrawUntil; + + void DirtyLayout(int minimumRefreshes = 1) { + if (_layoutDirty < minimumRefreshes) { + _layoutDirty = minimumRefreshes; + } + } + +#if UNITY_EDITOR + void OnValidate() { + + if (EnforceSingle && Guid == "") { + Guid = System.Guid.NewGuid().ToString().Substring(0, 13); + } + _activeDirty = true; + if (_layoutDirty <= 0) { + _layoutDirty = 2; + + // Some aspects of Layout will throw warnings if run from OnValidate, so defer. + // Stop deferring when entering play mode, as this will cause null errors (thanks unity). + if (Application.isPlaying) { + UnityEditor.EditorApplication.delayCall += CalculateLayout; + } else { + UnityEditor.EditorApplication.delayCall -= CalculateLayout; + } + } + } + + void Reset() { + ResetInternal(); + } + +#endif + + void ResetInternal( + bool? enableObjectStats = null, + Stats.NetStatFlags? netStatsMask = null, + Stats.SimStatFlags? simStatsMask = null, + DefaultLayouts? objectLayout = null, + DefaultLayouts? screenLayout = null + ) { + // Destroy existing built graphs + var canv = GetComponentInChildren(); + if (canv) { + DestroyImmediate(canv.gameObject); + } + + if (TryGetComponent(out var _) == false) { + gameObject.AddComponent().UpdateLookAt(); + } + + bool hasNetworkObject = GetComponentInParent(); + // If attached to a NetObject + if (enableObjectStats.GetValueOrDefault() || (enableObjectStats.GetValueOrDefault(true) && hasNetworkObject)) { + EnableObjectStats = true; + _includedObjStats = Stats.ObjStatFlags.Buffer; + _includedSimStats = simStatsMask.GetValueOrDefault(); + _includedNetStats = netStatsMask.GetValueOrDefault(); + _canvasType = StatCanvasTypes.GameObject; + EnforceSingle = false; + GraphColumnCount = 1; + } + else { + // If not attached to a GameObject (sim only) + + GraphColumnCount = 0; + + if (transform.parent) { + _canvasType = StatCanvasTypes.GameObject; + EnforceSingle = false; + } else { + _canvasType = StatCanvasTypes.Overlay; + EnforceSingle = true; + } + _includedSimStats = simStatsMask.GetValueOrDefault(DefaultSimStatsMask); + _includedNetStats = netStatsMask.GetValueOrDefault( + Stats.NetStatFlags.RoundTripTime | + Stats.NetStatFlags.SentPacketSizes | + Stats.NetStatFlags.ReceivedPacketSizes); + + } + + + ApplyDefaultLayout(objectLayout.GetValueOrDefault(hasNetworkObject ? DefaultLayouts.UpperRight : DefaultLayouts.Full), StatCanvasTypes.GameObject); + ApplyDefaultLayout(screenLayout.GetValueOrDefault(DefaultLayouts.Right), StatCanvasTypes.Overlay); + + Guid = System.Guid.NewGuid().ToString().Substring(0, 13); + GenerateGraphs(); + } + + void Awake() { + +#if !UNITY_EDITOR + if (_guidesRT) { + Destroy(_guidesRT.gameObject); + } +#endif + + if (Application.isPlaying == false) { +#if UNITY_EDITOR + if (_canvas) { + //// Hide canvas for rebuild, Unity makes this ugly. + if (EditorApplication.isCompiling == false) { + //_canvas.enabled = false; + UnityEditor.EditorApplication.delayCall += CalculateLayout; + + } + _layoutDirty = 2; + //CalculateLayout(); + } + return; +#endif + + } else { + _foundViews = new List(); + GetComponentsInChildren(true, _foundViews); + + } + + if (Guid == "") { + Guid = System.Guid.NewGuid().ToString().Substring(0, 13); + } + + if (EnforceSingle && Guid != null) { + if (_activeGuids.ContainsKey(Guid)) { + Destroy(this.gameObject); + return; + } + _activeGuids.Add(Guid, this); + } + + if (EnforceSingle && transform.parent == null && _canvasType == StatCanvasTypes.Overlay) { + DontDestroyOnLoad(gameObject); + } + } + + void Start() { + if (Application.isPlaying) { + Initialize(); + _activeDirty = true; + _layoutDirty = 2; + //_canvas.enabled = false; + + } + } + + void OnDestroy() { + // Try to unregister this Stats in case it hasn't already. + DisassociateWithRunner(_runner); + + // If this is the current enforce single instance of this GUID, remove it from the record. + if (Guid != null) { + if (_activeGuids.TryGetValue(Guid, out var stats)) { + if (stats == this) { + _activeGuids.Remove(Guid); + } + } + } + } + + [BehaviourButtonAction("Destroy Graphs", conditionMember: nameof(_canvasRT), ConditionFlags = BehaviourActionAttribute.ActionFlags.ShowAtNotRuntime)] + void DestroyGraphs() { + if (_canvasRT) { + DestroyImmediate(_canvasRT.gameObject); + } + _canvasRT = null; + } + + + void Initialize() { + + // Only add an event system if no active event systems exist. + if (Application.isPlaying && FindObjectOfType() == null) { + var eventSystemGO = new GameObject("Event System"); + eventSystemGO.AddComponent(); + eventSystemGO.AddComponent(); + if (Application.isPlaying) { + DontDestroyOnLoad(eventSystemGO); + } + } + + if (_canvasRT == false) { + GenerateGraphs(); + } + + // Already existed before runtime. (Scene object) + if (_canvasRT) { + // Listener connections are not retained with serialization and always need to be connected at startup. + // Remove listeners in case this is a copy of a runtime generated graph. + _togglButton?.onClick.RemoveListener(Toggle); + _canvsButton?.onClick.RemoveListener(ToggleCanvasType); + _clearButton?.onClick.RemoveListener(Clear); + _pauseButton?.onClick.RemoveListener(Pause); + _closeButton?.onClick.RemoveListener(Close); + _titleButton?.onClick.RemoveListener(PingSelectFusionStats); + _objctButton?.onClick.RemoveListener(PingSelectObject); + + _togglButton?.onClick.AddListener(Toggle); + _canvsButton?.onClick.AddListener(ToggleCanvasType); + _clearButton?.onClick.AddListener(Clear); + _pauseButton?.onClick.AddListener(Pause); + _closeButton?.onClick.AddListener(Close); + _titleButton?.onClick.AddListener(PingSelectFusionStats); + _objctButton?.onClick.AddListener(PingSelectObject); + // Run Unity first frame layout failure hack. + + GetComponentsInChildren(true, _foundViews); + + foreach (var g in _foundViews) { + g.Initialize(); + } + + _layoutDirty = 1; + return; + } + } + + bool _graphsAreMissing => _canvasRT == null; + + [BehaviourButtonAction("Generate Graphs", conditionMember: nameof(_graphsAreMissing), ConditionFlags = BehaviourActionAttribute.ActionFlags.ShowAtNotRuntime)] + void GenerateGraphs() { + var rootRectTr = gameObject.GetComponent(); + _canvasRT = rootRectTr.CreateRectTransform("Stats Canvas"); + _canvas = _canvasRT.gameObject.AddComponent(); + _canvas.renderMode = RenderMode.ScreenSpaceOverlay; + + // If the runner has already started, the root FusionStats has been added to the VisNodes registration for the runner, + // But any generated children GOs here will not. Add the generated components to the visibility system. + if (Runner && Runner.IsRunning) { + RunnerVisibilityNode.AddVisibilityNodes(_canvasRT.gameObject, Runner); + } + var scaler = _canvasRT.gameObject.AddComponent(); + scaler.uiScaleMode = UI.CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(SCREEN_SCALE_W, SCREEN_SCALE_H); + scaler.matchWidthOrHeight = .4f; + + _canvasRT.gameObject.AddComponent(); + +#if UNITY_EDITOR + _guidesRT = _canvasRT.MakeGuides(); +#endif + + _rootPanelRT = _canvasRT + .CreateRectTransform("Root Panel"); + + _headerRT = _rootPanelRT + .CreateRectTransform("Header Panel") + .AddCircleSprite(PanelColor); + + _titleRT = _headerRT + .CreateRectTransform("Runner Title") + .SetAnchors(0.0f, 1.0f, 0.75f, 1.0f) + .SetOffsets(MARGIN, -MARGIN, 0.0f, -MARGIN); + + _titleButton = _titleRT.gameObject.AddComponent(); + _titleText = _titleRT.AddText(_runner ? _runner.name : "Disconnected", TextAnchor.UpperCenter, _fontColor); + _titleText.raycastTarget = true; + + // Buttons + _buttonsRT = _headerRT + .CreateRectTransform("Buttons") + .SetAnchors(0.0f, 1.0f, 0.0f, 0.75f) + .SetOffsets(MARGIN, -MARGIN, MARGIN, 0); + + var buttonsGrid = _buttonsRT.gameObject.AddComponent(); + buttonsGrid.childControlHeight = true; + buttonsGrid.childControlWidth = true; + buttonsGrid.spacing = MARGIN; + _buttonsRT.MakeButton(ref _togglButton, HIDE_ICON, HIDE_TEXT, out _togglIcon, out _togglLabel, Toggle); + _buttonsRT.MakeButton(ref _canvsButton, CNVS_ICON, CNVS_TEXT, out _canvsIcon, out _canvsLabel, ToggleCanvasType); + _buttonsRT.MakeButton(ref _pauseButton, PAUS_ICON, PAUS_TEXT, out _pauseIcon, out _pauseLabel, Pause); + _buttonsRT.MakeButton(ref _clearButton, CLER_ICON, CLER_TEXT, out _clearIcon, out _clearLabel, Clear); + _buttonsRT.MakeButton(ref _closeButton, CLSE_ICON, CLSE_TEXT, out _closeIcon, out _closeLabel, Close); + + // Minor tweak to foldout arrow icon, since its too tall. + _togglIcon.rectTransform.anchorMax = new Vector2(1, 0.85f); + + // Stats stack + + _statsPanelRT = _rootPanelRT + .CreateRectTransform("Stats Panel") + .AddCircleSprite(PanelColor); + + // Object Name, IDs and Meters + + _objectTitlePanelRT = _statsPanelRT + .CreateRectTransform("Object Name Panel") + .ExpandTopAnchor(MARGIN) + .AddCircleSprite(_objDataBackColor); + + _objctButton = _objectTitlePanelRT.gameObject.AddComponent(); + + var objectTitleRT = _objectTitlePanelRT + .CreateRectTransform("Object Name") + .SetAnchors(0.0f, 1.0f, 0.15f, 0.85f) + .SetOffsets(PAD, -PAD, 0, 0); + + _objectNameText = objectTitleRT.AddText("Object Name", TextAnchor.MiddleCenter, _fontColor); + _objectNameText.alignByGeometry = false; + _objectNameText.raycastTarget = false; + + _objectIdsGroupRT = FusionStatsObjectIds.Create(_statsPanelRT, this); + + _objectMetersPanelRT = _statsPanelRT + .CreateRectTransform("Object Meters Layout") + .ExpandTopAnchor(MARGIN) + .AddVerticalLayoutGroup(MARGIN); + + FusionStatsMeterBar.Create(_objectMetersPanelRT, this, Stats.StatSourceTypes.NetworkObject, (int)Stats.ObjStats.Bandwidth, 15, 30); + FusionStatsMeterBar.Create(_objectMetersPanelRT, this, Stats.StatSourceTypes.NetworkObject, (int)Stats.ObjStats.RPC, 3, 6); + + // Graphs + _graphsLayoutRT = _statsPanelRT + .CreateRectTransform("Graphs Layout") + .ExpandAnchor() + .SetOffsets(MARGIN, 0,0,0); + + //.AddGridlLayoutGroup(MRGN); + _graphGridLayoutGroup = _graphsLayoutRT.AddGridlLayoutGroup(MARGIN); + + _objGraphs = new FusionGraph[Stats.OBJ_STAT_TYPE_COUNT]; + for (int i = 0; i < Stats.OBJ_STAT_TYPE_COUNT; ++i) { + if (InitializeAllGraphs == false) { + var statFlag = (Stats.ObjStatFlags)(1 << i); + if ((statFlag & _includedObjStats) == 0) { + continue; + } + } + CreateGraph(Stats.StatSourceTypes.NetworkObject, i, _graphsLayoutRT); + } + + _netGraphs = new FusionGraph[Stats.NET_STAT_TYPE_COUNT]; + for (int i = 0; i < Stats.NET_STAT_TYPE_COUNT; ++i) { + if (InitializeAllGraphs == false) { + var statFlag = (Stats.NetStatFlags)(1 << i); + if ((statFlag & _includedNetStats) == 0) { + continue; + } + } + CreateGraph(Stats.StatSourceTypes.NetConnection, i, _graphsLayoutRT); + } + + _simGraphs = new FusionGraph[Stats.SIM_STAT_TYPE_COUNT]; + for (int i = 0; i < Stats.SIM_STAT_TYPE_COUNT; ++i) { + if (InitializeAllGraphs == false) { + var statFlag = (Stats.SimStatFlags)(1 << i); + if ((statFlag & _includedSimStats) == 0) { + continue; + } + } + CreateGraph(Stats.StatSourceTypes.Simulation, i, _graphsLayoutRT); + } + + // Hide canvas for a tick. Unity makes some ugliness on the first update. + //_canvas.enabled = false; + _activeDirty = true; + + _layoutDirty = 2; + } + + void AssociateWithRunner(NetworkRunner runner) { + if (runner != null) { + if (_statsForRunnerLookup.TryGetValue(runner, out var runnerStats) == false) { + _statsForRunnerLookup.Add(runner, new List() { this }); + } else { + runnerStats.Add(this); + } + } + } + + void DisassociateWithRunner(NetworkRunner runner) { + if (runner != null && _statsForRunnerLookup.TryGetValue(runner, out var oldrunnerstats)) { + if (oldrunnerstats.Contains(this)) { + oldrunnerstats.Remove(this); + } + } + } + + void Pause() { + if (_runner && _runner.Simulation != null) { + _paused = !_paused; + + var icon = _paused ? PLAY_ICON : PAUS_ICON; + var label = _paused ? PLAY_TEXT : PAUS_TEXT; + _pauseIcon.text = icon; + _pauseLabel.text = label; + + // Pause for all SimStats tied to this runner if all related FusionStats are paused. + if (_statsForRunnerLookup.TryGetValue(_runner, out var stats)) { + + bool statsAreBeingUsed = false; + foreach (var stat in stats) { + if (stat._paused == false) { + statsAreBeingUsed = true; + break; + } + } + _runner.Simulation.Stats.Pause(statsAreBeingUsed == false); + } + } + } + + void Toggle() { + _hidden = !_hidden; + + _togglIcon.text = _hidden ? SHOW_ICON : HIDE_ICON; + _togglLabel.text = _hidden ? SHOW_TEXT : HIDE_TEXT; + + _statsPanelRT.gameObject.SetActive(!_hidden); + + for (int i = 0; i < _simGraphs.Length; ++i) { + var graph = _simGraphs[i]; + if (graph) { + _simGraphs[i].gameObject.SetActive(!_hidden && (1 << i & (int)_includedSimStats) != 0); + } + } + for (int i = 0; i < _objGraphs.Length; ++i) { + var graph = _objGraphs[i]; + if (graph) { + _objGraphs[i].gameObject.SetActive(!_hidden && (1 << i & (int)_includedObjStats) != 0); + } + } + for (int i = 0; i < _netGraphs.Length; ++i) { + var graph = _netGraphs[i]; + if (graph) { + _netGraphs[i].gameObject.SetActive(!_hidden && (1 << i & (int)_includedNetStats) != 0); + } + } + } + + void Clear() { + if (_runner && _runner.Simulation != null) { + _runner.Simulation.Stats.Clear(); + } + + for (int i = 0; i < _simGraphs.Length; ++i) { + var graph = _simGraphs[i]; + if (graph) { + _simGraphs[i].Clear(); + } + } + for (int i = 0; i < _objGraphs.Length; ++i) { + var graph = _objGraphs[i]; + if (graph) { + _objGraphs[i].Clear(); + } + } + for (int i = 0; i < _netGraphs.Length; ++i) { + var graph = _netGraphs[i]; + if (graph) { + _netGraphs[i].Clear(); + } + } + } + + void ToggleCanvasType() { +#if UNITY_EDITOR + UnityEditor.EditorGUIUtility.PingObject(gameObject); + if (Selection.activeGameObject == null) { + Selection.activeGameObject = gameObject; + } +#endif + _canvasType = (_canvasType == StatCanvasTypes.GameObject) ? StatCanvasTypes.Overlay : StatCanvasTypes.GameObject; + //_canvas.enabled = false; + _layoutDirty = 3; + CalculateLayout(); + } + + void Close() { + Destroy(this.gameObject); + } + + void PingSelectObject() { + +#if UNITY_EDITOR + var obj = Object; + if (obj) { + EditorGUIUtility.PingObject(Object.gameObject); + Selection.activeGameObject = Object.gameObject; + } +#endif + } + + void PingSelectFusionStats() { + +#if UNITY_EDITOR + EditorGUIUtility.PingObject(gameObject); + Selection.activeGameObject = gameObject; +#endif + } + +#if UNITY_EDITOR + + private void OnDrawGizmos() { + AutoGuideVisibility(); + } + + void AutoGuideVisibility() { + if (_canvasRT == null) { + return; + } + + if (CanvasType == StatCanvasTypes.GameObject) { + if (_guidesRT == null) { + _guidesRT = FusionStatsUtilities.MakeGuides(_canvasRT); + } + if (Selection.activeGameObject == gameObject) { + _guidesRT.gameObject.SetActive(true); + _guidesRT.localRotation = default; + + } else { + _guidesRT.gameObject.SetActive(false); + + } + } else { + if (_guidesRT) { + DestroyImmediate(_guidesRT.gameObject); + } + } + } + +#endif + + void LateUpdate() { + + // Use of the Runner getter here is intentional - this forces a test of the existing Runner having gone null or inactive. + var runner = Runner; + bool runnerIsNull = runner == null; + + if (AutoDestroy && runnerIsNull) { + Destroy(this.gameObject); + return; + } + + if (_activeDirty) { + ReapplyEnabled(); + } + + if (_layoutDirty > 0) { + CalculateLayout(); + } + + if (Application.isPlaying == false) { + return; + } + + // NetConnection stats do not like being polled after shutdown and will throw assert fails. + if (runnerIsNull || runner.IsShutdown) { + return; + } + + if (_paused) { + return; + } + + // Cap redraw rate - rate of 0 = disabled. + if (RedrawInterval > 0) { + var currentime = Time.timeAsDouble; + if (currentime > _delayDrawUntil) { + _currentDrawTime = currentime; + while (_delayDrawUntil <= currentime) { + _delayDrawUntil += RedrawInterval; + } + } + + if (currentime != _currentDrawTime) { + return; + } + } + + if (EnableObjectStats) { + RefreshObjectValues(); + } + + foreach (var graph in _foundViews) { + if (graph != null && graph.isActiveAndEnabled) { + graph.Refresh(); + } + } + } + + string _previousObjectTitle; + + void RefreshObjectValues() { + + var obj = Object; + if (obj == null) { + return; + } + + var objectName = obj.name; + if (_previousObjectTitle != objectName) { + _objectNameText.text = objectName; + _previousObjectTitle = objectName; + } + } + + public FusionGraph CreateGraph(Stats.StatSourceTypes type, int statId, RectTransform parentRT) { + + var fg = FusionGraph.Create(this, type, statId, parentRT); + + if (type == Stats.StatSourceTypes.Simulation) { + _simGraphs[statId] = fg; + if (((int)_includedSimStats & (1 << statId)) == 0) { + fg.gameObject.SetActive(false); + } + } else if (type == Stats.StatSourceTypes.NetworkObject) { + _objGraphs[statId] = fg; + if (((int)_includedObjStats & (1 << statId)) == 0) { + fg.gameObject.SetActive(false); + } + } else { + _netGraphs[statId] = fg; + if (((int)_includedNetStats & (1 << statId)) == 0) { + fg.gameObject.SetActive(false); + } + } + + return fg; + } + + // returns true if a graph has been added. + void ReapplyEnabled() { + + _activeDirty = false; + + if (_simGraphs == null || _simGraphs.Length < 0) { + return; + } + + // This is null if the children were deleted. Stop execution, or new Graphs will be created without a parent. + if (_graphsLayoutRT == null) { + return; + } + + for (int i = 0; i < _simGraphs.Length; ++i) { + var graph = _simGraphs[i]; + bool enabled = ((Stats.SimStatFlags)(1 << i) & _includedSimStats) != 0; + if (graph == null) { + if (enabled) { + graph = CreateGraph(Stats.StatSourceTypes.Simulation, i, _graphsLayoutRT); + _simGraphs[i] = graph; + } else { + continue; + } + } + graph.gameObject.SetActive(enabled); + } + + for (int i = 0; i < _objGraphs.Length; ++i) { + var graph = _objGraphs[i]; + bool enabled = _enableObjectStats && ((Stats.ObjStatFlags)(1 << i) & _includedObjStats) != 0; + if (graph == null) { + if (enabled) { + graph = CreateGraph(Stats.StatSourceTypes.NetworkObject, i, _graphsLayoutRT); + _objGraphs[i] = graph; + } else { + continue; + } + } + + if (_objGraphs[i] != null) { + graph.gameObject.SetActive(enabled); + } + } + + for (int i = 0; i < _netGraphs.Length; ++i) { + var graph = _netGraphs[i]; + bool enabled = ((Stats.NetStatFlags)(1 << i) & _includedNetStats) != 0; + if (graph == null) { + if (enabled) { + graph = CreateGraph(Stats.StatSourceTypes.NetConnection, i, _graphsLayoutRT); + _netGraphs[i] = graph; + } else { + continue; + } + } + + if (_netGraphs[i] != null) { + graph.gameObject.SetActive(enabled); + } + } + } + + float _lastLayoutUpdate; + + void CalculateLayout() { + + if (_rootPanelRT == null || _graphsLayoutRT == null) { + return; + } + + if (_foundGraphs == null) { + _foundGraphs = new List(_graphsLayoutRT.GetComponentsInChildren(false)); + } else { + GetComponentsInChildren(false, _foundGraphs); + } + + // Don't count multiple executions of CalculateLayout in the same Update as reducing the dirty count. + // _layoutDirty can be set to values greater than 1 to force a recalculate for several consecutive Updates. + var time = Time.time; + + if (_lastLayoutUpdate < time) { + _layoutDirty--; + _lastLayoutUpdate = time; + + } + +#if UNITY_EDITOR + if (Application.isPlaying == false && _layoutDirty > 0) { + UnityEditor.EditorApplication.delayCall -= CalculateLayout; + UnityEditor.EditorApplication.delayCall += CalculateLayout; + } +#endif + + if (_layoutDirty <= 0 && _canvas.enabled == false) { + //_canvas.enabled = true; + } + + if (_rootPanelRT) { + +#if UNITY_EDITOR + AutoGuideVisibility(); +#endif + + var maxHeaderHeight = Math.Min(_maxHeaderHeight, _rootPanelRT.rect.width / 4); + + if (_canvasType == StatCanvasTypes.GameObject) { + _canvas.renderMode = RenderMode.WorldSpace; + var scale = CanvasScale / SCREEN_SCALE_H; // (1f / SCREEN_SCALE_H) * Scale; + _canvasRT.localScale = new Vector3(scale, scale, scale); + _canvasRT.sizeDelta = new Vector2(1024, 1024); + _canvasRT.localPosition = new Vector3(0, 0, CanvasDistance); + + // TODO: Cache this + if (_canvasRT.GetComponent() == false) { + _canvasRT.localRotation = default; + } + } else { + _canvas.renderMode = RenderMode.ScreenSpaceOverlay; + } + + _objectTitlePanelRT.gameObject.SetActive(_enableObjectStats); + _objectIdsGroupRT.gameObject.SetActive(_enableObjectStats); + _objectMetersPanelRT.gameObject.SetActive(_enableObjectStats); + + Vector2 icoMinAnchor; + + if (_showButtonLabels) { + icoMinAnchor = new Vector2(0.0f, FusionStatsUtilities.BTTN_LBL_NORM_HGHT * .5f); + } else { + icoMinAnchor = new Vector2(0.0f, 0.0f); + } + + _togglIcon.rectTransform.anchorMin = icoMinAnchor + new Vector2(0, .15f); + _canvsIcon.rectTransform.anchorMin = icoMinAnchor; + _clearIcon.rectTransform.anchorMin = icoMinAnchor; + _pauseIcon.rectTransform.anchorMin = icoMinAnchor; + _closeIcon.rectTransform.anchorMin = icoMinAnchor; + + _togglLabel.gameObject.SetActive(_showButtonLabels); + _canvsLabel.gameObject.SetActive(_showButtonLabels); + _clearLabel.gameObject.SetActive(_showButtonLabels); + _pauseLabel.gameObject.SetActive(_showButtonLabels); + _closeLabel.gameObject.SetActive(_showButtonLabels); + + var rect = CurrentRect; + + _rootPanelRT.anchorMax = new Vector2(rect.xMax, rect.yMax); + _rootPanelRT.anchorMin = new Vector2(rect.xMin, rect.yMin); + _rootPanelRT.sizeDelta = new Vector2(0.0f, 0.0f); + _rootPanelRT.pivot = new Vector2(0.5f, 0.5f); + _rootPanelRT.anchoredPosition3D = default; + + _headerRT.anchorMin = new Vector2(0.0f, 1); + _headerRT.anchorMax = new Vector2(1.0f, 1); + _headerRT.pivot = new Vector2(0.5f, 1); + _headerRT.anchoredPosition3D = default; + _headerRT.sizeDelta = new Vector2(0, /*TITLE_HEIGHT +*/ maxHeaderHeight); + + _objectTitlePanelRT.offsetMax = new Vector2(-MARGIN, -MARGIN); + _objectTitlePanelRT.offsetMin = new Vector2( MARGIN, -(ObjectTitleHeight)); + _objectIdsGroupRT.offsetMax = new Vector2(-MARGIN, -(ObjectTitleHeight + MARGIN)); + _objectIdsGroupRT.offsetMin = new Vector2( MARGIN, -(ObjectTitleHeight + ObjectIdsHeight)); + _objectMetersPanelRT.offsetMax = new Vector2(-MARGIN, -(ObjectTitleHeight + ObjectIdsHeight + MARGIN)); + _objectMetersPanelRT.offsetMin = new Vector2( MARGIN, -(ObjectTitleHeight + ObjectIdsHeight + ObjectMetersHeight )); + + // Disable object sections that have been minimized to 0 + _objectTitlePanelRT .gameObject.SetActive(EnableObjectStats && ObjectTitleHeight > 0); + _objectIdsGroupRT .gameObject.SetActive(EnableObjectStats && ObjectIdsHeight > 0); + _objectMetersPanelRT.gameObject.SetActive(EnableObjectStats && ObjectMetersHeight > 0); + + _statsPanelRT.ExpandAnchor().SetOffsets(0, 0, 0, -(/*TITLE_HEIGHT + */maxHeaderHeight)); + + if (_enableObjectStats && _statsPanelRT.rect.height < (ObjectTitleHeight + ObjectIdsHeight + ObjectMetersHeight)) { + _statsPanelRT.offsetMin = new Vector2(0.0f, _statsPanelRT.rect.height -(ObjectTitleHeight + ObjectIdsHeight + ObjectMetersHeight + MARGIN)); + } + + var graphColCount = GraphColumnCount > 0 ? GraphColumnCount : (int)(_graphsLayoutRT.rect.width / (_graphMaxWidth + MARGIN)); + if (graphColCount < 1) { + graphColCount = 1; + } + + var graphRowCount = (int)Math.Ceiling((double)_foundGraphs.Count / graphColCount); + if (graphRowCount < 1) { + graphRowCount = 1; + } + + if (graphRowCount == 1) { + graphColCount = _foundGraphs.Count; + } + + _graphGridLayoutGroup.constraint = UI.GridLayoutGroup.Constraint.FixedColumnCount; + _graphGridLayoutGroup.constraintCount = graphColCount; + + var cellwidth = _graphsLayoutRT.rect.width / graphColCount - MARGIN; + var cellheight = _graphsLayoutRT.rect.height / graphRowCount - (/*(graphRowCount - 1) **/ MARGIN); + + _graphGridLayoutGroup.cellSize = new Vector2(cellwidth, cellheight); + _graphsLayoutRT.offsetMax = new Vector2(0, _enableObjectStats ? -(ObjectTitleHeight + ObjectIdsHeight + ObjectMetersHeight + MARGIN) : -MARGIN); + + + if (_foundViews == null) { + _foundViews = new List(GetComponentsInChildren(false)); + } else { + GetComponentsInChildren(false, _foundViews); + } + + if (_objGraphs != null) { + // enabled/disable any object graphs based on _enabledObjectStats setting + foreach (var objGraph in _objGraphs) { + if (objGraph) { + objGraph.gameObject.SetActive(((int)_includedObjStats & (1 << objGraph.StatId)) != 0 && _enableObjectStats); + } + } + } + + for (int i = 0; i < _foundViews.Count; ++i) { + var graph = _foundViews[i]; + if (graph == null || graph.isActiveAndEnabled == false) { + continue; + } + graph.CalculateLayout(); + graph.transform.localRotation = default; + graph.transform.localScale = new Vector3(1, 1, 1); + } + } + } + + void ApplyDefaultLayout(DefaultLayouts defaults, StatCanvasTypes? applyForCanvasType = null) { + bool applyToGO = applyForCanvasType.HasValue == false || applyForCanvasType.Value == StatCanvasTypes.GameObject; + bool applyToOL = applyForCanvasType.HasValue == false || applyForCanvasType.Value == StatCanvasTypes.Overlay; + + if (defaults == DefaultLayouts.Custom) { + return; + } + + Rect screenrect; + Rect objectrect; + bool isTall; +#if UNITY_EDITOR + var currentRes = UnityEditor.Handles.GetMainGameViewSize(); + isTall = (currentRes.y > currentRes.x); +#else + isTall = Screen.height > Screen.width; +#endif + + switch (defaults) { + case DefaultLayouts.Left: { + objectrect = Rect.MinMaxRect(0.0f, 0.0f, 0.3f, 1.0f); + screenrect = objectrect; + break; + } + case DefaultLayouts.Right: { + objectrect = Rect.MinMaxRect(0.7f, 0.0f, 1.0f, 1.0f); + screenrect = objectrect; + break; + } + case DefaultLayouts.UpperLeft: { + objectrect = Rect.MinMaxRect(0.0f, 0.5f, 0.3f, 1.0f); + screenrect = isTall ? Rect.MinMaxRect(0.0f, 0.7f, 0.3f, 1.0f) : objectrect ; + break; + } + case DefaultLayouts.UpperRight: { + objectrect = Rect.MinMaxRect(0.7f, 0.5f, 1.0f, 1.0f); + screenrect = isTall ? Rect.MinMaxRect(0.7f, 0.7f, 1.0f, 1.0f) : objectrect; + break; + } + case DefaultLayouts.Full: { + objectrect = Rect.MinMaxRect(0.0f, 0.0f, 1.0f, 1.0f); + screenrect = objectrect; + break; + } + default: { + objectrect = Rect.MinMaxRect(0.0f, 0.5f, 0.3f, 1.0f); + screenrect = objectrect; + break; + } + } + + if (applyToGO) { + GameObjectRect = objectrect; + } + if (applyToOL) { + OverlayRect = screenrect; + } + + _layoutDirty += 1; + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/FusionStats.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats.cs.meta new file mode 100644 index 0000000..3a47a53 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d358eee53e80c8a45be7aae6cb5d4a4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats.meta b/Assets/Photon/Fusion/Scripts/FusionStats.meta new file mode 100644 index 0000000..2c1f615 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ee057786a0477a44792d4a96ac2f0a8f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs b/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs new file mode 100644 index 0000000..7d08371 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs @@ -0,0 +1,305 @@ +using System; +using UnityEngine; +using Fusion; +using UI = UnityEngine.UI; +using Stats = Fusion.Simulation.Statistics; +using Fusion.StatsInternal; + +[ScriptHelp(BackColor = EditorHeaderBackColor.Olive)] +public abstract class FusionGraphBase : Fusion.Behaviour, IFusionStatsView { + + protected const int PAD = FusionStatsUtilities.PAD; + protected const int MRGN = FusionStatsUtilities.MARGIN; + protected const int MAX_FONT_SIZE_WITH_GRAPH = 24; + + [SerializeField] [HideInInspector] protected UI.Text LabelTitle; + [SerializeField] [HideInInspector] protected UI.Image BackImage; + + /// + /// Which section of the Fusion engine is being monitored. In combination with StatId, this selects the stat being monitored. + /// + [InlineHelp] + [SerializeField] + protected Stats.StatSourceTypes _statSourceType; + public Stats.StatSourceTypes StateSourceType { + get => _statSourceType; + set { + _statSourceType = value; + TryConnect(); + } + } + + /// + /// The specific stat being monitored. + /// + [InlineHelp] + [SerializeField] + [CastEnum(nameof(CastToStatType))] + protected int _statId; + public int StatId { + get => _statId; + set { + _statId = value; + TryConnect(); + } + } + + [InlineHelp] + public float WarnThreshold; + + [InlineHelp] + public float ErrorThreshold; + + protected IStatsBuffer _statsBuffer; + public IStatsBuffer StatsBuffer { + get { + if (_statsBuffer == null) { + TryConnect(); + } + return _statsBuffer; + } + } + + protected bool _isOverlay; + public bool IsOverlay { + set { + if (_isOverlay != value) { + _isOverlay = value; + CalculateLayout(); + _layoutDirty = true; + } + } + get { + return _isOverlay; + } + } + + + + protected virtual Color BackColor { + get { + if (_statSourceType == Stats.StatSourceTypes.Simulation) { + return _fusionStats.SimDataBackColor; + } + if (_statSourceType == Stats.StatSourceTypes.NetConnection) { + return _fusionStats.NetDataBackColor; + } + return _fusionStats.ObjDataBackColor; + } + } + + protected Type CastToStatType => + (_statSourceType == Stats.StatSourceTypes.Simulation) ? typeof(Stats.SimStats) : + (_statSourceType == Stats.StatSourceTypes.NetConnection) ? typeof(Stats.NetStats) : + typeof(Stats.ObjStats); + + protected FusionStats _fusionStats; + protected FusionStats LocateParentFusionStats() { + if (_fusionStats == null) { + _fusionStats = GetComponentInParent(); + } + return _fusionStats; + } + + protected bool _layoutDirty = true; + + protected Stats.StatsPer CurrentPer; + + public Stats.StatSourceInfo StatSourceInfo; + + + // Track source values to detect changes in OnValidate. + [SerializeField] + [HideInInspector] + Stats.StatSourceTypes _prevStatSourceType; + + [SerializeField] + [HideInInspector] + int _prevStatId; + +#if UNITY_EDITOR + + protected virtual void OnValidate() { + if (_statSourceType != _prevStatSourceType || _statId != _prevStatId) { + WarnThreshold = 0; + ErrorThreshold = 0; + _prevStatSourceType = _statSourceType; + _prevStatId = _statId; + } + } +#endif + + public virtual void Initialize() { + + } + + public virtual void CyclePer() { + + var flags = StatSourceInfo.PerFlags; + switch (CurrentPer) { + case Stats.StatsPer.Individual: + if ((flags & Stats.StatsPer.Tick) == Stats.StatsPer.Tick) { + CurrentPer = Stats.StatsPer.Tick; + } else if ((flags & Stats.StatsPer.Second) == Stats.StatsPer.Second) { + CurrentPer = Stats.StatsPer.Second; + } + return; + + case Stats.StatsPer.Tick: + if ((flags & Stats.StatsPer.Second) == Stats.StatsPer.Second) { + CurrentPer = Stats.StatsPer.Second; + } else if ((flags & Stats.StatsPer.Individual) == Stats.StatsPer.Individual) { + CurrentPer = Stats.StatsPer.Individual; + } + return; + + case Stats.StatsPer.Second: + if ((flags & Stats.StatsPer.Individual) == Stats.StatsPer.Individual) { + CurrentPer = Stats.StatsPer.Individual; + } else if ((flags & Stats.StatsPer.Tick) == Stats.StatsPer.Tick) { + CurrentPer = Stats.StatsPer.Tick; + } + return; + + default: + return; + } + } + + public abstract void CalculateLayout(); + + public abstract void Refresh(); + + protected virtual bool TryConnect() { + + StatSourceInfo = Stats.GetDescription(_statSourceType, _statId); + // + if (WarnThreshold == 0 && ErrorThreshold == 0) { + WarnThreshold = StatSourceInfo.WarnThreshold; + ErrorThreshold = StatSourceInfo.ErrorThreshold; + } + + if (gameObject.activeInHierarchy == false) { + return false; + } + + if (_fusionStats == null) { + _fusionStats = GetComponentInParent(); + } + + // Any data connection requires a runner for the statistics source. + var runner = _fusionStats?.Runner; + + var statistics = runner?.Simulation?.Stats; + //Stats.StatSourceInfo info; + + + switch (_statSourceType) { + case Stats.StatSourceTypes.Simulation: { + _statsBuffer = statistics?.GetStatBuffer((Stats.SimStats)_statId); + break; + } + case Stats.StatSourceTypes.NetworkObject: { + if (_statId >= Stats.OBJ_STAT_TYPE_COUNT) { + StatId = 0; + } + if (_fusionStats.Object == null) { + _statsBuffer = null; + break; + } + + _statsBuffer = statistics?.GetObjectBuffer(_fusionStats.Object.Id, (Stats.ObjStats)_statId, true); + break; + } + case Stats.StatSourceTypes.NetConnection: { + + //StatSourceInfo = Stats.GetDescription((Stats.NetStats)_statId); + if (runner == null) { + _statsBuffer = null; + break; + } + _statsBuffer = statistics?.GetStatBuffer((Stats.NetStats)_statId, runner); + + break; + } + default: { + _statsBuffer = null; + break; + } + } + if (BackImage) { + BackImage.color = BackColor; + } + + // Update the labels, regardless if a connection can be made. + if (LabelTitle) { + CheckIfValidIncurrentMode(runner); + ApplyTitleText(); + } + + + CurrentPer = StatSourceInfo.PerDefault; + + return (_statsBuffer != null); + } + + protected void ApplyTitleText() { + var info = StatSourceInfo; + + // stat info is invalid + if (info.LongName == null) { + return; + } + + if (info.InvalidReason != null) { + LabelTitle.text = info.InvalidReason; + BackImage.gameObject.SetActive(false); + LabelTitle.color = _fusionStats.FontColor * new Color(1, 1, 1, 0.2f); + } else { + var titleRT = LabelTitle.rectTransform; + if (titleRT.rect.width < 100) { + LabelTitle.text = info.ShortName ?? info.LongName; + } else { + LabelTitle.text = info.LongName; + } + BackImage.gameObject.SetActive(true); + } + } + + protected void CheckIfValidIncurrentMode(NetworkRunner runner) { + if (runner == false) { + return; + } + + //var info = StatSourceInfo; + var flags = StatSourceInfo.Flags; + + if ((flags & Stats.StatFlags.ValidForBuildType) == 0) { + StatSourceInfo.InvalidReason = "DEBUG DLL ONLY"; + return; + } + + var obj = _statSourceType == Stats.StatSourceTypes.NetworkObject ? _fusionStats?.Object : null; + + if (obj) { + bool nonStateAuthOnly = (flags & Stats.StatFlags.ValidOnStateAuthority) == 0; + if (nonStateAuthOnly && obj.HasStateAuthority) { + StatSourceInfo.InvalidReason = "NON STATE AUTH ONLY"; + return; + } + } + + if (runner) { + bool clientOnly = (flags & Stats.StatFlags.ValidOnServer) == 0; + if (clientOnly && runner.IsClient == false) { + StatSourceInfo.InvalidReason = "CLIENT ONLY"; + return; + } + bool ecOnly = (flags & Stats.StatFlags.ValidWithDeltaSnapshot) == 0; + if (ecOnly && runner.Config.Simulation.ReplicationMode == SimulationConfig.StateReplicationModes.DeltaSnapshots) { + StatSourceInfo.InvalidReason = "EC MODE ONLY"; + return; + } + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs.meta new file mode 100644 index 0000000..9de3310 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionGraphBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 398cb446c5c510b4c8c531d36dafb4eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs new file mode 100644 index 0000000..6b39c62 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs @@ -0,0 +1,91 @@ + +using UnityEngine; +using Fusion; + +/// +/// Companion component for , which automatically faces this GameObject toward the supplied Camera. If Camera == null, will face towards Camera.main. +/// +[Fusion.ScriptHelp(BackColor = EditorHeaderBackColor.Olive)] +[ExecuteAlways] +public class FusionStatsBillboard : Fusion.Behaviour { + + /// + /// Force a particular camera to billboard this object toward. Leave null to use Camera.main. + /// + [InlineHelp] + public Camera Camera; + + // Camera find is expensive, so do it once per update for ALL implementations + static float _lastCameraFindTime; + static Camera _currentCam; + + FusionStats _fusionStats; + + private void Awake() { + _fusionStats = GetComponent(); + } + + private void OnEnable() { + UpdateLookAt(); + } + + private void OnDisable() { + transform.localRotation = default; + } + + Camera MainCamera { + set { + _currentCam = value; + } + get { + + var time = Time.time; + // Only look for the camera once per Update. + if (time == _lastCameraFindTime) + return _currentCam; + + _lastCameraFindTime = time; + var cam = Camera.main; + _currentCam = cam; + return cam; + } + } + +#if UNITY_EDITOR + private void OnDrawGizmos() { + LateUpdate(); + } +#endif + + private void LateUpdate() { + UpdateLookAt(); + } + + public void UpdateLookAt() { + + // Save the CPU here if our FusionStats is in overlay. Billboarding does nothing. + if (_fusionStats && _fusionStats.CanvasType == FusionStats.StatCanvasTypes.Overlay) { + return; + } + + var cam = Camera ? Camera : MainCamera; + + if (cam) { + if (enabled) { + + //var armOffset = transform.position - cam.transform.position; + //if (_canvasT == null) { + // _canvasT = GetComponentInChildren()?.transform; + // if (_canvasT) { + // _canvasT.localPosition = Offset; + // } + //} else { + // _canvasT.localPosition = Offset; + //} + + transform.rotation = cam.transform.rotation; + //transform.LookAt(transform.position + armOffset, cam.transform.up); + } + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs.meta new file mode 100644 index 0000000..5c21f5a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsBillboard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25acde9209338d24d83cc1c826297be4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs new file mode 100644 index 0000000..122f66e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs @@ -0,0 +1,308 @@ +using Fusion; +using UnityEngine; +using UnityEngine.UI; +using Stats = Fusion.Simulation.Statistics; +using Fusion.StatsInternal; + +public class FusionStatsMeterBar : FusionGraphBase +{ + //public float WarningValueThreshold; + //public float ErrorValueThreshold; + + + public float HoldPeakTime = 0.1f; + public float DecayTime = 0.25f; + + /// + /// Values greater than 0 will limit the meter to a range of 0 to MeterMax. + /// Value of 0 will adjust the max to the largest value occurance. + /// + [InlineHelp] + public int MeterMax = 0; + + + /// + /// Exposes the UI labels and controls of , so they may be modified if customizing this graph. + /// + [InlineHelp] + [SerializeField] + bool _showUITargets; + + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public Text ValueLabel; + [DrawIf(nameof(_showUITargets), true, DrawIfHideType.Hide)] + public Image Bar; + + + //public string CurrentLabel; + //double _currentRawValue; + double _currentDisplayValue; + double _currentBarValue; + Color CurrentColor; + + + protected override Color BackColor => base.BackColor * new Color(.5f, .5f, .5f, 1); + +#if UNITY_EDITOR + + protected override void OnValidate() { + base.OnValidate(); + + if (MeterMax < 0) { + MeterMax = 0; + } + + if (Application.isPlaying == false) { + TryConnect(); + _layoutDirty = true; + } + } +#endif + + public override void Initialize() { + base.Initialize(); + + _max = MeterMax; + // Prefabs lose editor generated sprites - recreate as needed. + if (BackImage.sprite == null) { + BackImage.sprite = FusionStatsUtilities.MeterSprite; + Bar.sprite = BackImage.sprite; + } + + // TODO: Can remove these later. Backwards compat for mask removal on Dec 30 2021 + BackImage.type = Image.Type.Simple; + if (Bar.rectTransform.parent != BackImage.rectTransform.parent) { + var oldMask = Bar.transform.parent; + Bar.rectTransform.SetParent(BackImage.rectTransform.parent); + Bar.transform.SetSiblingIndex(BackImage.transform.GetSiblingIndex() + 1); + //Destroy(oldMask); + } + Bar.type = Image.Type.Filled; + Bar.fillMethod = Image.FillMethod.Horizontal; + Bar.fillAmount = 0; + } + + double _lastImportedSampleTickTime; + double _max; + double _total; + float _lastPeakSetTime; + + public override void Refresh() { + if (_layoutDirty) { + CalculateLayout(); + } + + var statsBuffer = StatsBuffer; + if (statsBuffer == null || statsBuffer.Count < 1) { + return; + } + + // Awkward temp RPC handling + if (statsBuffer.DefaultVisualization == FusionGraphVisualization.CountHistogram) { + + if (statsBuffer.Count > 0) { + + int highestRpcsFoundForTick = 0; + float newestSampleTick = statsBuffer.GetSampleAtIndex(statsBuffer.Count - 1).TickValue; + var tick = newestSampleTick; + // Only look back at ticks we have not yet already looked at on previous updates. + if (newestSampleTick > _lastImportedSampleTickTime) { + int tickRpcCount = 0; + for (int i = statsBuffer.Count - 1; i >= 0; i--) { + var sampletick = statsBuffer.GetSampleAtIndex(i).TickValue; + + if (sampletick > _lastImportedSampleTickTime) { + // If we are now looking at samples from a different tick that previous for loop, reset to get count for this tick now. + if (sampletick != tick) { + tick = sampletick; + // Capture the RPC count for the last recorded tick if it is the new high. + if (tickRpcCount > highestRpcsFoundForTick) { + highestRpcsFoundForTick = tickRpcCount; + } + tickRpcCount = 0; + } + tickRpcCount++; + _total++; + } else { + break; + } + } + _lastImportedSampleTickTime = newestSampleTick; + } + + SetValue(highestRpcsFoundForTick); + } + return; + } + + if (statsBuffer.Count > 0) { + var value = statsBuffer.GetSampleAtIndex(statsBuffer.Count - 1); + if (value.TickValue == _fusionStats.Runner.Simulation.LatestServerState.Tick) { + SetValue(value.FloatValue); + } else { + SetValue(0); + } + } + } + + public void LateUpdate() { + + if (DecayTime <= 0) { + return; + } + + if (_currentBarValue <= 0) { + return; + } + + if (Time.time < _lastPeakSetTime + HoldPeakTime) { + return; + } + + double decayedVal = System.Math.Max(_currentBarValue - Time.deltaTime / DecayTime * _max, 0); + SetBar(decayedVal); + + } + + public void SetValue(double rawvalue) { + + var info = StatSourceInfo; + + double multiplied = rawvalue * info.Multiplier; + + if (MeterMax == 0) { + if (multiplied > _max) { + _max = multiplied; + } + } + + + double clampedValue = System.Math.Max(System.Math.Min(multiplied, _max), 0); + var roundedValue = System.Math.Round(clampedValue, info.Decimals); + var newDisplayValue = _total > 0 ? _total : roundedValue; + + if (clampedValue >= _currentBarValue) { + _lastPeakSetTime = Time.time; + } + + if (newDisplayValue != _currentDisplayValue) { + ValueLabel.text = _total > 0 ? _total.ToString() : clampedValue.ToString(); + _currentDisplayValue = newDisplayValue; + } + + // Only set values greater than the current shown value when using decay. + if (DecayTime >= 0 && clampedValue <= _currentBarValue) { + return; + } + + if (clampedValue != _currentBarValue) { + SetBar(clampedValue); + } + + } + + void SetBar(double value) { + + var fusionStats = _fusionStats; + + Bar.fillAmount = (float)(value / _max); + + _currentBarValue = value; + + if (value < WarnThreshold) { + var GoodColor = fusionStats.GraphColorGood; + if (CurrentColor != GoodColor) { + CurrentColor = GoodColor; + Bar.color = GoodColor; + } + } else if (value < ErrorThreshold) { + var WarnColor = fusionStats.GraphColorWarn; + if (CurrentColor != WarnColor) { + Bar.color = WarnColor; + CurrentColor = WarnColor; + } + } else { + var ErrorColor = fusionStats.GraphColorBad; + if (CurrentColor != ErrorColor) { + Bar.color = ErrorColor; + CurrentColor = ErrorColor; + } + } + } + + public override void CalculateLayout() { + + _layoutDirty = false; + + // Special padding handling because Arial vertically sits below center. + var pad = LabelTitle.transform.parent.GetComponent().rect.height * .2f; + LabelTitle.rectTransform.offsetMax = new Vector2(0, -pad); + LabelTitle.rectTransform.offsetMin = new Vector2(PAD, pad * 1.2f); + + ValueLabel.rectTransform.offsetMax = new Vector2(-PAD, -pad); + ValueLabel.rectTransform.offsetMin = new Vector2(0, pad * 1.2f); + + ApplyTitleText(); + } + + // unfinished + public static FusionStatsMeterBar Create( + RectTransform parent, + FusionStats fusionStats, + Stats.StatSourceTypes statSourceType, + int statId, + float warnThreshold, + float alertThreshold + ) { + + var info = Stats.GetDescription(statSourceType, statId); + var barRT = parent.CreateRectTransform(info.LongName, true); + var bar = barRT.gameObject.AddComponent(); + bar.StatSourceInfo = info; + bar._fusionStats = fusionStats; + //bar.WarningValueThreshold = warnThreshold; + //bar.ErrorValueThreshold = alertThreshold; + bar._statSourceType = statSourceType; + bar._statId = statId; + bar.GenerateMeter(); + return bar; + } + + public void GenerateMeter() { + + var info = Stats.GetDescription(_statSourceType, _statId); + var backRT = transform.CreateRectTransform("Back", true); + BackImage = backRT.gameObject.AddComponent(); + BackImage.raycastTarget = false; + BackImage.sprite = FusionStatsUtilities.MeterSprite; + BackImage.color = BackColor; + BackImage.type = Image.Type.Simple; + var barRT = transform.CreateRectTransform("Bar", true); + Bar = barRT.gameObject.AddComponent(); + Bar.raycastTarget = false; + Bar.sprite = BackImage.sprite; + Bar.color = _fusionStats.GraphColorGood; + Bar.type = Image.Type.Filled; + Bar.fillMethod = Image.FillMethod.Horizontal; + Bar.fillAmount = 0; + + var titleRT = transform.CreateRectTransform("Label", true) + .ExpandAnchor() + .SetAnchors(0.0f, 0.5f, 0.0f,1.0f) + .SetOffsets(6, -6, 6, -6); + + + LabelTitle = titleRT.AddText(info.LongName, TextAnchor.MiddleLeft, _fusionStats.FontColor); + LabelTitle.alignByGeometry = false; + + var valueRT = transform.CreateRectTransform("Value", true) + .ExpandAnchor() + .SetAnchors(0.5f, 1.0f, 0.0f, 1.0f) + .SetOffsets(6, -6, 6, -6); + + ValueLabel = valueRT.AddText("0.0", TextAnchor.MiddleRight, _fusionStats.FontColor); + ValueLabel.alignByGeometry = false; + + } + +} diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs.meta new file mode 100644 index 0000000..a4d16bc --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsMeterBar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ed135d9ebf10d284e9e18f8e5432fd70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs new file mode 100644 index 0000000..4dca895 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs @@ -0,0 +1,173 @@ + +using UnityEngine; +using Fusion.StatsInternal; +using UI = UnityEngine.UI; + +public class FusionStatsObjectIds : Fusion.Behaviour, IFusionStatsView { + + protected const int PAD = FusionStatsUtilities.PAD; + protected const int MARGIN = FusionStatsUtilities.MARGIN; + + [SerializeField] [HideInInspector] UI.Text _inputValueText; + [SerializeField] [HideInInspector] UI.Text _stateValueText; + [SerializeField] [HideInInspector] UI.Text _objectIdLabel; + + [SerializeField] [HideInInspector] UI.Image _stateAuthBackImage; + [SerializeField] [HideInInspector] UI.Image _inputAuthBackImage; + + FusionStats _fusionStats; + + void Awake() { + _fusionStats = GetComponentInParent(); + } + + static Color _noneAuthColor = new Color(0.2f, 0.2f, 0.2f, 0.9f); + static Color _inputAuthColor = new Color(0.1f, 0.6f, 0.1f, 1.0f); + static Color _stateAuthColor = new Color(0.8f, 0.4f, 0.0f, 1.0f); + + void IFusionStatsView.Initialize() { + + } + + public static RectTransform Create(RectTransform parent, FusionStats fusionStats) { + + var rt = parent.CreateRectTransform("Object Ids Panel") + .ExpandTopAnchor(MARGIN); + + var stats = rt.gameObject.AddComponent(); + + stats._fusionStats = fusionStats; + + stats.Generate(); + + return rt; + } + + const float LABEL_DIVIDING_POINT = .7f; + const float TEXT_PAD = 4; + const float TEXT_PAD_HORIZ = 6; + const int MAX_TAG_FONT_SIZE = 18; + + public void Generate() { + + + + var fontColor = _fusionStats.FontColor; + var layoutRT = transform.CreateRectTransform("IDs Layout") + .ExpandAnchor() + .AddCircleSprite(_fusionStats.ObjDataBackColor) + ; + + // Object ID panel on Left + { + var idPanelRT = layoutRT.CreateRectTransform("Object Id Panel", true) + .ExpandTopAnchor() + .SetAnchors(0, 0.4f, 0, 1); + + + var objIdLabelRT = idPanelRT.CreateRectTransform("Object Id Label") + .SetAnchors(0, 1, LABEL_DIVIDING_POINT, 1) + .SetOffsets(TEXT_PAD_HORIZ, -TEXT_PAD_HORIZ, 0, -TEXT_PAD); + + objIdLabelRT.AddText("OBJECT ID", TextAnchor.MiddleCenter, fontColor) + .resizeTextMaxSize = MAX_TAG_FONT_SIZE; + + var objIdValueRT = idPanelRT.CreateRectTransform("Object Id Value") + .SetAnchors(0, 1, 0, LABEL_DIVIDING_POINT) + .SetOffsets(TEXT_PAD_HORIZ, -TEXT_PAD_HORIZ, TEXT_PAD, 0); + + _objectIdLabel = objIdValueRT.AddText("00", TextAnchor.MiddleCenter, fontColor); + } + + // Authority ID panel on right + { + + AddAuthorityPanel(layoutRT, "Input", ref _inputValueText, ref _inputAuthBackImage) + .SetAnchors(0.4f, 0.7f, 0, 1); + + AddAuthorityPanel(layoutRT, "State", ref _stateValueText, ref _stateAuthBackImage) + .SetAnchors(0.7f, 1.0f, 0, 1); + } + } + + RectTransform AddAuthorityPanel(RectTransform parent, string label, ref UI.Text valueText, ref UI.Image backImage) { + var fontColor = _fusionStats.FontColor; + + var authIdPanelRT = parent.CreateRectTransform($"{label} Id Panel", true) + .ExpandTopAnchor() + .SetAnchors(0.5f, 1, 0, 1) + .AddCircleSprite(_noneAuthColor, out backImage); + + var authLabelRT = authIdPanelRT.CreateRectTransform($"{label} Label") + .SetAnchors(0, 1, LABEL_DIVIDING_POINT, 1) + .SetOffsets(TEXT_PAD_HORIZ, -TEXT_PAD_HORIZ, 0, -TEXT_PAD); + + var authorityText = authLabelRT.AddText(label, TextAnchor.MiddleCenter, fontColor); + authorityText.resizeTextMaxSize = MAX_TAG_FONT_SIZE; + + var authValueRT = authIdPanelRT.CreateRectTransform($"{label} Value") + .SetAnchors(0, 1, 0, LABEL_DIVIDING_POINT) + .SetOffsets(TEXT_PAD_HORIZ, -TEXT_PAD_HORIZ, TEXT_PAD, 0); + + valueText = authValueRT.AddText("P0", TextAnchor.MiddleCenter, fontColor); + + return authIdPanelRT; + } + + void IFusionStatsView.CalculateLayout() { + //throw new System.NotImplementedException(); + } + + // cache of last applied UI values + bool _previousHasInputAuth; + bool _previousHasStateAuth; + int _previousInputAuthValue = - 2; + int _previousStateAuthValue = - 2; + uint _previousObjectIdValue; + + //SimulationConfig.Topologies _previousTopology = (SimulationConfig.Topologies)(-1); + + void IFusionStatsView.Refresh() { + if (_fusionStats == null) { + return; + } + + var obj = _fusionStats.Object; + if (obj == null) { + return; + } + + if (obj.IsValid) { + + bool hasInputAuth = obj.HasInputAuthority; + if (_previousHasInputAuth != hasInputAuth) { + _inputAuthBackImage.color = hasInputAuth ? _inputAuthColor : _noneAuthColor; + _previousHasInputAuth = hasInputAuth; + } + + bool hasStateAuth = obj.HasStateAuthority || obj.Runner.IsServer; + if (_previousHasStateAuth != hasStateAuth) { + _stateAuthBackImage.color = hasStateAuth ? _stateAuthColor : _noneAuthColor; + _previousHasStateAuth = hasStateAuth; + } + + var inputAuth = obj.InputAuthority; + if (_previousInputAuthValue != inputAuth) { + _inputValueText.text = inputAuth == -1 ? "-" : "P" + inputAuth.PlayerId.ToString(); + _previousInputAuthValue = inputAuth; + } + + var stateAuth = obj.StateAuthority; + if (_previousStateAuthValue != stateAuth) { + _stateValueText.text = stateAuth == -1 ? "-" : "P" + stateAuth.PlayerId.ToString(); + _previousStateAuthValue = stateAuth; + } + } + + uint objectId = obj.Id.Raw; + if (objectId != _previousObjectIdValue) { + _objectIdLabel.text = objectId.ToString(); + _previousObjectIdValue = objectId; + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs.meta new file mode 100644 index 0000000..13a7626 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsObjectIds.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 495a1cd68ccf6664fa601f466669e8c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs new file mode 100644 index 0000000..080853c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs @@ -0,0 +1,487 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityEngine.Events; +using UI = UnityEngine.UI; + +namespace Fusion.StatsInternal { + public interface IFusionStatsView { + void Initialize(); + void CalculateLayout(); + void Refresh(); + bool isActiveAndEnabled { get; } + Transform transform { get; } + } + + public static class FusionStatsUtilities { + + public const int PAD = 10; + public const int MARGIN = 6; + public const int FONT_SIZE = 12; + public const int FONT_SIZE_MIN = 4; + public const int FONT_SIZE_MAX = 200; + + + static List _cachedGraphVisualizationNames; + public static List CachedTelemetryNames { + get { + if (_cachedGraphVisualizationNames == null) { + var enumtype = typeof(FusionGraphVisualization); + var names = System.Enum.GetNames(enumtype); + _cachedGraphVisualizationNames = new List(names.Length); + // Use Description for the nicified name + for (int i = 0; i < names.Length; ++i) { + string name; + try { + MemberInfo[] memberInfo = enumtype.GetMember(names[i]); + var _Attribs = memberInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false); + name = ((System.ComponentModel.DescriptionAttribute)(_Attribs[0])).Description; + } catch { + name = names[i]; + } + _cachedGraphVisualizationNames.Add(name); + } + } + return _cachedGraphVisualizationNames; + } + } + + static Font _font; + public static Font Font { + get { + if (_font == null) { + _font = Resources.GetBuiltinResource("Arial.ttf"); + + } + return _font; + } + } + + const int METER_TEXTURE_WIDTH = 512; + static Texture2D _meterTexture; + static Texture2D MeterTexture { + get { + if (_meterTexture == null) { + var tex = new Texture2D(METER_TEXTURE_WIDTH, 2); + for (int x = 0; x < METER_TEXTURE_WIDTH; ++x) { + for (int y = 0; y < 2; ++y) { + var color = (x != 0 && x % 16 == 0) ? new Color(1f, 1f, 1f, 0.75f) : new Color(1f, 1f, 1f, 1f); + tex.SetPixel(x, y, color); + } + } + tex.Apply(); + return _meterTexture = tex; + + } + return _meterTexture; + } + } + + static Sprite _meterSprite; + public static Sprite MeterSprite { + get { + if (_meterSprite == null) { + _meterSprite = Sprite.Create(MeterTexture, new Rect(0, 0, METER_TEXTURE_WIDTH, 2), new Vector2()); + } + return _meterSprite; + } + } + + const int R = 64; + + static Texture2D _circle32Texture; + static Texture2D Circle32Texture { + get { + if (_circle32Texture == null) { + var tex = new Texture2D(R * 2, R * 2); + for (int x = 0; x < R; ++x) { + for (int y = 0; y < R; ++y) { + double h = System.Math.Abs( System.Math.Sqrt(x * x + y * y)); + float a = h > R ? 0.0f : h < (R - 1) ? 1.0f :(float) (R - h); + var c = new Color(1.0f, 1.0f, 1.0f, a); + tex.SetPixel(R + 0 + x, R + 0 + y, c); + tex.SetPixel(R - 1 - x, R + 0 + y, c); + tex.SetPixel(R + 0 + x, R - 1 - y, c); + tex.SetPixel(R - 1 - x, R - 1 - y, c); + + } + } + tex.Apply(); + return _circle32Texture = tex; + } + return _circle32Texture; + } + } + + static Sprite _circle32Sprite; + public static Sprite CircleSprite { + get { + if (_circle32Sprite == null) { + _circle32Sprite = Sprite.Create(Circle32Texture, new Rect(0, 0, R * 2, R * 2), new Vector2(R , R), 10f, 0, SpriteMeshType.Tight, new Vector4(R-1, R-1, R-1, R-1)); + } + return _circle32Sprite; + } + } + + public static Color DARK_GREEN = new Color(0.0f, 0.5f, 0.0f, 1.0f); + public static Color DARK_BLUE = new Color(0.0f, 0.0f, 0.5f, 1.0f); + public static Color DARK_RED = new Color(0.5f, 0.0f, 0.0f, 1.0f); + public static List _reusableList = new List(1); + + public static bool TryFindActiveRunner(FusionStats fusionStats, out NetworkRunner runner, SimulationModes? mode = null) { + + var gameObject = fusionStats.gameObject; + var gameobjScene = fusionStats.gameObject.scene; + + var enumerator = NetworkRunner.GetInstancesEnumerator(); + while (enumerator.MoveNext()) { + var found = enumerator.Current; + if (found && found.IsRunning) { + if (mode.HasValue && (mode.Value & found.Mode) == 0) { + continue; + } + if (fusionStats.EnforceSingle) { + runner = found; + return true; + } + if (found.SimulationUnityScene == gameobjScene) { + runner = found; + return true; + } + } + } + + runner = null; + return false; + } + + public static RectTransform CreateRectTransform(this Transform parent, string name, bool expand = false) { + var go = new GameObject(name); + var rt = go.AddComponent(); + rt.SetParent(parent); + rt.localPosition = default; + rt.localScale = default; + rt.localScale = new Vector3(1, 1, 1); + + if (expand) { + ExpandAnchor(rt); + } + return rt; + } + + [System.Obsolete] + internal static RectTransform CreateRectTransform(string name, Transform parent, bool expand = false) { + var go = new GameObject(name); + var rt = go.AddComponent(); + rt.SetParent(parent); + rt.localPosition = default; + rt.localScale = default; + rt.localScale = new Vector3(1, 1, 1); + + if (expand) { + ExpandAnchor(rt); + } + return rt; + } + + public static UI.Dropdown CreateDropdown(this RectTransform rt, float padding, Color fontColor) { + var dropRT = rt.CreateRectTransform("Dropdown") + .ExpandAnchor(-MARGIN); + + var dropimg = dropRT.gameObject.AddComponent(); + var dropdown = dropRT.gameObject.AddComponent(); + dropimg.color = new Color(0, 0, 0, 0); + dropdown.image = dropimg; + + var templateRT = dropRT.CreateRectTransform("Template", true) + .ExpandTopAnchor() + .SetOffsets(0, 0, -150, 0); + + var contentRT = templateRT.CreateRectTransform("Content") + .ExpandTopAnchor() + .SetOffsets(0, 0, -150, 0); + + var itemRT = contentRT.CreateRectTransform("Item", true) + .SetAnchors(0, 1, 1, 1) + .SetPivot(0.5f, 1) + .SetSizeDelta(0, 50); + + var toggle = itemRT.gameObject.AddComponent(); + toggle.colors = new UI.ColorBlock() { + colorMultiplier = 1, + normalColor = new Color(0.2f, 0.2f, 0.2f, 1f), + highlightedColor = new Color(.3f, .3f, .3f, 1f), + pressedColor = new Color(.4f, .4f, .4f, 4f), + selectedColor = new Color(.25f, .25f, .25f, 1f), + }; + var itemBackRT = itemRT.CreateRectTransform("Item Background", true); + var itemBack = itemBackRT.gameObject.AddComponent(); + + var itemChckRT = itemRT.CreateRectTransform("Item Checkmark", true) + .SetAnchors(0.05f, 0.1f, 0.1f, 0.9f) + .SetOffsets(0, 0, 0, 0); + + var check = itemChckRT.gameObject.AddComponent(); + check.sprite = CircleSprite; + check.preserveAspect = true; + + var itemLablRT = itemRT.CreateRectTransform("Item Label", true) + .SetAnchors(0.15f, 0.9f, 0.1f, 0.9f) + .SetOffsets(0, 0, 0, 0); + + var itemLabl = itemLablRT.AddText("Sample", TextAnchor.UpperLeft, fontColor); + itemLabl.alignment = TextAnchor.MiddleLeft; + itemLabl.resizeTextMaxSize = 24; + + toggle.targetGraphic = itemBack; + toggle.graphic = check; + toggle.isOn = true; + + dropdown.template = templateRT; + dropdown.itemText = itemLabl; + + templateRT.gameObject.SetActive(false); + + return dropdown; + } + + + public static UI.Text AddText(this RectTransform rt, string label, TextAnchor anchor, Color FontColor) { + var text = rt.gameObject.AddComponent(); + text.text = label; + text.color = FontColor; + text.font = Font; + text.alignment = anchor; + text.fontSize = FONT_SIZE; + text.raycastTarget = false; + //text.alignByGeometry = true; + text.resizeTextMinSize = FONT_SIZE_MIN; + text.resizeTextMaxSize = FONT_SIZE_MAX; + text.resizeTextForBestFit = true; + return text; + } + + public const float BTTN_LBL_NORM_HGHT = .175f; + private const int BTTN_FONT_SIZE_MAX = 100; + private const float BTTN_ALPHA = 0.925f; + + internal static void MakeButton(this RectTransform parent, ref UI.Button button, string iconText, string labelText, out UI.Text icon, out UI.Text text, UnityAction action) { + var rt = parent.CreateRectTransform(labelText); + button = rt.gameObject.AddComponent(); + + var iconRt = rt.CreateRectTransform("Icon", true); + iconRt.anchorMin = new Vector2(0, BTTN_LBL_NORM_HGHT); + iconRt.anchorMax = new Vector2(1, 1.0f); + iconRt.offsetMin = new Vector2(0, 0); + iconRt.offsetMax = new Vector2(0, 0); + + icon = iconRt.gameObject.AddComponent(); + button.targetGraphic = icon; + icon.font = FusionStatsUtilities.Font; + icon.text = iconText; + icon.alignment = TextAnchor.MiddleCenter; + icon.fontStyle = FontStyle.Bold; + icon.fontSize = BTTN_FONT_SIZE_MAX; + icon.resizeTextMinSize = 0; + icon.resizeTextMaxSize = BTTN_FONT_SIZE_MAX; + icon.alignByGeometry = true; + icon.resizeTextForBestFit = true; + + var textRt = rt.CreateRectTransform("Label", true); + textRt.anchorMin = new Vector2(0, 0); + textRt.anchorMax = new Vector2(1, BTTN_LBL_NORM_HGHT); + textRt.pivot = new Vector2(.5f, BTTN_LBL_NORM_HGHT * .5f); + textRt.offsetMin = new Vector2(0, 0); + textRt.offsetMax = new Vector2(0, 0); + + text = textRt.gameObject.AddComponent(); + text.color = Color.black; + text.font = FusionStatsUtilities.Font; + text.text = labelText; + text.alignment = TextAnchor.MiddleCenter; + text.fontStyle = FontStyle.Bold; + text.fontSize = 0; + text.resizeTextMinSize = 0; + text.resizeTextMaxSize = BTTN_FONT_SIZE_MAX; + text.resizeTextForBestFit = true; + text.horizontalOverflow = HorizontalWrapMode.Overflow; + + UI.ColorBlock colors = button.colors; + colors.normalColor = new Color(.0f, .0f, .0f, BTTN_ALPHA); + colors.pressedColor = new Color(.5f, .5f, .5f, BTTN_ALPHA); + colors.highlightedColor = new Color(.3f, .3f, .3f, BTTN_ALPHA); + colors.selectedColor = new Color(.0f, .0f, .0f, BTTN_ALPHA); + button.colors = colors; + + button.onClick.AddListener(action); + } + + public static RectTransform AddHorizontalLayoutGroup(this RectTransform rt, float spacing, int? rgtPad = null, int? lftPad = null, int? topPad = null, int? botPad = null) { + var group = rt.gameObject.AddComponent(); + group.childControlHeight = true; + group.childControlWidth = true; + group.spacing = spacing; + group.padding = new RectOffset( + rgtPad.HasValue ? rgtPad.Value : 0, + lftPad.HasValue ? lftPad.Value : 0, + topPad.HasValue ? topPad.Value : 0, + botPad.HasValue ? botPad.Value : 0 + ); + return rt; + } + + public static RectTransform AddVerticalLayoutGroup(this RectTransform rt, float spacing, int? rgtPad = null, int? lftPad = null, int? topPad = null, int? botPad = null) { + var group = rt.gameObject.AddComponent(); + group.childControlHeight = true; + group.childControlWidth = true; + group.spacing = spacing; + //group.padding = new RectOffset( + // rgtPad.HasValue ? rgtPad.Value : 0, + // lftPad.HasValue ? lftPad.Value : 0, + // topPad.HasValue ? topPad.Value : 0, + // botPad.HasValue ? botPad.Value : 0 + // ); + return rt; + } + + public static UI.GridLayoutGroup AddGridlLayoutGroup(this RectTransform rt, float spacing, int? rgtPad = null, int? lftPad = null, int? topPad = null, int? botPad = null) { + var group = rt.gameObject.AddComponent(); + group.spacing = new Vector2( spacing, spacing); + //group.padding = new RectOffset( + // rgtPad.HasValue ? rgtPad.Value : 0, + // lftPad.HasValue ? lftPad.Value : 0, + // topPad.HasValue ? topPad.Value : 0, + // botPad.HasValue ? botPad.Value : 0 + // ); + return group; + } + + public static RectTransform AddImage(this RectTransform rt, Color color) { + var image = rt.gameObject.AddComponent(); + image.color = color; + image.raycastTarget = false; + return rt; + } + + public static RectTransform AddCircleSprite(this RectTransform rt, Color color) { + rt.AddCircleSprite(color, out var _); + return rt; + } + + public static RectTransform AddCircleSprite(this RectTransform rt, Color color, out UI.Image image) { + image = rt.gameObject.AddComponent(); + image.sprite = CircleSprite; + image.type = UI.Image.Type.Sliced; + image.pixelsPerUnitMultiplier = 100f; + image.color = color; + image.raycastTarget = false; + return rt; + + } + + public static RectTransform ExpandAnchor(this RectTransform rt, float? padding = null) { + rt.anchorMax = new Vector2(1, 1); + rt.anchorMin = new Vector2(0, 0); + rt.pivot = new Vector2(0.5f, 0.5f); + if (padding.HasValue) { + rt.offsetMin = new Vector2(padding.Value, padding.Value); + rt.offsetMax = new Vector2(-padding.Value, -padding.Value); + } else { + rt.sizeDelta = default; + rt.anchoredPosition = default; + } + return rt; + } + + public static RectTransform ExpandTopAnchor(this RectTransform rt, float? padding = null) { + rt.anchorMax = new Vector2(1, 1); + rt.anchorMin = new Vector2(0, 1); + rt.pivot = new Vector2(0.5f, 1f); + if (padding.HasValue) { + rt.offsetMin = new Vector2(padding.Value, padding.Value); + rt.offsetMax = new Vector2(-padding.Value, -padding.Value); + } else { + rt.sizeDelta = default; + rt.anchoredPosition = default; + } + return rt; + } + + public static RectTransform ExpandMiddleLeft(this RectTransform rt) { + rt.anchorMax = new Vector2(0, 0.5f); + rt.anchorMin = new Vector2(0, 0.5f); + rt.pivot = new Vector2(0.0f, .5f); + return rt; + } + + public static RectTransform SetSizeDelta(this RectTransform rt, float offsetX, float offsetY) { + rt.sizeDelta = new Vector2(offsetX, offsetY); + return rt; + } + + + public static RectTransform SetOffsets(this RectTransform rt, float minX, float maxX, float minY, float maxY) { + rt.offsetMin = new Vector2(minX, minY); + rt.offsetMax = new Vector2(maxX, maxY); + return rt; + } + + public static RectTransform SetPivot(this RectTransform rt, float pivotX, float pivotY) { + rt.pivot = new Vector2(pivotX, pivotY); + return rt; + } + + public static RectTransform SetAnchors(this RectTransform rt, float minX, float maxX, float minY, float maxY) { + rt.anchorMin = new Vector2(minX, minY); + rt.anchorMax = new Vector2(maxX, maxY); + return rt; + } + + const float GUIDE_MARGIN = .01f; + const float GUIDE_MARGIN_HALF = GUIDE_MARGIN * .5f; + + internal static RectTransform MakeGuides(this RectTransform parent) { + + var outlineColor = new Color(0.5f, 0.5f, 0.5f, 0.75f); + var rect = parent.CreateRectTransform("Guides", true); + rect.SetSiblingIndex(0); + + var back = rect.CreateRectTransform("Back", true); + back.gameObject.AddComponent().color = new Color(0.5f, 0.5f, 0.5f, 0.25f); + + var left = rect.CreateRectTransform("Left", true); + left.anchorMin = new Vector2(-GUIDE_MARGIN, 0); + left.anchorMax = new Vector2(0, 1); + left.gameObject.AddComponent().color = outlineColor; + + var right = rect.CreateRectTransform("Right", true); + right.anchorMin = new Vector2(1, 0); + right.anchorMax = new Vector2(1 + GUIDE_MARGIN, 1); + right.gameObject.AddComponent().color = outlineColor; + + var top = rect.CreateRectTransform("Top", true); + top.anchorMin = new Vector2(-GUIDE_MARGIN, 1); + top.anchorMax = new Vector2(1 + GUIDE_MARGIN, 1 + GUIDE_MARGIN); + top.gameObject.AddComponent().color = outlineColor; + + var bottom = rect.CreateRectTransform("Bottom", true); + bottom.anchorMin = new Vector2(-GUIDE_MARGIN, -GUIDE_MARGIN); + bottom.anchorMax = new Vector2(1 + GUIDE_MARGIN, 0); + bottom.gameObject.AddComponent().color = outlineColor; + + rect.CreateRectTransform("Center", true) + .SetAnchors(0.5f - GUIDE_MARGIN_HALF, 0.5f + GUIDE_MARGIN_HALF, 0, 1) + .AddImage(outlineColor); + + rect.CreateRectTransform("Middle", true) + .SetAnchors(0, 1, 0.5f - GUIDE_MARGIN_HALF, 0.5f + GUIDE_MARGIN_HALF) + .AddImage(outlineColor); + + return rect; + } + + + } +} + diff --git a/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs.meta b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs.meta new file mode 100644 index 0000000..7be2417 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionStats/FusionStatsUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3b8978fb990cdd8488bfaa43637b0a7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs b/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs new file mode 100644 index 0000000..82dee6e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace Fusion { + [Serializable] + public partial class FusionUnityLogger : Fusion.ILogger { + + /// + /// Implement this to modify values of this logger. + /// + /// + static partial void InitializePartial(ref FusionUnityLogger logger); + + StringBuilder _builder = new StringBuilder(); + + /// + /// If true, all messages will be prefixed with [Fusion] tag + /// + public bool UseGlobalPrefix; + + /// + /// If true, some parts of messages will be enclosed with <color> tags. + /// + public bool UseColorTags; + + /// + /// Color of the global prefix (see ). + /// + public string GlobalPrefixColor; + + /// + /// + /// + public Color32 MinRandomColor; + /// + /// + /// + public Color32 MaxRandomColor; + + public Color ServerColor; + + /// + /// Converts object to a color . By default works only for and uses and fields. + /// + public Func GetColor { get; set; } + + public FusionUnityLogger() { + bool isDarkMode = false; +#if UNITY_EDITOR + isDarkMode = UnityEditor.EditorGUIUtility.isProSkin; +#endif + + MinRandomColor = isDarkMode ? new Color32(158, 158, 158, 255) : new Color32(30, 30, 30, 255); + MaxRandomColor = isDarkMode ? new Color32(255, 255, 255, 255) : new Color32(90, 90, 90, 255); + ServerColor = isDarkMode ? new Color32(255, 255, 158, 255) : new Color32(30, 90, 200, 255); + + UseColorTags = true; + UseGlobalPrefix = true; + GlobalPrefixColor = Color32ToRGBString(isDarkMode ? new Color32(115, 172, 229, 255) : new Color32(20, 64, 120, 255)); + + GetColor = (obj) => { + if (obj is NetworkRunner runner) { + // flag server/host runners as special with seed of -1 + var seed = runner.GetHashCodeForLogger(); + return GetRandomColor(seed); + } + return default; + }; + } + + public void Log(LogType logType, string prefix, ref T context, string message) where T : ILogBuilder { + + Debug.Assert(_builder.Length == 0); + string fullMessage; + + try { + if (logType == LogType.Debug) { + _builder.Append("[DEBUG] "); + } else if (logType == LogType.Trace) { + _builder.Append("[TRACE] "); + } + + if (UseGlobalPrefix) { + if (UseColorTags) { + _builder.Append(""); + } + _builder.Append("[Fusion"); + + if (!string.IsNullOrEmpty(prefix)) { + _builder.Append("/"); + _builder.Append(prefix); + } + + _builder.Append("]"); + + if (UseColorTags) { + _builder.Append(""); + } + _builder.Append(" "); + } else { + if (!string.IsNullOrEmpty(prefix)) { + _builder.Append(prefix); + _builder.Append(": "); + } + } + + var options = new LogOptions(UseColorTags, GetColor); + context.BuildLogMessage(_builder, message, options); + fullMessage = _builder.ToString(); + } finally { + _builder.Clear(); + } + + var obj = context as UnityEngine.Object; + + switch (logType) { + case LogType.Error: + Debug.LogError(fullMessage, obj); + break; + case LogType.Warn: + Debug.LogWarning(fullMessage, obj); + break; + default: + Debug.Log(fullMessage, obj); + break; + } + } + + public void LogException(string prefix, ref T context, Exception ex) where T : ILogBuilder { + Log(LogType.Error, string.Empty, ref context, $"{ex.GetType()}\nSee next error log entry for details."); + if (context is UnityEngine.Object obj) { + Debug.LogException(ex, obj); + } else { + Debug.LogException(ex); + } + } + + int GetRandomColor(int seed) => GetRandomColor(seed, MinRandomColor, MaxRandomColor, ServerColor); + + static int GetRandomColor(int seed, Color32 min, Color32 max, Color32 svr) { + var random = new NetworkRNG(seed); + int r, g, b; + // -1 indicates host/client - give it a more pronounced color. + if (seed == -1) { + r = svr.r; + g = svr.g; + b = svr.b; + } else { + r = random.RangeInclusive(min.r, max.r); + g = random.RangeInclusive(min.g, max.g); + b = random.RangeInclusive(min.b, max.b); + } + + r = Mathf.Clamp(r, 0, 255); + g = Mathf.Clamp(g, 0, 255); + b = Mathf.Clamp(b, 0, 255); + + int rgb = (r << 16) | (g << 8) | b; + return rgb; + } + + static int Color32ToRGB24(Color32 c) { + return (c.r << 16) | (c.g << 8) | c.b; + } + + static string Color32ToRGBString(Color32 c) { + return string.Format("#{0:X6}", Color32ToRGB24(c)); + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] + static void Initialize() { + if (Fusion.Log.Initialized) { + return; + } + + var logger = new FusionUnityLogger(); + + // Optional override of default values + InitializePartial(ref logger); + + if (logger != null) { + Fusion.Log.Init(logger); + } + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs.meta b/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs.meta new file mode 100644 index 0000000..7e1ad76 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/FusionUnityLogger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e78eab8e13228ac4bad40afc2edfa4fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry.meta new file mode 100644 index 0000000..c80614f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 472d8cb1415b43b49bdf7e5f332c5a5a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor.meta new file mode 100644 index 0000000..e2b1c3d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8f9da9c36cf4db408eae7526562a857 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources.meta new file mode 100644 index 0000000..4a3ba39 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d23000734cb309540a8ed7c84eb4b3e8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset new file mode 100644 index 0000000..8cc6fcf --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: baf430e88a8f39549983d5def5bc8f37, type: 3} + m_Name: TelemetryPopupEditorSettings + m_EditorClassIdentifier: + _showWhenNotRunning: 1 + _globalScale: 0.0288 + _globalDistance: 2 diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset.meta new file mode 100644 index 0000000..a207d2e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Editor/Resources/TelemetryPopupEditorSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea6312fc023528b4486b71db7724b96b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs.meta new file mode 100644 index 0000000..09cf2f2 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bdd3e6925423fbe47a68d01103a71614 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab new file mode 100644 index 0000000..8ff1e4e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab @@ -0,0 +1,1600 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6129187217867618662 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187217867618663} + - component: {fileID: 6129187217867618658} + - component: {fileID: 6129187217867618657} + - component: {fileID: 6129187217867618656} + m_Layer: 0 + m_Name: LayoutPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187217867618663 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217867618662} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187219769373157} + - {fileID: 6129187218506566458} + - {fileID: 6129187218799956728} + - {fileID: 6129187219106237251} + m_Father: {fileID: 6129187218032608079} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -5.100076} + m_SizeDelta: {x: 0, y: -10.199847} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187217867618658 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217867618662} + m_CullTransparentMesh: 1 +--- !u!114 &6129187217867618657 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217867618662} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.16037738, g: 0.16037738, b: 0.16037738, a: 0.7294118} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &6129187217867618656 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217867618662} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 1 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &6129187217941626332 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187217941626333} + - component: {fileID: 6129187217941626335} + - component: {fileID: 6129187217941626334} + m_Layer: 0 + m_Name: TitlePanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187217941626333 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217941626332} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.0000013411045} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187219342970657} + m_Father: {fileID: 6129187218032608079} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 10.2} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &6129187217941626335 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217941626332} + m_CullTransparentMesh: 1 +--- !u!114 &6129187217941626334 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187217941626332} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.28568885, b: 0.28568885, a: 0.392} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &6129187218032608078 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218032608079} + - component: {fileID: 6129187218032608073} + - component: {fileID: 6129187218032608072} + m_Layer: 0 + m_Name: RightPanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218032608079 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218032608078} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187217941626333} + - {fileID: 6129187217867618663} + m_Father: {fileID: 6129187218965061549} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.6972574, y: 0.5778636} + m_AnchorMax: {x: 0.99999994, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187218032608073 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218032608078} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218032608072 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218032608078} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &6129187218043714045 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218043714046} + - component: {fileID: 6129187218043714040} + - component: {fileID: 6129187218043714047} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218043714046 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218043714045} + m_LocalRotation: {x: -0.000000022351742, y: -0.000000009313226, z: -0.000000013504177, + w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00001192093} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187219769373157} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187218043714040 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218043714045} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218043714047 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218043714045} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.08877712, b: 0.08877712, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 7 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: TEST +--- !u!1 &6129187218233732095 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218233732088} + - component: {fileID: 6129187218233732089} + m_Layer: 0 + m_Name: TelemetryPopup (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6129187218233732088 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218233732095} + m_LocalRotation: {x: -0.21236314, y: -0.050924167, z: -0.02334748, w: 0.97558373} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187218965061549} + m_Father: {fileID: 6129187218910475647} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 46.25, y: -30.445, z: 0} +--- !u!114 &6129187218233732089 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218233732095} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 717f997609b652b4192b148f6199f3b0, type: 3} + m_Name: + m_EditorClassIdentifier: + _graphs: [] + _titleHeight: 10.2 + _scale: 0.0414 + _resolution: 0.1 + RegionRect: + serializedVersion: 2 + x: 0.6972574 + y: 0.5778636 + width: 0.3027426 + height: 0.42213643 + _distanceOffset: -2.6 + _canvasRT: {fileID: 6129187218965061549} + _line: {fileID: 6129187218965061551} + _rightPanelRT: {fileID: 6129187218032608079} + _titlePanelRT: {fileID: 6129187217941626333} + _layoutPanelRT: {fileID: 6129187217867618663} +--- !u!1 &6129187218415458781 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218415458782} + - component: {fileID: 6129187218415458776} + - component: {fileID: 6129187218415458783} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218415458782 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218415458781} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187218799956728} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187218415458776 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218415458781} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218415458783 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218415458781} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.7490196, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &6129187218506566457 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218506566458} + - component: {fileID: 6129187218506566454} + - component: {fileID: 6129187218506566453} + - component: {fileID: 6129187218506566452} + - component: {fileID: 6129187218506566459} + m_Layer: 0 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218506566458 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218506566457} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187219061338479} + m_Father: {fileID: 6129187217867618663} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187218506566454 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218506566457} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218506566453 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218506566457} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 4 +--- !u!114 &6129187218506566452 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218506566457} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 6129187218506566453} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &6129187218506566459 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218506566457} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!1 &6129187218799956735 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218799956728} + - component: {fileID: 6129187218799956731} + - component: {fileID: 6129187218799956730} + - component: {fileID: 6129187218799956729} + m_Layer: 0 + m_Name: TelemetryElement + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218799956728 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218799956735} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.0000015795231} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187218415458782} + - {fileID: 6129187219626248122} + m_Father: {fileID: 6129187217867618663} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187218799956731 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218799956735} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218799956730 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218799956735} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!114 &6129187218799956729 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218799956735} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &6129187218910475619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218910475647} + - component: {fileID: 6129187218910475646} + - component: {fileID: 6129187218910475645} + - component: {fileID: 6129187218910475644} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6129187218910475647 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218910475619} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -1.16, y: 3.68, z: -1.73} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187218233732088} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6129187218910475646 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218910475619} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6129187218910475645 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218910475619} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!135 &6129187218910475644 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218910475619} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &6129187218965061548 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187218965061549} + - component: {fileID: 6129187218965061546} + - component: {fileID: 6129187218965061545} + - component: {fileID: 6129187218965061544} + - component: {fileID: 6129187218965061551} + - component: {fileID: 6129187218965061550} + m_Layer: 0 + m_Name: TelemetryCanvas(Clone) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187218965061549 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_LocalRotation: {x: -0.09014511, y: 0.19402614, z: -0.01790831, w: 0.9766817} + m_LocalPosition: {x: 0, y: 0, z: 2.361984} + m_LocalScale: {x: 0.0414, y: 0.0414, z: 1.0000001} + m_Children: + - {fileID: 6129187218032608079} + m_Father: {fileID: 6129187218233732088} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0.9938041, y: 0.4397557} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!223 &6129187218965061546 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 2 + m_Camera: {fileID: 0} + m_PlaneDistance: 2.93 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!222 &6129187218965061545 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_CullTransparentMesh: 1 +--- !u!114 &6129187218965061544 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!120 &6129187218965061551 +LineRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 0 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Positions: + - {x: -1.16, y: 3.68, z: -1.73} + - {x: 0.44591853, y: 5.1056805, z: -0.021409454} + m_Parameters: + serializedVersion: 3 + widthMultiplier: 1 + widthCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0.5 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + colorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.75, b: 0, a: 1} + key1: {r: 1, g: 0.75, b: 0, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + numCornerVertices: 0 + numCapVertices: 0 + alignment: 0 + textureMode: 0 + shadowBias: 0.5 + generateLightingData: 0 + m_UseWorldSpace: 1 + m_Loop: 0 +--- !u!114 &6129187218965061550 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187218965061548} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 1 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 5 + m_PresetInfoIsWorld: 1 +--- !u!1 &6129187219061338478 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219061338479} + - component: {fileID: 6129187219061338473} + - component: {fileID: 6129187219061338472} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219061338479 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219061338478} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187218506566458} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187219061338473 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219061338478} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219061338472 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219061338478} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Button +--- !u!1 &6129187219106237250 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219106237251} + - component: {fileID: 6129187219106237278} + - component: {fileID: 6129187219106237277} + - component: {fileID: 6129187219106237276} + m_Layer: 0 + m_Name: TelemetryElement (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219106237251 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219106237250} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.0000015795231} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187219387844501} + - {fileID: 6129187219212131837} + m_Father: {fileID: 6129187217867618663} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187219106237278 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219106237250} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219106237277 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219106237250} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!114 &6129187219106237276 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219106237250} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.38303638, g: 1, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &6129187219212131836 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219212131837} + - component: {fileID: 6129187219212131839} + - component: {fileID: 6129187219212131838} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219212131837 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219212131836} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00000035369797} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187219106237251} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 4.710022, y: 0} + m_SizeDelta: {x: -14.820001, y: 11.3} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &6129187219212131839 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219212131836} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219212131838 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219212131836} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 12800000, guid: bcf7d23a2d9385449bb7940b203ebbee, type: 3} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 0 + m_MaxSize: 300 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Meter:' +--- !u!1 &6129187219342970656 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219342970657} + - component: {fileID: 6129187219342970659} + - component: {fileID: 6129187219342970658} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219342970657 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219342970656} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187217941626333} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -4, y: -4} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187219342970659 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219342970656} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219342970658 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219342970656} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.18463612, g: 0.6855206, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 0 + m_MaxSize: 100 + m_Alignment: 4 + m_AlignByGeometry: 1 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Player Name +--- !u!1 &6129187219387844500 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219387844501} + - component: {fileID: 6129187219387844503} + - component: {fileID: 6129187219387844502} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219387844501 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219387844500} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187219106237251} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187219387844503 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219387844500} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219387844502 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219387844500} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.7490196, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &6129187219626248121 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219626248122} + - component: {fileID: 6129187219626248116} + - component: {fileID: 6129187219626248123} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6129187219626248122 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219626248121} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00000035369797} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6129187218799956728} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 4.710022, y: 0} + m_SizeDelta: {x: -14.820001, y: 11.3} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &6129187219626248116 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219626248121} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219626248123 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219626248121} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 12800000, guid: bcf7d23a2d9385449bb7940b203ebbee, type: 3} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 0 + m_MaxSize: 300 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Meter:' +--- !u!1 &6129187219769373156 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6129187219769373157} + - component: {fileID: 6129187219769373152} + - component: {fileID: 6129187219769373159} + - component: {fileID: 6129187219769373158} + m_Layer: 0 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &6129187219769373157 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219769373156} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 6129187218043714046} + m_Father: {fileID: 6129187217867618663} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 26.263306, y: -13.968994} + m_SizeDelta: {x: 52.526596, y: 27.93796} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6129187219769373152 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219769373156} + m_CullTransparentMesh: 1 +--- !u!114 &6129187219769373159 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219769373156} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &6129187219769373158 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6129187219769373156} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab.meta new file mode 100644 index 0000000..fe6eb37 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/Sphere.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 694ea62e65e707e49915f6d1b90a732a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab new file mode 100644 index 0000000..3bb5618 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab @@ -0,0 +1,1046 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1649585381495412213 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8095324492617866804} + - component: {fileID: 459055240341442764} + - component: {fileID: 2795118350387174343} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8095324492617866804 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1649585381495412213} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 5598688016629379855} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &459055240341442764 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1649585381495412213} + m_CullTransparentMesh: 1 +--- !u!114 &2795118350387174343 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1649585381495412213} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Button +--- !u!1 &2437056955632664118 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6163766924881061448} + - component: {fileID: 4893120739808861607} + - component: {fileID: 6242814190080636628} + - component: {fileID: 7730371192831621390} + - component: {fileID: 4878637178368018590} + - component: {fileID: 3561279976124909103} + - component: {fileID: 8269369672812425753} + m_Layer: 0 + m_Name: TelemetryCanvas(Clone) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6163766924881061448 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_LocalRotation: {x: -0, y: -0.000000044703473, z: 0.000000007450579, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 3849314500496318506} + - {fileID: 5492404702293212969} + - {fileID: 5598688016629379855} + - {fileID: 2870056302528189662} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -25.000002, y: 24.999998} + m_SizeDelta: {x: -50, y: -50} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!223 &4893120739808861607 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 2 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &6242814190080636628 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 4 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 1 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!222 &7730371192831621390 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_CullTransparentMesh: 1 +--- !u!114 &4878637178368018590 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!120 &3561279976124909103 +LineRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 0 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Positions: + - {x: 2.89, y: 1.2365646, z: 1.0142264} + - {x: 3.010961, y: 2.3943927, z: 0.1422152} + m_Parameters: + serializedVersion: 3 + widthMultiplier: 1 + widthCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0.5 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + colorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.75, b: 0, a: 1} + key1: {r: 1, g: 0.75, b: 0, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + numCornerVertices: 0 + numCapVertices: 0 + alignment: 0 + textureMode: 0 + shadowBias: 0.5 + generateLightingData: 0 + m_UseWorldSpace: 1 + m_Loop: 0 +--- !u!114 &8269369672812425753 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2437056955632664118} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 5 + m_PresetInfoIsWorld: 0 +--- !u!1 &2461167326332208122 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5492404702293212969} + - component: {fileID: 1617212434532148355} + - component: {fileID: 402714333433559905} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &5492404702293212969 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2461167326332208122} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4950866391927648708} + m_Father: {fileID: 6163766924881061448} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 468.125, y: -227.66} + m_SizeDelta: {x: 936.25, y: 154.84} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1617212434532148355 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2461167326332208122} + m_CullTransparentMesh: 1 +--- !u!114 &402714333433559905 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2461167326332208122} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &3606148801588235070 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6782058877681004172} + - component: {fileID: 2880431040921018623} + - component: {fileID: 7609636958674738766} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6782058877681004172 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3606148801588235070} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00000035369797} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2870056302528189662} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 4.710022, y: 0} + m_SizeDelta: {x: -14.820001, y: 11.3} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &2880431040921018623 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3606148801588235070} + m_CullTransparentMesh: 1 +--- !u!114 &7609636958674738766 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3606148801588235070} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 12800000, guid: bcf7d23a2d9385449bb7940b203ebbee, type: 3} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 0 + m_MaxSize: 300 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: 'Meter:' +--- !u!1 &3723090724673321593 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5189714714993388630} + - component: {fileID: 1411325830388047588} + - component: {fileID: 1940381718726377267} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5189714714993388630 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3723090724673321593} + m_LocalRotation: {x: -0.000000022351742, y: -0.000000009313226, z: -0.000000013504177, + w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00001192093} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 3849314500496318506} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1411325830388047588 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3723090724673321593} + m_CullTransparentMesh: 1 +--- !u!114 &1940381718726377267 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3723090724673321593} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.08877712, b: 0.08877712, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 7 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: TEST +--- !u!1 &4642639782424184119 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5462299118947836205} + - component: {fileID: 2220451531258579511} + - component: {fileID: 2137736735810377581} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5462299118947836205 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4642639782424184119} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2870056302528189662} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &2220451531258579511 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4642639782424184119} + m_CullTransparentMesh: 1 +--- !u!114 &2137736735810377581 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4642639782424184119} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: a59e246710cfeea44919def1d52ac8ca, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &6361818577994080018 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3849314500496318506} + - component: {fileID: 6001498755134154726} + - component: {fileID: 7071697147628670408} + - component: {fileID: 324337104493450945} + m_Layer: 0 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &3849314500496318506 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6361818577994080018} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.000009536744} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5189714714993388630} + m_Father: {fileID: 6163766924881061448} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 33.65, y: -16.825} + m_SizeDelta: {x: 67.3, y: 33.65} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6001498755134154726 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6361818577994080018} + m_CullTransparentMesh: 1 +--- !u!114 &7071697147628670408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6361818577994080018} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &324337104493450945 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6361818577994080018} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!1 &6637645689261092147 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5598688016629379855} + - component: {fileID: 3926599443818664042} + - component: {fileID: 4155994817201808693} + - component: {fileID: 666098088240576611} + - component: {fileID: 5630506602785477392} + m_Layer: 0 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &5598688016629379855 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6637645689261092147} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8095324492617866804} + m_Father: {fileID: 6163766924881061448} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 468.125, y: -305.08002} + m_SizeDelta: {x: 936.25, y: 37} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3926599443818664042 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6637645689261092147} + m_CullTransparentMesh: 1 +--- !u!114 &4155994817201808693 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6637645689261092147} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 4 +--- !u!114 &666098088240576611 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6637645689261092147} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4155994817201808693} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &5630506602785477392 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6637645689261092147} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!1 &7736669399213902855 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4950866391927648708} + - component: {fileID: 3989862768570389076} + - component: {fileID: 1440725753929590170} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4950866391927648708 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7736669399213902855} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 5492404702293212969} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &3989862768570389076 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7736669399213902855} + m_CullTransparentMesh: 1 +--- !u!114 &1440725753929590170 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7736669399213902855} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.18463612, g: 0.6855206, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 10 + m_MaxSize: 100 + m_Alignment: 4 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Title +--- !u!1 &7857981542358865609 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2870056302528189662} + - component: {fileID: 4133621046628240780} + - component: {fileID: 7809965208149630476} + - component: {fileID: 464090746496504828} + m_Layer: 0 + m_Name: TelemetryElement + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2870056302528189662 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7857981542358865609} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -4.4667725e-23} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5462299118947836205} + - {fileID: 6782058877681004172} + m_Father: {fileID: 6163766924881061448} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4133621046628240780 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7857981542358865609} + m_CullTransparentMesh: 1 +--- !u!114 &7809965208149630476 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7857981542358865609} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!114 &464090746496504828 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7857981542358865609} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab.meta new file mode 100644 index 0000000..9f35dff --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas(Clone).prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e883cfaae5e4c224e9db082be44fbb71 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab new file mode 100644 index 0000000..059e56f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab @@ -0,0 +1,996 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4050433373216904987 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5790405949671934019} + - component: {fileID: 5693258621346189753} + - component: {fileID: 5057428781961270072} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &5790405949671934019 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4050433373216904987} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1116094144843595946} + m_Father: {fileID: 4965931653615153650} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 154.84} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5693258621346189753 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4050433373216904987} + m_CullTransparentMesh: 1 +--- !u!114 &5057428781961270072 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4050433373216904987} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 0.392} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &4965931653413297163 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4965931653413297162} + - component: {fileID: 4965931653413297207} + - component: {fileID: 4965931653413297204} + - component: {fileID: 4965931653413297205} + m_Layer: 0 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &4965931653413297162 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653413297163} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -0.000009536744} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4965931654022505014} + m_Father: {fileID: 4965931653615153650} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 33.65, y: -16.825} + m_SizeDelta: {x: 67.3, y: 33.65} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4965931653413297207 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653413297163} + m_CullTransparentMesh: 1 +--- !u!114 &4965931653413297204 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653413297163} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4965931653413297205 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653413297163} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!1 &4965931653524362699 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4965931653524362698} + - component: {fileID: 4965931653524362740} + - component: {fileID: 4965931653524362741} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4965931653524362698 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653524362699} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4965931653788972680} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4965931653524362740 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653524362699} + m_CullTransparentMesh: 1 +--- !u!114 &4965931653524362741 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653524362699} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Button +--- !u!1 &4965931653615153651 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4965931653615153650} + - component: {fileID: 4965931653615153656} + - component: {fileID: 4965931653615153662} + - component: {fileID: 4965931653615153663} + - component: {fileID: 4965931653615153660} + - component: {fileID: 4735998557858774319} + - component: {fileID: 3046511910880231108} + m_Layer: 0 + m_Name: TelemetryCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4965931653615153650 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4965931653413297162} + - {fileID: 5790405949671934019} + - {fileID: 4965931653788972680} + - {fileID: 4965931653654997002} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: -50, y: 50} + m_SizeDelta: {x: -100, y: -100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!223 &4965931653615153656 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 2 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &4965931653615153662 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_ChildAlignment: 0 + m_Spacing: 0 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 1 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!222 &4965931653615153663 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_CullTransparentMesh: 1 +--- !u!114 &4965931653615153660 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 0 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!120 &4735998557858774319 +LineRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 0 + m_LightProbeUsage: 0 + m_ReflectionProbeUsage: 0 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Positions: + - {x: 0, y: 0, z: 0} + - {x: 1.7409998, y: 0.9684151, z: 0.6694432} + m_Parameters: + serializedVersion: 3 + widthMultiplier: 1 + widthCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0.5 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: -0.5 + outSlope: -0.5 + tangentMode: 34 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + colorGradient: + serializedVersion: 2 + key0: {r: 1, g: 0.75, b: 0, a: 1} + key1: {r: 1, g: 0.75, b: 0, a: 1} + key2: {r: 0, g: 0, b: 0, a: 0} + key3: {r: 0, g: 0, b: 0, a: 0} + key4: {r: 0, g: 0, b: 0, a: 0} + key5: {r: 0, g: 0, b: 0, a: 0} + key6: {r: 0, g: 0, b: 0, a: 0} + key7: {r: 0, g: 0, b: 0, a: 0} + ctime0: 0 + ctime1: 65535 + ctime2: 0 + ctime3: 0 + ctime4: 0 + ctime5: 0 + ctime6: 0 + ctime7: 0 + atime0: 0 + atime1: 65535 + atime2: 0 + atime3: 0 + atime4: 0 + atime5: 0 + atime6: 0 + atime7: 0 + m_Mode: 1 + m_NumColorKeys: 2 + m_NumAlphaKeys: 2 + numCornerVertices: 0 + numCapVertices: 0 + alignment: 0 + textureMode: 0 + shadowBias: 0.5 + generateLightingData: 0 + m_UseWorldSpace: 1 + m_Loop: 0 +--- !u!114 &3046511910880231108 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653615153651} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 5 + m_PresetInfoIsWorld: 0 +--- !u!1 &4965931653788972681 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4965931653788972680} + - component: {fileID: 4965931653788972724} + - component: {fileID: 4965931653788972725} + - component: {fileID: 4965931653788972682} + - component: {fileID: 4965931653788972683} + m_Layer: 0 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4965931653788972680 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653788972681} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 4965931653524362698} + m_Father: {fileID: 4965931653615153650} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 10} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4965931653788972724 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653788972681} + m_CullTransparentMesh: 1 +--- !u!114 &4965931653788972725 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653788972681} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 4 +--- !u!114 &4965931653788972682 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653788972681} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 4965931653788972725} + m_OnClick: + m_PersistentCalls: + m_Calls: [] +--- !u!114 &4965931653788972683 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931653788972681} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!1 &4965931654022505015 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4965931654022505014} + - component: {fileID: 4965931654022505008} + - component: {fileID: 4965931654022505009} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4965931654022505014 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931654022505015} + m_LocalRotation: {x: -0.000000022351742, y: -0.000000009313226, z: -0.000000013504177, + w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00001192093} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 4965931653413297162} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4965931654022505008 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931654022505015} + m_CullTransparentMesh: 1 +--- !u!114 &4965931654022505009 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4965931654022505015} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.990566, g: 0.08877712, b: 0.08877712, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 7 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 1 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: TEST +--- !u!1 &8271693224669904963 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1116094144843595946} + - component: {fileID: 4183032438240638029} + - component: {fileID: 7138871482072797074} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1116094144843595946 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8271693224669904963} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 5790405949671934019} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &4183032438240638029 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8271693224669904963} + m_CullTransparentMesh: 1 +--- !u!114 &7138871482072797074 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8271693224669904963} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.18463612, g: 0.6855206, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 10 + m_MaxSize: 100 + m_Alignment: 4 + m_AlignByGeometry: 1 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Title +--- !u!1001 &1675980929958536163 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 4965931653615153650} + m_Modifications: + - target: {fileID: 6028295983316110312, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Name + value: TelemetryElement + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Pivot.x + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Pivot.y + value: 0.5 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_RootOrder + value: 3 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMax.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMax.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMin.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_SizeDelta.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_SizeDelta.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalRotation.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalRotation.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalRotation.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7465905141727143500, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Text + value: 'Meter:' + objectReference: {fileID: 0} + - target: {fileID: 7465905141727143500, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Enabled + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 7465905141727143500, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Material + value: + objectReference: {fileID: 0} + - target: {fileID: 7465905141727143500, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_FontData.m_BestFit + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_Pivot.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMax.x + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMax.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMin.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchorMin.y + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_SizeDelta.x + value: -14.820001 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_SizeDelta.y + value: 11.3 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_LocalPosition.z + value: 0.00000035369797 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchoredPosition.x + value: 4.710022 + objectReference: {fileID: 0} + - target: {fileID: 8044273962773963535, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + propertyPath: m_AnchoredPosition.y + value: 0 + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: b8ad69dae7d958645960cdee599803bb, type: 3} +--- !u!224 &4965931653654997002 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 6028295983316110313, guid: b8ad69dae7d958645960cdee599803bb, + type: 3} + m_PrefabInstance: {fileID: 1675980929958536163} + m_PrefabAsset: {fileID: 0} diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab.meta new file mode 100644 index 0000000..63f265f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryCanvas.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 05ad9a61e553ee349a615ea24af6dce9 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab new file mode 100644 index 0000000..59341dd --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab @@ -0,0 +1,247 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1094618816761655250 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8044273962773963535} + - component: {fileID: 8239875000799919310} + - component: {fileID: 7465905141727143500} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8044273962773963535 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1094618816761655250} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0.00000020861626} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6028295983316110313} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 30} + m_Pivot: {x: 0.5, y: 1} +--- !u!222 &8239875000799919310 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1094618816761655250} + m_CullTransparentMesh: 1 +--- !u!114 &7465905141727143500 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1094618816761655250} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 2100000, guid: bcf7d23a2d9385449bb7940b203ebbee, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 12800000, guid: bcf7d23a2d9385449bb7940b203ebbee, type: 3} + m_FontSize: 12 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 300 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: New Text +--- !u!1 &6028295983316110312 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6028295983316110313} + - component: {fileID: 6028295983316110292} + - component: {fileID: 6028295983316110295} + - component: {fileID: 6028295983316110294} + m_Layer: 0 + m_Name: TelemetryElementPlaceholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6028295983316110313 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6028295983316110312} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7907516266142914393} + - {fileID: 8044273962773963535} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 50} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6028295983316110292 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6028295983316110312} + m_CullTransparentMesh: 1 +--- !u!114 &6028295983316110295 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6028295983316110312} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8f9f435ffcc4e8345a40c30cd37f1019, type: 3} + m_Name: + m_EditorClassIdentifier: + AspectRatio: 2 +--- !u!114 &6028295983316110294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6028295983316110312} + m_Enabled: 0 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 +--- !u!1 &9088670746883931521 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907516266142914393} + - component: {fileID: 453050132478351308} + - component: {fileID: 8595218430796329136} + m_Layer: 0 + m_Name: Panel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &7907516266142914393 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9088670746883931521} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 6028295983316110313} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &453050132478351308 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9088670746883931521} + m_CullTransparentMesh: 1 +--- !u!114 &8595218430796329136 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9088670746883931521} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 0.75, b: 0, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 0 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: a59e246710cfeea44919def1d52ac8ca, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 5 diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab.meta new file mode 100644 index 0000000..aa48179 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPlaceholder.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b8ad69dae7d958645960cdee599803bb +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs new file mode 100644 index 0000000..428ddfa --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs @@ -0,0 +1,63 @@ +#if UNITY_EDITOR + +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Fusion.Assistants { + public static class TelemetryPrefabs { + + //private static TelemetryDevelopmentCanvas _devPopupCanvas; + + //public static TelemetryDevelopmentCanvas DevelopmentPopupCanvas { + // get { + // if (_devPopupCanvas != null) + // return _devPopupCanvas; + + // var found = Object.FindObjectsOfType(true); + // for (int i = 0; i < found.Length; ++i){ + // var devcanvas = found[i]; + // if (i == found.Length - 1) { + // _devPopupCanvas = devcanvas; + // return devcanvas; + // } else { + // // Destroy extra found dev canvases. + // Object.Destroy(devcanvas); + // } + // } + + // _devPopupCanvas = (PrefabUtility.InstantiatePrefab(TelemetryCanvas) as GameObject).AddComponent(); + // return _devPopupCanvas; + // } + //} + + + // Default Element + private const string _defaultGuid = "b8ad69dae7d958645960cdee599803bb"; + private static GameObject _default; + public static GameObject Default { get { return _defaultGuid.GetAssetFromGuid(ref _default); } } + + // Default complete popup prefab + private const string _telemetryPopupGuid = "c3854702886c370448adaa8a2e2e47a5"; + private static GameObject _telemetryPopup; + public static GameObject TelemetryPopup { get { return _telemetryPopupGuid.GetAssetFromGuid(ref _telemetryPopup); } } + + // Default telemetry canvas group + private const string _telemetryCanvasGuid = "05ad9a61e553ee349a615ea24af6dce9"; + private static GameObject _telemetryCanvas; + public static GameObject TelemetryCanvas { get { return _telemetryCanvasGuid.GetAssetFromGuid(ref _telemetryCanvas); } } + + + private static T GetAssetFromGuid(this string Guid, ref T backing) where T : UnityEngine.Object { + if (backing != null) + return backing; + + var path = AssetDatabase.GUIDToAssetPath(Guid); + if (path != null && path != "") + backing = AssetDatabase.LoadAssetAtPath(path); + return backing; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs.meta new file mode 100644 index 0000000..1406d9a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/Prefabs/TelemetryElementPrefabs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b9169d6133ef783499c6ec6974077bd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs new file mode 100644 index 0000000..626f0b9 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs @@ -0,0 +1,100 @@ +//using System.Collections; +//using System.Collections.Generic; +//using UnityEngine; +//using Fusion.Assistants; + +//#if UNITY_EDITOR +//using UnityEditor; +//#endif + +////[ExecuteInEditMode] +//public class TelemetryDevelopmentCanvas : MonoBehaviour { + +// public void Awake() { +// if (Application.isPlaying) +// Destroy(this); +// } + +// private static TelemetryDevelopmentCanvas _instance; +// public static TelemetryDevelopmentCanvas Instance { + +// get { +// if (_instance != null) { +// return _instance; +// } + +// var found = Object.FindObjectsOfType(true); +// for (int i = 0; i < found.Length; ++i) { +// var devcanvas = found[i]; +// if (i == found.Length - 1) { +// _instance = devcanvas; +// return devcanvas; +// } else { +// // Destroy extra found dev canvases. +// Object.Destroy(devcanvas); +// } +// } +// _instance = (PrefabUtility.InstantiatePrefab(TelemetryPrefabs.TelemetryCanvas) as GameObject).AddComponent(); +// return _instance; +// } +// } + +// private static RectTransform _rectTransform; +// public static RectTransform RectTransform { +// get { +// if (_rectTransform) +// return _rectTransform; + +// _rectTransform = Instance.GetComponent(); +// return _rectTransform; +// } +// } + +// private static LineRenderer _lineRenderer; +// public static LineRenderer LineRenderer { +// get { +// if (_lineRenderer) +// return _lineRenderer; + +// _lineRenderer = Instance.GetComponent(); +// return _lineRenderer; +// } +// } + +//#if UNITY_EDITOR + +// [UnityEditor.Callbacks.DidReloadScripts] +// public static void OnCompile() { +// Selection.selectionChanged -= SelectionChanged; +// Selection.selectionChanged += SelectionChanged; +// } + +// //[InitializeOnLoadMethod] +// //public static void OnLoad() { +// // Selection.selectionChanged -= SelectionChanged; +// // Selection.selectionChanged += SelectionChanged; +// //} + +// ////[InitializeOnLoadMethod] +// //void OnEnable() { +// // Debug.LogWarning("Register"); +// // Selection.selectionChanged -= SelectionChanged; +// // Selection.selectionChanged += SelectionChanged; +// //} + +// //void OnDisable() { +// // Selection.selectionChanged -= SelectionChanged; +// //} + +// public static void SelectionChanged() { +// var selected = Selection.activeGameObject; +// if (selected != null && selected.TryGetComponent(out var found)) { +// } else { +// Instance.gameObject.SetActive(false); +// } +// } + + +//#endif + +//} diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs.meta new file mode 100644 index 0000000..a3eb149 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryDevelopmentCanvas.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e59292251e635848abc94636c29b792 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs new file mode 100644 index 0000000..dac2aa8 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs @@ -0,0 +1,29 @@ +using UnityEngine; + +//[ExecuteInEditMode] +public class TelemetryElement : MonoBehaviour { + + /// + /// The aspect ratio this element should maintain. 2 = twice as wide as tall. + /// + public float AspectRatio = 2; + + // cached + RectTransform _rectTrans; + float _prevWidth; + + void Update() { + + if (_rectTrans == null) + _rectTrans = GetComponent(); + + // Rescale the element to maintain the desired aspect ratio + var newWidth = _rectTrans.sizeDelta.x; + if (newWidth != _prevWidth) { + _rectTrans.sizeDelta = new Vector2(newWidth, newWidth / AspectRatio); + _prevWidth = newWidth; + } + + + } +} diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs.meta new file mode 100644 index 0000000..d5befc9 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryElement.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8f9f435ffcc4e8345a40c30cd37f1019 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs new file mode 100644 index 0000000..967e264 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs @@ -0,0 +1,201 @@ + +//using UnityEngine; +//using Fusion; + +//#if UNITY_EDITOR +//using UnityEditor; +//using Fusion.Editor; +//using Fusion.Assistants; +//#endif + +////[ExecuteInEditMode] +//public class TelemetryOverlay : Fusion.NetworkBehaviour { + +// //public bool UseGlobalScale; +// //public bool UseGlobalDistance; + +// [SerializeField] +// [Range(0, 100f)] +// private float _titleHeight = 60f; + +// [SerializeField] +// //[HideInInspector] +// [Range(0f, .1f)] +// private float _scale = .1f; + +// [SerializeField] +// //[HideInInspector] +// [Unit(Units.Units, -10f, 10f)] +// private float _distanceOffset = 1f; + + +// Rect _regionRect; +// private bool _layoutIsDirty; + +//#if UNITY_EDITOR + +// private void OnValidate() { +// _layoutIsDirty = true; +// Update(); +// } + +// [MenuItem("GameObject/Fusion/Telemetry/Add Telemetry Popup", false, FusionAssistants.PRIORITY)] +// private static void AddTelemetryAssist() { +// var selected = Selection.activeGameObject; +// if (selected == null) +// return; + +// var bounds = selected.gameObject.CollectMyBounds(BoundsTools.BoundsType.Both, out int dummy); + +// var go = (GameObject)PrefabUtility.InstantiatePrefab(TelemetryPrefabs.TelemetryPopup, selected.transform); +// var telePopup = go.GetComponent(); +// //telePopup.UseGlobalDistance = false; +// var extents = bounds.extents; +// } + +//#endif + +// [SerializeField] RectTransform _testRect; + +// [SerializeField] Canvas _canvas; +// [SerializeField] RectTransform _canvasRT; +// [SerializeField] LineRenderer _line; +// [SerializeField] RectTransform _rightPanelRT; +// [SerializeField] RectTransform _titlePanelRT; +// [SerializeField] RectTransform _layoutPanelRT; + +// float _previousScale; + +// // Camera find is expensive, so do it once per update for ALL implementations +// static float _lastCameraFindTime; +// static Camera _currentCam; + +// Camera MainCamera { +// set { +// _currentCam = value; +// } +// get { + +// var time = Time.time; +// // Only look for the camera once per Update. +// if (time == _lastCameraFindTime) +// return _currentCam; + +// _lastCameraFindTime = time; +// var cam = Camera.main; +// _currentCam = cam; +// return cam; +// } +// } + + +////#if UNITY_EDITOR + +// [BehaviourButtonAction("Generate Popup")] +// private void Initialize() { + +// if (_canvasRT == null) { +// // Generate popup boilerplate here. +// ApplyDistance(); +// ApplyScale(); +// } +// } + +// public void Update() { + +// if (_canvasRT == null) { +// return; +// } + +// var cam = MainCamera; +// if (cam == null) { +// return; +// } + +// // Scene cam not available yet or is shutting down... nothing to do. +// if (cam == null) +// return; + +// //var dot = Vector3.Dot(cam.transform.forward, transform.position); +// //if (dot > 0) { +// // _canvas.enabled = false; +// // //return; +// //} else { +// // _canvas.enabled = true; +// //} +// var dist = Vector3.Distance(cam.transform.position, transform.position); +// _canvas.planeDistance = dist - _distanceOffset; +// //Debug.LogWarning(_canvas.planeDistance); +// //Debug.LogWarning(_canvas.planeDistance); + +// var no = Object ? Object : transform.GetComponentInParent(); +// if (no == null) { +// return; +// } + +// const float HEIGHT = .1f; +// const float WIDTH = .1f; + +// var screenpoint = cam.WorldToScreenPoint(no.transform.position); +// _regionRect.Set(screenpoint.x / cam.pixelWidth, screenpoint.y / cam.pixelHeight, WIDTH, HEIGHT); +// ApplyLayout(); +// } + +//#if UNITY_EDITOR +// private void OnDrawGizmos () { + +// if (_canvasRT) { +// if (_layoutIsDirty) { +// ApplyLayout(); +// ApplyDistance(); +// ApplyScale(); +// } +// Update(); +// } +// } +//#endif + +// private static Vector3[] worldCornersNonalloc = new Vector3[4]; + + +// public void ApplyLayout() { +// _rightPanelRT.anchorMin = new Vector2(_regionRect.xMin, _regionRect.yMin); +// _rightPanelRT.anchorMax = new Vector2(_regionRect.xMax, _regionRect.yMax); +// _rightPanelRT.offsetMin = default; +// _rightPanelRT.offsetMax = default; +// _titlePanelRT.sizeDelta = new Vector2(0, _titleHeight); +// _layoutPanelRT.offsetMax = new Vector2(0, -_titleHeight); +// _layoutIsDirty = false; +// } + + +// public void ApplyScale() { + +// //var scale = _scale; + +// //if (scale == _previousScale) +// // return; + +// //var holdrot = _canvasRT.rotation; +// //_canvasRT.rotation = Quaternion.identity; +// //var lossy = transform.lossyScale; +// //_canvasRT.localScale = new Vector3(scale / lossy.x, scale/ lossy.y, 1 / lossy.z); +// //_canvasRT.rotation = holdrot; +// } + +// public void ApplyDistance() { + +// ////_canvasRT.position = transform.position + (transform.rotation * new Vector3(dist, dist, -dist)); +// //_canvasRT.position = transform.position + _canvasRT.forward * -_distanceOffset; +// } + +// public void ApplyConnector(LineRenderer line, RectTransform canvasRectTransform) { + +// _layoutPanelRT.GetWorldCorners(worldCornersNonalloc); +// // make line connect center of object with corner of popup canvas +// line.SetPosition(0, transform.position); +// //line.SetPosition(1, worldCornersNonalloc[0]); +// line.SetPosition(1, worldCornersNonalloc[0]); +// //line.SetPosition(1, canvasRectTransform.position); +// } +//} diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs.meta new file mode 100644 index 0000000..a66dd92 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryOverlay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c60d52b628bf8514d936498015c1b1c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs new file mode 100644 index 0000000..89be8b3 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs @@ -0,0 +1,220 @@ + +//using UnityEngine; +//using Fusion; + +//#if UNITY_EDITOR +//using UnityEditor; +//using Fusion.Editor; +//using Fusion.Assistants; +//#endif + +////[ExecuteInEditMode] +//public class TelemetryPopup : Fusion.Behaviour { + +// //public bool UseGlobalScale; +// //public bool UseGlobalDistance; + +// [SerializeField] FusionGraph[] _graphs; + +// [SerializeField] +// [Range(0, 100f)] +// private float _titleHeight = 60f; + +// [SerializeField] +// //[HideInInspector] +// [Range(0f, 100f)] +// private float _scale = 5f; + +// [SerializeField] +// //[HideInInspector] +// [Range(10f, 2048f)] +// private float _resolution = 1024; + +// [SerializeField] +// [NormalizedRect] +// Rect RegionRect = Rect.MinMaxRect(0.6f, 0.5f, 1f, 1f); + +// [SerializeField] +// //[HideInInspector] +// [Range(-10f, 10f)] +// private float _distanceOffset = 0f; + +// private bool _layoutIsDirty; + +//#if UNITY_EDITOR + +// private void OnValidate() { +// _layoutIsDirty = true; +// Update(); +// } + +// [MenuItem("GameObject/Fusion/Telemetry/Add Telemetry Popup", false, FusionAssistants.PRIORITY)] +// private static void AddTelemetryAssist() { +// var selected = Selection.activeGameObject; +// if (selected == null) +// return; + +// var bounds = selected.gameObject.CollectMyBounds(BoundsTools.BoundsType.Both, out int dummy); + +// var go = (GameObject)PrefabUtility.InstantiatePrefab(TelemetryPrefabs.TelemetryPopup, selected.transform); +// var telePopup = go.GetComponent(); +// //telePopup.UseGlobalDistance = false; +// var extents = bounds.extents; +// telePopup._distanceOffset = System.Math.Max(extents.x, (System.Math.Max(extents.y, extents.z))) * 1.25f; +// } + +//#endif + +// //[SerializeField] GameObject _canvas; +// [SerializeField] RectTransform _canvasRT; +// [SerializeField] LineRenderer _line; +// [SerializeField] RectTransform _rightPanelRT; +// [SerializeField] RectTransform _titlePanelRT; +// [SerializeField] RectTransform _layoutPanelRT; + +// float _previousScale; +// float _previousResol; + +// // Camera find is expensive, so do it once per update for ALL implementations +// static float _lastCameraFindTime; +// static Camera _currentCam; + +// Camera MainCamera { +// set { +// _currentCam = value; +// } +// get { + +// var time = Time.time; +// // Only look for the camera once per Update. +// if (time == _lastCameraFindTime) +// return _currentCam; + +// _lastCameraFindTime = time; +// var cam = Camera.main; +// _currentCam = cam; +// return cam; +// } +// } + + +////#if UNITY_EDITOR + +// [BehaviourButtonAction("Generate Popup")] +// private void Initialize() { + +// if (_canvasRT == null) { +// // Generate popup boilerplate here. +// ApplyDistance(); +// ApplyScale(); +// } +// } + +// public void Update() { + +// if (_canvasRT == null) { +// return; +// } + +// var cam = MainCamera; + +// // Scene cam not available yet or is shutting down... nothing to do. +// if (cam == null) +// return; + +// ApplyConnector(_line, _canvasRT); + +// // Adjust the canvas height to match the telemetry elements inside of it. +// //SetCanvasHeightBasedOnElements(_canvasRT); + +// var armOffset = transform.position - cam.transform.position; + +// transform.LookAt(transform.position + armOffset, cam.transform.up); +// //transform.LookAt(transform.position + armOffset, cam.transform.up); +// } + +// private void LateUpdate() { +// foreach (var graph in _graphs) { +// if (graph != null) { +// graph.Refresh(); +// } +// } +// } + + +//#if UNITY_EDITOR +// private void OnDrawGizmos () { + +// if (_canvasRT) { +// if (_layoutIsDirty) { +// ApplyLayout(); +// ApplyDistance(); +// ApplyScale(); + + +// } +// Update(); +// } +// } +//#endif + +// private static Vector3[] worldCornersNonalloc = new Vector3[4]; + + +// public void ApplyLayout() { +// _rightPanelRT.anchorMin = new Vector2(RegionRect.xMin, RegionRect.yMin); +// _rightPanelRT.anchorMax = new Vector2(RegionRect.xMax, RegionRect.yMax); +// _titlePanelRT.sizeDelta = new Vector2(0, _titleHeight); +// _layoutPanelRT.offsetMax = new Vector2(0, -_titleHeight); +// _layoutIsDirty = false; + +// Debug.LogWarning("Apply Layout to graphs"); +// if (_graphs != null && _graphs.Length > 0) { +// for (int i = 0; i < _graphs.Length; ++i) { +// var graph = _graphs[i]; +// if (graph == null) { +// continue; +// } +// graph.CalculateLayout(); +// } +// } +// } + + +// public void ApplyScale() { + +// //var scale = _scale; +// //var resol = _resolution; + +// if (_scale == _previousScale && _resolution == _previousResol) +// return; + +// _previousResol = _resolution; +// _previousScale = _scale; + +// _canvasRT.sizeDelta = new Vector2(_resolution, _resolution); + +// var scale = _scale / _resolution; +// var holdrot = _canvasRT.rotation; +// _canvasRT.rotation = Quaternion.identity; +// var lossy = transform.lossyScale; +// _canvasRT.localScale = new Vector3(scale /*/ lossy.x*/, scale /*/ lossy.y*/, 1 /*/ lossy.z*/); +// _canvasRT.rotation = holdrot; +// } + +// public void ApplyDistance() { + +// //_canvasRT.position = transform.position + (transform.rotation * new Vector3(dist, dist, -dist)); +// _canvasRT.position = transform.position + _canvasRT.forward * -_distanceOffset; +// } + +// public void ApplyConnector(LineRenderer line, RectTransform canvasRectTransform) { + +// _layoutPanelRT.GetWorldCorners(worldCornersNonalloc); +// // make line connect center of object with corner of popup canvas +// line.SetPosition(0, transform.position); +// //line.SetPosition(1, worldCornersNonalloc[0]); +// line.SetPosition(1, worldCornersNonalloc[0]); +// //line.SetPosition(1, canvasRectTransform.position); +// } +//} diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs.meta new file mode 100644 index 0000000..55b744b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 717f997609b652b4192b148f6199f3b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs new file mode 100644 index 0000000..0cd50fa --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs @@ -0,0 +1,105 @@ + +//using UnityEditor; +//using UnityEngine; + +//#if FUSION_DEV +//[CreateAssetMenu(fileName = nameof(TelemetryPopupEditorSettings), menuName = "Fusion/Telemetry Popup Editor Settings", order = 1)] +//#endif +//public class TelemetryPopupEditorSettings : ScriptableObject +//{ + +// [SerializeField] +// bool _showWhenNotRunning = true; +// public bool ShowWhenNotRunning { +// get { +// return _showWhenNotRunning; +// } +// set { +// if (_showWhenNotRunning == value) +// return; + +// Undo.RecordObject(this, "Toggle Show Popups When Not Running"); +// _showWhenNotRunning = value; +// EditorUtility.SetDirty(this); +// UpdateAllShowWhenNotRunning(); +// //AssetDatabase.SaveAssets(); +// } +// } + +// [Range(0f, .1f)] +// [SerializeField] +// private float _globalScale = 100f; +// public float GlobalScale { +// get { return _globalScale; } +// set { +// if (_globalScale == value) +// return; +// Debug.Log("SetGlobal scale " + value); +// Undo.RecordObject(this, "Change Global Telemetry Popup Scale"); +// _globalScale = value; +// EditorUtility.SetDirty(this); +// //AssetDatabase.SaveAssets(); +// UpdateAllPopupScaling(); +// } +// } + +// [Range(0f, 10f)] +// [SerializeField] +// private float _globalDistance = 2f; +// public float GlobalDistance { +// get { return _globalDistance; } +// set { +// if (value == _globalDistance) +// return; + +// Undo.RecordObject(this, "Change Global Telemetry Popup Distance"); +// _globalDistance = value; +// EditorUtility.SetDirty(this); +// //AssetDatabase.SaveAssets(); +// UpdateAllPopupDistances(); +// } +// } + +// //[InitializeOnLoadMethod] +// private void UpdateAllShowWhenNotRunning() { +// var popups = FindObjectsOfType(true); +// foreach (var p in popups) { +// p.SetChildrenEnabled(ShowWhenNotRunning); +// } +// } + +// private void UpdateAllPopupScaling() { +// var popups = FindObjectsOfType(true); +// foreach (var p in popups) { +// if (p.UseGlobalScale) { +// p.ApplyScale(); +// } +// } +// } + +// private void UpdateAllPopupDistances() { +// var popups = FindObjectsOfType(true); +// foreach (var p in popups) { +// if (p.UseGlobalDistance) { +// p.ApplyDistance(); +// } +// } +// } + + + +// static TelemetryPopupEditorSettings _instance; + +// // Standard singleton enforcement. +// public static TelemetryPopupEditorSettings Instance { +// get { +// if (_instance) { +// return _instance; +// } +// _instance = Resources.Load(nameof(TelemetryPopupEditorSettings)); + +// return _instance; +// } +// } +//} + diff --git a/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs.meta b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs.meta new file mode 100644 index 0000000..1c1cc43 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/InGameTelemetry/TelemetryPopupEditorSettings.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: baf430e88a8f39549983d5def5bc8f37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping.meta b/Assets/Photon/Fusion/Scripts/Prototyping.meta new file mode 100644 index 0000000..604a9be --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f5da973e1aa898448b30db5210d1eb7f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest.meta b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest.meta new file mode 100644 index 0000000..e65858a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73422e46a443eef4994b2db19560b369 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs new file mode 100644 index 0000000..14bc109 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs @@ -0,0 +1,45 @@ + +using Fusion; +using UnityEngine; + +/// +/// Prototyping component for Fusion. Updates the Player's AOI every tick to be a radius around this object. +/// +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class PlayerAOIPrototype : NetworkBehaviour { + + protected enum AuthorityType { InputAuthority = 1, StateAuthority = 2 } + + [SerializeField] + protected AuthorityType TargetPlayerBy = AuthorityType.InputAuthority; + + [SerializeField] + protected bool DrawAreaOfInterestRadius; + + /// + /// Radius around this GameObject that defines the Area Of Interest for the InputAuthority of the object. + /// The InputAuthority player of this , + /// will receive updates for any other within this radius. + /// + public float Radius = 32f; + + public override void FixedUpdateNetwork() { + var target = TargetPlayerBy == AuthorityType.InputAuthority ? Object.InputAuthority : Object.StateAuthority; + + if (target) { + Runner.AddPlayerAreaOfInterest(target, position: transform.position, radius: Radius); + } + } + + private void OnDrawGizmos() { + if (DrawAreaOfInterestRadius) { + var baseColor = Gizmos.color; + + Gizmos.color = Color.white; + + Gizmos.DrawWireSphere(transform.position, Radius); + + Gizmos.color = baseColor; + } + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs.meta new file mode 100644 index 0000000..aaee047 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/AreaOfInterest/PlayerAOIPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 72872188c080b844a9c9d1015cf8e60a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Assistants.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants.meta new file mode 100644 index 0000000..336d2ce --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9ca2a4436a887014bb8bc59d99b2275f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs new file mode 100644 index 0000000..0f7e136 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs @@ -0,0 +1,198 @@ +#if UNITY_EDITOR + +using UnityEngine; +using System; +using UnityEditor; + +namespace Fusion.Assistants { + public static class FusionAssistants { + public const int PRIORITY = 0; + public const int PRIORITY_LOW = 1000; + + //// Default Material + //private const string _prototypeMaterialGUID = "38fbbd3db9e06c54e8a11016e2381469"; + //private static Material _prototypeMaterial; + //public static Material PrototypeMaterial { + // get { + // if (_prototypeMaterial != null) + // return _prototypeMaterial; + + // var path = AssetDatabase.GUIDToAssetPath(_prototypeMaterialGUID); + // if (path != null && path != "") + // _prototypeMaterial = AssetDatabase.LoadAssetAtPath(path); + // return _prototypeMaterial; + // } + //} + + //// Black Material + //private const string _prototypeMaterialBlackGUID = "5d1b896bc311a1d438c929c45b0c5fbc"; + //private static Material _prototypeMaterialBlack; + //public static Material PrototypeMaterialBlack { + // get { + // if (_prototypeMaterialBlack != null) + // return _prototypeMaterialBlack; + + // var path = AssetDatabase.GUIDToAssetPath(_prototypeMaterialBlackGUID); + // if (path != null && path != "") + // _prototypeMaterialBlack = AssetDatabase.LoadAssetAtPath(path); + // return _prototypeMaterialBlack; + // } + //} + + + //// Floor Material + //private const string _prototypeMaterialFloorGUID = "002db72054162e04b917e86a395e0a0f"; + //private static Material _prototypeMaterialFloor; + //public static Material PrototypeMaterialFloor { + // get { + // if (_prototypeMaterialFloor != null) + // return _prototypeMaterialFloor; + + // var path = AssetDatabase.GUIDToAssetPath(_prototypeMaterialFloorGUID); + // if (path != null && path != "") + // _prototypeMaterialFloor = AssetDatabase.LoadAssetAtPath(path); + // return _prototypeMaterialFloor; + // } + //} + + //// Box Material + //private const string _prototypeMaterialBoxGUID = "2544ff8e0cb0b4649ad11a93d3259ffa"; + //private static Material _prototypeMaterialBox; + //public static Material PrototypeMaterialBox { + // get { + // if (_prototypeMaterialBox != null) + // return _prototypeMaterialBox; + + // var path = AssetDatabase.GUIDToAssetPath(_prototypeMaterialBoxGUID); + // if (path != null && path != "") + // _prototypeMaterialBox = AssetDatabase.LoadAssetAtPath(path); + // return _prototypeMaterialBox; + // } + //} + + + /// + /// Ensure GameObject has component T. Will create as needed and return the found/created component. + /// + public static T EnsureComponentExists(this GameObject go) where T : Component { + if (go.TryGetComponent(out var t)) + return t; + + else + return go.AddComponent(); + } + + public static GameObject EnsureComponentsExistInScene(string preferredGameObjectName, params Type[] components) { + + GameObject go = null; + + foreach(var c in components) { + var found = UnityEngine.Object.FindObjectOfType(c); + if (found) + continue; + + if (go == null) + go = new GameObject(preferredGameObjectName); + + go.AddComponent(c); + } + + return go; + } + + public static T EnsureExistsInScene(string preferredGameObjectName = null, GameObject onThisObject = null, params Type[] otherRequiredComponents) where T : Component { + + if (preferredGameObjectName == null) + preferredGameObjectName = typeof(T).Name; + + T comp; + comp = UnityEngine.Object.FindObjectOfType(); + if (comp == null) { + // T was not found in scene, create a new gameobject and add T, as well as other required components + if (onThisObject == null) + onThisObject = new GameObject(preferredGameObjectName); + comp = onThisObject.AddComponent(); + foreach (var add in otherRequiredComponents) { + onThisObject.AddComponent(add); + } + } else { + // Make sure existing found T has the indicated extra components as well. + foreach (var add in otherRequiredComponents) { + if (comp.GetComponent(add) == false) + comp.gameObject.AddComponent(add); + } + } + return comp; + } + + /// + /// Create a scene object with all of the supplied arguments and parameters applied. + /// + public static GameObject CreatePrimitive( + PrimitiveType? primitive, + string name, + Vector3? position, + Quaternion? rotation, + Vector3? scale, + Transform parent, + Material material, + params Type[] addComponents) { + + GameObject go; + if (primitive.HasValue) { + go = GameObject.CreatePrimitive(primitive.Value); + + go.name = name; + + if (material != null) + go.GetComponent().material = material; + + foreach (var type in addComponents) { + go.AddComponent(type); + } + + } else { + go = new GameObject(name, addComponents); + } + + if (position.HasValue) + go.transform.position = position.Value; + + if (rotation.HasValue) + go.transform.rotation = rotation.Value; + + if (scale.HasValue) + go.transform.localScale = scale.Value; + + if (parent) + go.transform.parent = parent; + + return go; + } + + internal static RunnerVisibilityNodes EnsureComponentHasVisibilityNode(this Component component) { + var allExistingNodes = component.GetComponents(); + foreach (var existingNodes in allExistingNodes) { + foreach (var comp in existingNodes.Components) { + if (comp == component) { + return existingNodes; + } + } + } + + // Component is not represented yet. If there is a VisNodes already, use it. Otherwise make one. + RunnerVisibilityNodes targetNodes = component.GetComponent(); + if (targetNodes == null) { + targetNodes = component.gameObject.AddComponent(); + } + + // Add this component to the collection. + int newArrayPos = targetNodes.Components.Length; + Array.Resize(ref targetNodes.Components, newArrayPos + 1); + targetNodes.Components[newArrayPos] = component; + return targetNodes; + } + } +} + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs.meta new file mode 100644 index 0000000..37701c6 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionAssistants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d33d69ef2b49d0a439e55fd32391b5f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs new file mode 100644 index 0000000..d77bc73 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs @@ -0,0 +1,180 @@ +#if UNITY_EDITOR +using UnityEditor; + +using UnityEngine; +using UnityEngine.SceneManagement; +using Fusion.Editor; + +namespace Fusion.Assistants { + + public static class FusionSceneSetupAssistants { + + + [MenuItem("Fusion/GameObject/Setup Basic Fusion Scene", false, FusionAssistants.PRIORITY_LOW)] + [MenuItem("GameObject/Fusion/Setup Basic Fusion Scene", false, FusionAssistants.PRIORITY)] + public static void SetupBasicFusionScene() { + + // Create floor and Spawn Points + var floor = FusionAssistants.CreatePrimitive(PrimitiveType.Cube, "Prototype Floor", new Vector3(0, -1, 0), null, new Vector3(20, 2, 20), null, FusionPrototypeMaterials.Floor).transform; + + // Delete any existing spawn points + var found = UnityEngine.Object.FindObjectsOfType(); + foreach (var spawn in found) { + GameObject.DestroyImmediate(spawn.gameObject); + } + + // Add 5 spawn points + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 0", new Vector3(-2, 2, 4), Quaternion.Euler(0, 160, 0), null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 1", new Vector3(2, 2, 4), Quaternion.Euler(0, 200, 0), null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 2", new Vector3(4, 2, 0), Quaternion.Euler(0, -90, 0), null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 3", new Vector3(0, 2, -4), Quaternion.Euler(0, 0, 0), null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 4", new Vector3(-4, 2, 0), Quaternion.Euler(0, 90, 0), null, floor, null, typeof(PlayerSpawnPointPrototype)); + + // Add NetworkDebugRunner if missing + var n = AddNetworkStartup(); + + var nds = n.Item1; + var nr = n.Item2; + + nr.gameObject.EnsureComponentExists(); + AddPlayerSpawner(nr.gameObject); + nr.gameObject.EnsureComponentExists(); + + // Set our physics to 2D + NetworkProjectConfig.Global.PhysicsEngine = NetworkProjectConfig.PhysicsEngines.Physics3D; + NetworkProjectConfigUtilities.SaveGlobalConfig(); + + // Get scene and mark scene as dirty. + DirtyAndSaveScene(nds.gameObject.scene); + } + + [MenuItem("Fusion/GameObject/Setup Basic Fusion Scene 2D", false, FusionAssistants.PRIORITY_LOW)] + [MenuItem("GameObject/Fusion/Setup Basic Fusion Scene 2D", false, FusionAssistants.PRIORITY)] + public static void SetupBasic2DFusionScene() { + + // Create floor and Spawn Points + var floor = GameObject.Instantiate(FusionPrototypingPrefabs.Ground2D, null).transform; + + // Delete any existing spawn points + var found = UnityEngine.Object.FindObjectsOfType(); + foreach(var spawn in found) { + GameObject.DestroyImmediate(spawn.gameObject); + } + + // Add 5 spawn points + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 0", new Vector3(-4, 2, 0), default, null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 1", new Vector3(-2, 2, 0), default, null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 2", new Vector3(-0, 2, 0), default, null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 3", new Vector3(2, 2, 0), default, null, floor, null, typeof(PlayerSpawnPointPrototype)); + FusionAssistants.CreatePrimitive(null, "Player SpawnPoint 4", new Vector3(4, 2, 0), default, null, floor, null, typeof(PlayerSpawnPointPrototype)); + + // Add NetworkDebugRunner if missing + var n = AddNetworkStartup(); + + var nds = n.Item1; + var nr = n.Item2; + + nr.gameObject.EnsureComponentExists(); + var spawner = AddPlayerSpawner(nr.gameObject); + spawner.PlayerPrefab = FusionPrototypingPrefabs.BasicPlayerRB2D.GetComponent(); + nr.gameObject.EnsureComponentExists(); + + // Set our physics to 2D + NetworkProjectConfig.Global.PhysicsEngine = NetworkProjectConfig.PhysicsEngines.Physics2D; + NetworkProjectConfigUtilities.SaveGlobalConfig(); + + // Get scene and mark scene as dirty. + DirtyAndSaveScene(nds.gameObject.scene); + } + + [MenuItem("Fusion/GameObject/Setup/Add Networking To Scene", false, FusionAssistants.PRIORITY_LOW)] + [MenuItem("GameObject/Fusion/Setup/Add Networking To Scene", false, FusionAssistants.PRIORITY)] + public static void AddNetworkingToScene() { + (NetworkDebugStart nds, NetworkRunner nr) n = AddNetworkStartup(); + n.nr.gameObject.EnsureComponentExists(); + + // Get scene and mark scene as dirty. + DirtyAndSaveScene(n.nds.gameObject.scene); + } + + public static (NetworkDebugStart, NetworkRunner) AddNetworkStartup() { + // Add Visibility node to AudioListeners to disallow multiple active in shared instance mode (preventing log spam) + HandleAudioListeners(); + + // Add NetworkDebugRunner if missing + var nds = FusionAssistants.EnsureExistsInScene("Prototype Network Start"); + + NetworkRunner nr = nds.RunnerPrefab == null ? null : nds.RunnerPrefab.TryGetComponent(out var found) ? found : null; + // Add NetworkRunner to scene if the DebugStart doesn't have one as a prefab set already. + if (nr == null) { + + // Add NetworkRunner to scene if NetworkDebugStart doesn't have one set as a prefab already. + nr = FusionAssistants.EnsureExistsInScene("Prototype Runner"); + + nds.RunnerPrefab = nr; + // The runner go is also our fallback spawn point... so raise it into the air a bit + nr.transform.position = new Vector3(0, 3, 0); + } + + return (nds, nr); + } + + //[MenuItem("GameObject/Fusion/Setup/Add Player Spawner", false, FusionAssistants.PRIORITY)] + public static void AddPlayerSpawner() { AddPlayerSpawner(null); } + public static PlayerSpawnerPrototype AddPlayerSpawner(GameObject addTo) { + if (addTo == null) { + if (Selection.activeGameObject != null && Selection.activeGameObject.scene == SceneManager.GetActiveScene()) { + addTo = Selection.activeGameObject; + } else { + addTo = new GameObject("Prototype Player Spawner"); + Selection.activeGameObject = addTo; + } + } + + var spawner = addTo.EnsureComponentExists(); + addTo.EnsureComponentExists(); + return spawner; + } + + //[MenuItem("GameObject/Fusion/Setup/Add Player Spawn Point", false, FusionAssistants.PRIORITY)] + public static void AddPlayerSpawnPoint() { + var parent = Selection.activeGameObject ? Selection.activeGameObject.transform : null; + var point = FusionAssistants.CreatePrimitive(null, "Player SpawnPoint", null, null, null, parent, null, typeof(PlayerSpawnPointPrototype)); + Selection.activeGameObject = point; + } + + + [MenuItem("Fusion/GameObject/Setup/Add Current Scene To Build Settings", false, FusionAssistants.PRIORITY_LOW)] + [MenuItem("GameObject/Fusion/Setup/Add Current Scene To Build Settings", false, FusionAssistants.PRIORITY)] + public static void AddCurrentSceneToSettings() { DirtyAndSaveScene(SceneManager.GetActiveScene()); } + public static void DirtyAndSaveScene(Scene scene) { + + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(scene); + var scenename = scene.path; + + // Give chance to save - required in order to build out. If users cancel will only be able to run in the editor. + if (scenename == "") { + UnityEditor.SceneManagement.EditorSceneManager.SaveModifiedScenesIfUserWantsTo(new Scene[] { scene }); + scenename = scene.path; + } + + // Add scene to Build and Fusion settings + if (scenename != "") + scene.AddSceneToBuildSettings(); + } + + [MenuItem("Fusion/GameObject/Setup/Add AudioListener Handling", false, FusionAssistants.PRIORITY_LOW)] + [MenuItem("GameObject/Fusion/Setup/Add AudioListener Handling", false, FusionAssistants.PRIORITY)] + public static void HandleAudioListeners() { + int count = 0; + foreach (var listener in Object.FindObjectsOfType()) { + count++; + listener.EnsureComponentHasVisibilityNode(); + } + Debug.Log($"{count} {nameof(AudioListener)}(s) found and given a {nameof(RunnerVisibilityNode)} component."); + } + + } +} + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs.meta new file mode 100644 index 0000000..adb7c9a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Assistants/FusionSceneSetupAssistants.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 71790442d7134184fb5bacd79fbc9d19 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs b/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs new file mode 100644 index 0000000..be03f93 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs @@ -0,0 +1,163 @@ +// Removed May 21 2021 (obsoleted months prior) + +//using System.Collections.Generic; +//using UnityEngine; +//using Fusion; + +///// +///// Flags a GameObject that should not be active in all runners while running in Shared Instance mode. +///// Use to disable redundant non-simulation items such as lights or UI while testing. This will SetActive(false) all but one copy of this object. +///// +//[DisallowMultipleComponent] +//[AddComponentMenu("Fusion/Prototyping/Common Scene Object")] +//[System.Obsolete("Use " + nameof(RunnerVisibilityNode))] +//public class CommonSceneObject : Fusion.Behaviour { + +// public const string FUSION_HELP_TEXT = "Flags this GameObject as object that should only be enabled in one runner in InstanceModes.SharedInstance. " + +// "While running in Shared Instance mode this will SetActive(false) all but one networked instance. Use to disable objects like lights or UI elements."; + +// public enum PreferredRunner { Server, NotServer, Client } + +// private static Dictionary _actives = new Dictionary(); +// private static Dictionary> _inactives = new Dictionary>(); + +// [SerializeField] +// [Tooltip("Will SetActive(false) all copies of this networked GameObject, except one with a " + nameof(NetworkRunner) + " matching this setting.")] +// private PreferredRunner _preferredRunner = PreferredRunner.Server; + +// [SerializeField] +// [EditorDisabled] +// private int _uid; + +// private NetworkRunner _cachedrunner; +// private NetworkRunner Runner { +// get { +// if (_cachedrunner == null) +// _cachedrunner = FindRunner(); +// return _cachedrunner; +// } +// } +// private bool _preferredRunnerFound; + +// private void Reset() { +// _uid = Random.Range(0, int.MaxValue); +// } + +// public void Awake() { + +// if (NetworkProjectConfigAsset.Instance.Config.PeerMode != NetworkProjectConfig.PeerModes.Multiple) { +// enabled = false; +// return; +// } + +// // if an instance of this uid is already active, inactive any others. +// if (_actives.ContainsKey(_uid)) { + +// if (!_inactives.ContainsKey(_uid)) +// _inactives.Add(_uid, new Queue()); + +// _inactives[_uid].Enqueue(this); + +// gameObject.SetActive(false); + +// } else { +// _actives.Add(_uid, this); +// } +// } + +// private void OnApplicationQuit() { +// _inactives.Clear(); +// } + +// private void OnDestroy() { +// _actives.Remove(_uid); +// TrySwitchActiveToNextInstance(); +// } + +// public void Update() { +// if (_preferredRunnerFound) +// return; + +// SearchForPreferredRunner(); +// } + +// private void TrySwitchActiveToNextInstance() { +// if (_inactives.TryGetValue(_uid, out var commons)) { + +// if (commons.Count > 0) { +// var newactive = commons.Dequeue(); +// _actives.Add(_uid, newactive); +// if (newactive != null) +// newactive.gameObject.SetActive(true); +// } +// } +// } + +// private void SearchForPreferredRunner() { +// var runner = Runner; +// var pref = _preferredRunner; + +// // Test if current active object already is the preferred runner. +// if (runner != null) { +// if ( +// (!runner.IsServer && pref == PreferredRunner.NotServer) || +// (runner.IsServer && pref == PreferredRunner.Server) || +// (runner.IsClient && pref == PreferredRunner.Client)) { +// _preferredRunnerFound = true; +// return; +// } +// } + +// if (_inactives.TryGetValue(_uid, out var inactives)) { + +// // cycle through all inactive until we find a server runner +// for (int i = 0, cnt = inactives.Count; i < cnt; ++i) { +// var other = inactives.Dequeue(); +// var otherRunner = other.Runner; +// if (otherRunner == null) { +// inactives.Enqueue(other); +// continue; +// } + +// var isServer = otherRunner.IsServer; +// var isClient = otherRunner.IsClient; +// // if we found an alternate instance that is our desired runner type, switch that to active +// if ( +// (!isServer && pref == PreferredRunner.NotServer) || +// (isServer && pref == PreferredRunner.Server) || +// (isClient && pref == PreferredRunner.Client) ) { + +// this.gameObject.SetActive(false); +// other.gameObject.SetActive(true); +// _actives[_uid] = other; +// other._preferredRunnerFound = true; +// inactives.Enqueue(this); +// return; +// } + +// // Return to the front of the queue if it didn't meet our requirements. +// inactives.Enqueue(other); +// } +// } +// } + +// private NetworkRunner FindRunner() { +// var runners = NetworkRunner.GetInstancesEnumerator(); + +// while (runners.MoveNext()) { +// var runner = runners.Current; +// // Ignore inactive runners - might just be unused scene objects or other orphans. +// if (!runner.IsRunning) +// continue; + +// if (runner.SharedInstanceUnitySceneRoot) { +// var scene = runner.SharedInstanceUnitySceneRoot.scene; +// if (scene == gameObject.scene) { +// return runner; +// } +// } +// } +// return null; +// } +//} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs.meta new file mode 100644 index 0000000..4f7da27 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/CommonSceneObject.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a968f611dc8490e4fa9265214189ea8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs new file mode 100644 index 0000000..c2a42cc --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs @@ -0,0 +1,90 @@ + +using UnityEngine; +using Fusion; + +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class ControllerPrototype : Fusion.NetworkBehaviour { + protected NetworkCharacterControllerPrototype _ncc; + protected NetworkRigidbody _nrb; + protected NetworkRigidbody2D _nrb2d; + protected NetworkTransform _nt; + + [Networked] + public Vector3 MovementDirection { get; set; } + + public bool TransformLocal = false; + + [DrawIf(nameof(ShowSpeed), DrawIfHideType.Hide, DoIfCompareOperator.NotEqual)] + public float Speed = 6f; + + bool HasNCC => GetComponent(); + + bool ShowSpeed => this && !TryGetComponent(out _); + + public void Awake() { + CacheComponents(); + } + + public override void Spawned() { + CacheComponents(); + } + + private void CacheComponents() { + if (!_ncc) _ncc = GetComponent(); + if (!_nrb) _nrb = GetComponent(); + if (!_nrb2d) _nrb2d = GetComponent(); + if (!_nt) _nt = GetComponent(); + } + + public override void FixedUpdateNetwork() { + if (Runner.Config.PhysicsEngine == NetworkProjectConfig.PhysicsEngines.None) { + return; + } + + Vector3 direction; + if (GetInput(out NetworkInputPrototype input)) { + direction = default; + + if (input.IsDown(NetworkInputPrototype.BUTTON_FORWARD)) { + direction += TransformLocal ? transform.forward : Vector3.forward; + } + + if (input.IsDown(NetworkInputPrototype.BUTTON_BACKWARD)) { + direction -= TransformLocal ? transform.forward : Vector3.forward; + } + + if (input.IsDown(NetworkInputPrototype.BUTTON_LEFT)) { + direction -= TransformLocal ? transform.right : Vector3.right; + } + + if (input.IsDown(NetworkInputPrototype.BUTTON_RIGHT)) { + direction += TransformLocal ? transform.right : Vector3.right; + } + + direction = direction.normalized; + + MovementDirection = direction; + + if (input.IsDown(NetworkInputPrototype.BUTTON_JUMP)) { + if (_ncc) { + _ncc.Jump(); + } else { + direction += (TransformLocal ? transform.up : Vector3.up); + } + } + } else { + direction = MovementDirection; + } + + if (_ncc) { + _ncc.Move(direction); + } else if (_nrb && !_nrb.Rigidbody.isKinematic) { + _nrb.Rigidbody.AddForce(direction * Speed); + } else if (_nrb2d && !_nrb2d.Rigidbody.isKinematic) { + Vector2 direction2d = new Vector2(direction.x, direction.y + direction.z); + _nrb2d.Rigidbody.AddForce(direction2d * Speed); + } else { + transform.position += (direction * Speed * Runner.DeltaTime); + } + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs.meta new file mode 100644 index 0000000..1f393cf --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ControllerPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00e2a674f00349f4ca3accde09d57809 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Editor.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Editor.meta new file mode 100644 index 0000000..12b4a02 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 416fb9480bd3cb940aaa676a1fac253c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs new file mode 100644 index 0000000..492f302 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs @@ -0,0 +1 @@ +// Removed April 15 2021 \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs.meta new file mode 100644 index 0000000..f6e7f76 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Editor/NetworkDebugStartEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 794fdf8bf58390d4b8c1f02503368c95 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs new file mode 100644 index 0000000..c1266a6 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using Fusion; +using Fusion.Sockets; +using UnityEngine; + +/// +/// A simple example of Fusion input collection. This component should be on the same GameObject as the . +/// +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class InputBehaviourPrototype : Fusion.Behaviour, INetworkRunnerCallbacks { + + public void OnInput(NetworkRunner runner, NetworkInput input) { + var frameworkInput = new NetworkInputPrototype(); + + if (Input.GetKey(KeyCode.W)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_FORWARD; + } + + if (Input.GetKey(KeyCode.S)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_BACKWARD; + } + + if (Input.GetKey(KeyCode.A)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_LEFT; + } + + if (Input.GetKey(KeyCode.D)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_RIGHT; + } + + if (Input.GetKey(KeyCode.Space)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_JUMP; + } + + if (Input.GetKey(KeyCode.C)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_CROUCH; + } + + if (Input.GetKey(KeyCode.E)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_ACTION1; + } + + if (Input.GetKey(KeyCode.Q)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_ACTION2; + } + + if (Input.GetKey(KeyCode.F)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_ACTION3; + } + + if (Input.GetKey(KeyCode.G)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_ACTION4; + } + + if (Input.GetKey(KeyCode.R)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_RELOAD; + } + + if (Input.GetMouseButton(0)) { + frameworkInput.Buttons |= NetworkInputPrototype.BUTTON_FIRE; + } + + input.Set(frameworkInput); + } + + public void OnInputMissing(NetworkRunner runner, PlayerRef player, NetworkInput input) { + } + + public void OnConnectedToServer(NetworkRunner runner) { } + + public void OnConnectFailed(NetworkRunner runner, NetAddress remoteAddress, NetConnectFailedReason reason) { + // shutdown any client that has failed to connect + //runner.Shutdown(); + } + public void OnConnectRequest(NetworkRunner runner, NetworkRunnerCallbackArgs.ConnectRequest request, byte[] token) { } + + public void OnDisconnectedFromServer(NetworkRunner runner) { + // shutdown any client that has disconnected from server + //runner.Shutdown(); + } + public void OnPlayerJoined(NetworkRunner runner, PlayerRef player) { } + public void OnPlayerLeft(NetworkRunner runner, PlayerRef player) { } + public void OnUserSimulationMessage(NetworkRunner runner, SimulationMessagePtr message) { } + public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) { } + public void OnSessionListUpdated(NetworkRunner runner, List sessionList) { } + public void OnReliableDataReceived(NetworkRunner runner, PlayerRef player, ArraySegment data) { + } + + public void OnSceneLoadDone(NetworkRunner runner) { + + } + + public void OnSceneLoadStart(NetworkRunner runner) { + } + + public void OnCustomAuthenticationResponse(NetworkRunner runner, Dictionary data) { + } + + public void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) { + + } +} + +/// +/// Example definition of an INetworkStruct. +/// +public struct NetworkInputPrototype : INetworkInput { + public const uint BUTTON_USE = 1 << 0; + public const uint BUTTON_FIRE = 1 << 1; + public const uint BUTTON_FIRE_ALT = 1 << 2; + + public const uint BUTTON_FORWARD = 1 << 3; + public const uint BUTTON_BACKWARD = 1 << 4; + public const uint BUTTON_LEFT = 1 << 5; + public const uint BUTTON_RIGHT = 1 << 6; + + public const uint BUTTON_JUMP = 1 << 7; + public const uint BUTTON_CROUCH = 1 << 8; + public const uint BUTTON_WALK = 1 << 9; + + public const uint BUTTON_ACTION1 = 1 << 10; + public const uint BUTTON_ACTION2 = 1 << 11; + public const uint BUTTON_ACTION3 = 1 << 12; + public const uint BUTTON_ACTION4 = 1 << 14; + + public const uint BUTTON_RELOAD = 1 << 15; + + public uint Buttons; + public byte Weapon; + public Angle Yaw; + public Angle Pitch; + + public bool IsUp(uint button) { + return IsDown(button) == false; + } + + public bool IsDown(uint button) { + return (Buttons & button) == button; + } +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs.meta new file mode 100644 index 0000000..350e5fa --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/InputBehaviourPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e3bfdb733d449574a8f4796094d35a63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials.meta new file mode 100644 index 0000000..1c4afb8 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5052b1d48c3804842bb9ac99a24a594c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png new file mode 100644 index 0000000..5dd14af Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png differ diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png.meta new file mode 100644 index 0000000..d2c8791 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridBox.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 933188806222c8f47969cdea92996cc9 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 0 + aniso: -1 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 256 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png new file mode 100644 index 0000000..551c7e7 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png differ diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png.meta new file mode 100644 index 0000000..f37b23d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridSphere.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: 2422cfae11c31e848bc464c60f8f7af7 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 0 + aniso: 0 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 256 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 256 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 256 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png new file mode 100644 index 0000000..0f73b48 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png differ diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png.meta new file mode 100644 index 0000000..0bba139 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeGridTexture.png.meta @@ -0,0 +1,120 @@ +fileFormatVersion: 2 +guid: bcf7904fc6371a5409e0fbc3232dfc98 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: -1 + mipBias: -100 + wrapU: 2 + wrapV: 2 + wrapW: 2 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 1024 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 1024 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 1024 + resizeAlgorithm: 1 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat new file mode 100644 index 0000000..eab4288 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterial + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: bcf7904fc6371a5409e0fbc3232dfc98, type: 3} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat.meta new file mode 100644 index 0000000..97513e8 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterial.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 38fbbd3db9e06c54e8a11016e2381469 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat new file mode 100644 index 0000000..19ce960 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialBall + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 933188806222c8f47969cdea92996cc9, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat.meta new file mode 100644 index 0000000..6cd6c22 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBall.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3dcabcbc2cd009d4bac7889f284c6345 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat new file mode 100644 index 0000000..c0b8c67 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialBlack + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat.meta new file mode 100644 index 0000000..313a3ff --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlack.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d1b896bc311a1d438c929c45b0c5fbc +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat new file mode 100644 index 0000000..cd11826 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialBlue + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.149, g: 0.284, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat.meta new file mode 100644 index 0000000..22c4322 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBlue.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e79a222d16731a4e96e7360bf14bdfd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat new file mode 100644 index 0000000..339e946 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialBox + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 933188806222c8f47969cdea92996cc9, type: 3} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat.meta new file mode 100644 index 0000000..12ac200 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialBox.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2544ff8e0cb0b4649ad11a93d3259ffa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat new file mode 100644 index 0000000..0a8a4ef --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialCyan + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat.meta new file mode 100644 index 0000000..7653a93 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialCyan.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1ed66b76f883f11419ff9b35e5f31616 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat new file mode 100644 index 0000000..13bb471 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialFloor + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 20, y: 20} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: bcf7904fc6371a5409e0fbc3232dfc98, type: 3} + m_Scale: {x: 20, y: 20} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.6156863, g: 0.69411767, b: 0.8862745, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat.meta new file mode 100644 index 0000000..918127c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialFloor.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 002db72054162e04b917e86a395e0a0f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat new file mode 100644 index 0000000..584a38c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialGreen + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.319, g: 1, b: 0.305, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat.meta new file mode 100644 index 0000000..17e0214 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialGreen.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f9372f58500a3f46ba541ea24e6d105 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat new file mode 100644 index 0000000..328fe11 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialHex + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 2800000, guid: 76fb993bd58deff4986de87bf9c8b953, type: 3} + m_Scale: {x: 2, y: 2} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat.meta new file mode 100644 index 0000000..8ad1a6f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialHex.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2baf260ec284cd248b4192b400c291ce +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat new file mode 100644 index 0000000..6a3e181 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialRed + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 0.17, b: 0.184, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat.meta new file mode 100644 index 0000000..bf05901 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialRed.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 42b24d338df372049a18bd257e6bc550 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat new file mode 100644 index 0000000..351c2a5 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat @@ -0,0 +1,78 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypeMaterialYellow + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat.meta new file mode 100644 index 0000000..86a02a7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterialYellow.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 37cb86b5e2e83fb47a760ff23a51aff6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs new file mode 100644 index 0000000..7e5c92c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs @@ -0,0 +1,67 @@ +#if UNITY_EDITOR + +using UnityEngine; +using FusionPrototypingInternal; + +public static class FusionPrototypeMaterials +{ + + // Default Material + private const string _defaultGuid = "38fbbd3db9e06c54e8a11016e2381469"; + private static Material _default; + public static Material Default { get { return _defaultGuid.GetAsset(ref _default); } } + + // Black Material + private const string _blackGuid = "5d1b896bc311a1d438c929c45b0c5fbc"; + private static Material _black; + public static Material Black { get { return _blackGuid.GetAsset(ref _black); } } + + // red Material + private const string _redGuid = "42b24d338df372049a18bd257e6bc550"; + private static Material _red; + public static Material Red { get { return _redGuid.GetAsset(ref _red); } } + + // green Material + private const string _greenGuid = "8f9372f58500a3f46ba541ea24e6d105"; + private static Material _green; + public static Material Green { get { return _greenGuid.GetAsset(ref _green); } } + + // Blue Material + private const string _blueGuid = "1e79a222d16731a4e96e7360bf14bdfd"; + private static Material _blue; + public static Material Blue { get { return _blueGuid.GetAsset(ref _blue); } } + + // Yellow Material + private const string _yellowGuid = "37cb86b5e2e83fb47a760ff23a51aff6"; + private static Material _yellow; + public static Material Yellow { get { return _yellowGuid.GetAsset(ref _yellow); } } + + // Yellow Material + private const string _cyanGuid = "1ed66b76f883f11419ff9b35e5f31616"; + private static Material _cyan; + public static Material Cyan { get { return _cyanGuid.GetAsset(ref _cyan); } } + + // Floor Material + private const string _floorGuid = "002db72054162e04b917e86a395e0a0f"; + private static Material _floor; + public static Material Floor { get { return _floorGuid.GetAsset(ref _floor); } } + + // Box Material + private const string _boxGuid = "2544ff8e0cb0b4649ad11a93d3259ffa"; + private static Material _box; + public static Material Box { get { return _boxGuid.GetAsset(ref _box); } } + + + //private static Material GetMaterial(string Guid, ref Material backing) { + // if (backing != null) + // return backing; + + // var path = AssetDatabase.GUIDToAssetPath(Guid); + // if (path != null && path != "") + // backing = AssetDatabase.LoadAssetAtPath(path); + // return backing; + //} + +} + +#endif \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs.meta new file mode 100644 index 0000000..93d9eb2 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeMaterials.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d4adeee9185ffa14ba7dd5d58cec4e3d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D new file mode 100644 index 0000000..0a52fc6 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!62 &6200000 +PhysicsMaterial2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FusionPrototypePhysicsMaterial2D + friction: 0.4 + bounciness: 0.3 diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D.meta new file mode 100644 index 0000000..90a8da0 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypePhysicsMaterial2D.physicsMaterial2D.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46bf9225396fb7546bf2ff74e5a4cf9d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 6200000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png new file mode 100644 index 0000000..0b300a2 Binary files /dev/null and b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png differ diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png.meta new file mode 100644 index 0000000..1831f48 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypeWhitePixel.png.meta @@ -0,0 +1,108 @@ +fileFormatVersion: 2 +guid: 5422498f780e1364b9989ffa3292a010 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs new file mode 100644 index 0000000..2a8d9c9 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs @@ -0,0 +1,15 @@ +#if UNITY_EDITOR + +using UnityEngine; +using FusionPrototypingInternal; + +public static class FusionPrototypingTextures +{ + // Box Grid Texture + private const string _boxGridGuid = "933188806222c8f47969cdea92996cc9"; + private static Texture2D _boxGrid; + public static Texture2D BoxGrid { get { return _boxGridGuid.GetAsset(ref _boxGrid); } } + +} + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs.meta new file mode 100644 index 0000000..b6dc004 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Materials/FusionPrototypingTextures.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e23f786f5897144f8da79ee9778b3bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs new file mode 100644 index 0000000..2b1ad64 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs @@ -0,0 +1,121 @@ +using System; +using Fusion; +using UnityEngine; + +[RequireComponent(typeof(CharacterController))] +[OrderBefore(typeof(NetworkTransform))] +[DisallowMultipleComponent] +// ReSharper disable once CheckNamespace +public class NetworkCharacterControllerPrototype : NetworkTransform { + [Header("Character Controller Settings")] + public float gravity = -20.0f; + public float jumpImpulse = 8.0f; + public float acceleration = 10.0f; + public float braking = 10.0f; + public float maxSpeed = 2.0f; + public float rotationSpeed = 15.0f; + + [Networked] + [HideInInspector] + public bool IsGrounded { get; set; } + + [Networked] + [HideInInspector] + public Vector3 Velocity { get; set; } + + /// + /// Sets the default teleport interpolation velocity to be the CC's current velocity. + /// For more details on how this field is used, see . + /// + protected override Vector3 DefaultTeleportInterpolationVelocity => Velocity; + + /// + /// Sets the default teleport interpolation angular velocity to be the CC's rotation speed on the Z axis. + /// For more details on how this field is used, see . + /// + protected override Vector3 DefaultTeleportInterpolationAngularVelocity => new Vector3(0f, 0f, rotationSpeed); + + public CharacterController Controller { get; private set; } + + protected override void Awake() { + base.Awake(); + CacheController(); + } + + public override void Spawned() { + base.Spawned(); + CacheController(); + + // Caveat: this is needed to initialize the Controller's state and avoid unwanted spikes in its perceived velocity + Controller.Move(transform.position); + } + + private void CacheController() { + if (Controller == null) { + Controller = GetComponent(); + + Assert.Check(Controller != null, $"An object with {nameof(NetworkCharacterControllerPrototype)} must also have a {nameof(CharacterController)} component."); + } + } + + protected override void CopyFromBufferToEngine() { + // Trick: CC must be disabled before resetting the transform state + Controller.enabled = false; + + // Pull base (NetworkTransform) state from networked data buffer + base.CopyFromBufferToEngine(); + + // Re-enable CC + Controller.enabled = true; + } + + /// + /// Basic implementation of a jump impulse (immediately integrates a vertical component to Velocity). + /// Jump even if not in a grounded state. + /// Optional field to override the jump impulse. If null, is used. + /// + public virtual void Jump(bool ignoreGrounded = false, float? overrideImpulse = null) { + if (IsGrounded || ignoreGrounded) { + var newVel = Velocity; + newVel.y += overrideImpulse ?? jumpImpulse; + Velocity = newVel; + } + } + + /// + /// Basic implementation of a character controller's movement function based on an intended direction. + /// Intended movement direction, subject to movement query, acceleration and max speed values. + /// + public virtual void Move(Vector3 direction) { + var deltaTime = Runner.DeltaTime; + var previousPos = transform.position; + var moveVelocity = Velocity; + + direction = direction.normalized; + + if (IsGrounded && moveVelocity.y < 0) { + moveVelocity.y = 0f; + } + + moveVelocity.y += gravity * Runner.DeltaTime; + + var horizontalVel = default(Vector3); + horizontalVel.x = moveVelocity.x; + horizontalVel.z = moveVelocity.z; + + if (direction == default) { + horizontalVel = Vector3.Lerp(horizontalVel, default, braking * deltaTime); + } else { + horizontalVel = Vector3.ClampMagnitude(horizontalVel + direction * acceleration * deltaTime, maxSpeed); + transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Runner.DeltaTime); + } + + moveVelocity.x = horizontalVel.x; + moveVelocity.z = horizontalVel.z; + + Controller.Move(moveVelocity * deltaTime); + + Velocity = (transform.position - previousPos) * Runner.Simulation.Config.TickRate; + IsGrounded = Controller.isGrounded; + } +} \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs.meta new file mode 100644 index 0000000..3a882c7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkCharacterControllerPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 378411a89480da345945c2f888327a2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs new file mode 100644 index 0000000..51f6cde --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs @@ -0,0 +1,616 @@ +using System; +using Fusion; +using Fusion.Sockets; +using System.Collections; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.SceneManagement; +using System.Collections.Generic; +using System.Linq; + +#if UNITY_EDITOR +using UnityEditor; +using Fusion.Editor; +#endif + +/// +/// A Fusion prototyping class for starting up basic networking. Add this component to your startup scene, and supply a . +/// Can be set to automatically startup the network, display an in-game menu, or allow simplified start calls like . +/// +[DisallowMultipleComponent] +[AddComponentMenu("Fusion/Prototyping/Network Debug Start")] +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class NetworkDebugStart : Fusion.Behaviour { + + /// + /// Selection for how will behave at startup. + /// + public enum StartModes { + UserInterface, + Automatic, + Manual + } + + /// + /// The current stage of connection or shutdown. + /// + public enum Stage { + Disconnected, + StartingUp, + UnloadOriginalScene, + ConnectingServer, + ConnectingClients, + AllConnected, + } + + /// + /// Supply a Prefab or a scene object which has the component on it, + /// as well as any runner dependent components which implement , + /// such as or your own custom INetworkInput implementations. + /// + [WarnIf(nameof(RunnerPrefab), 0, "No " + nameof(RunnerPrefab) + " supplied. Will search for a " + nameof(NetworkRunner) + " in the scene at startup.")] + [InlineHelp] + public NetworkRunner RunnerPrefab; + + /// + /// Select how network startup will be triggered. Automatically, by in-game menu selection, or exclusively by script. + /// + [WarnIf(nameof(StartMode), (long)StartModes.Manual, "Start network by calling the methods " + + nameof(StartHost) + "(), " + + nameof(StartServer) + "(), " + + nameof(StartClient) + "(), " + + nameof(StartHostPlusClients) + "(), or " + + nameof(StartServerPlusClients) + "()" + )] + [InlineHelp] + public StartModes StartMode = StartModes.UserInterface; + + /// + /// When is set to , this option selects if the + /// will be started as a dedicated server, or as a host (which is a server with a local player). + /// + [UnityEngine.Serialization.FormerlySerializedAs("Server")] + [DrawIf(nameof(StartMode), (long)StartModes.Automatic, DrawIfHideType.Hide)] + [InlineHelp] + public GameMode AutoStartAs = GameMode.Shared; + + /// + /// will not render GUI elements while == . + /// + [DrawIf(nameof(StartMode), (long)StartModes.UserInterface, DrawIfHideType.Hide)] + [InlineHelp] + public bool AutoHideGUI = true; + + /// + /// The number of client instances that will be created if running in Mulit-Peer Mode. + /// When using the Select start mode, this number will be the default value for the additional clients option box. + /// + [DrawIf(nameof(ShowAutoClients), true, DrawIfHideType.Hide)] + [InlineHelp] + public int AutoClients = 1; + + + /// + /// The port that server/host will use. + /// + [InlineHelp] + public ushort ServerPort = 27015; + + /// + /// The default room name to use when connecting to photon cloud. + /// + [InlineHelp] + public string DefaultRoomName = ""; // empty/null means Random Room Name + + /// + /// Will automatically enable once peers have finished connecting. + /// + [InlineHelp] + public bool AlwaysShowStats = false; + + [NonSerialized] + NetworkRunner _server; + + /// + /// The Scene that will be loaded after network shutdown completes (all peers have disconnected). + /// If this field is null or invalid, will be set to the current scene when runs Awake(). + /// + [ScenePath] + [InlineHelp] + public string InitialScenePath; + static string _initialScenePath; + + [EditorDisabled] + protected Stage _currentStage; + /// + /// Indicates which step of the startup process is currently in. + /// + public Stage CurrentStage { + get => _currentStage; + internal set { + _currentStage = value; +#if UNITY_EDITOR + // Hack to force an inspector refresh when this value changes, as it affects which buttons are shown. + EditorUtility.SetDirty(this); +#endif + } + } + + /// + /// The index number used for the last created peer. + /// + public int LastCreatedClientIndex { get; internal set; } + + /// + /// The server mode that was used for initial startup. Used to inform UI which client modes should be available. + /// + public GameMode CurrentServerMode { get; internal set; } + + protected bool CanAddClients => CurrentStage == Stage.AllConnected && CurrentServerMode > 0 && CurrentServerMode != GameMode.Shared && CurrentServerMode != GameMode.Single; + protected bool CanAddSharedClients => CurrentStage == Stage.AllConnected && CurrentServerMode > 0 && CurrentServerMode == GameMode.Shared; + protected bool IsShutdown => CurrentStage == Stage.Disconnected; + protected bool IsShutdownAndMultiPeer => CurrentStage == Stage.Disconnected && UsingMultiPeerMode; + + protected bool UsingMultiPeerMode => NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple; + protected bool ShowAutoClients => StartMode != StartModes.Manual && UsingMultiPeerMode && AutoStartAs != GameMode.Single; + + +#if UNITY_EDITOR + protected virtual void Reset() { + if (TryGetComponent(out var ndsg) == false) { + ndsg = gameObject.AddComponent(); + } + } + +#endif + + + protected virtual void Start() { + + if (_initialScenePath == null) { + if (String.IsNullOrEmpty(InitialScenePath)) { + var currentScene = SceneManager.GetActiveScene(); + if (currentScene.IsValid()) { + _initialScenePath = currentScene.path; + } else { + // Last fallback is the first entry in the build settings + _initialScenePath = SceneManager.GetSceneByBuildIndex(0).path; + } + InitialScenePath = _initialScenePath; + } else { + _initialScenePath = InitialScenePath; + } + } + + var config = NetworkProjectConfig.Global; + var isMultiPeer = config.PeerMode == NetworkProjectConfig.PeerModes.Multiple; + + var existingrunner = FindObjectOfType(); + + if (existingrunner && existingrunner != RunnerPrefab) { + if (existingrunner.State != NetworkRunner.States.Shutdown) { + // disable + enabled = false; + + // destroy this and GUI (if exists), and return + var gui = GetComponent(); + if (gui) { + Destroy(gui); + } + + Destroy(this); + return; + } else { + // If no RunnerPrefab is supplied, use the scene runner. + if (RunnerPrefab == null) { + RunnerPrefab = existingrunner; + } + } + } + + if (StartMode == StartModes.Manual) + return; + + //// Force this to select if auto not allowed. + //if (StartMode == StartModes.Automatic && config.PeerMode != NetworkProjectConfig.PeerModes.Multiple && Server != ServerModes.Shared) { + // StartMode = StartModes.UserInterface; + //} + + if (StartMode == StartModes.Automatic) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(AutoStartAs, sceneRef, isMultiPeer ? AutoClients : (AutoStartAs == GameMode.Client ? 1 : 0))); + } + } else { + if (TryGetComponent(out var _) == false) { + gameObject.AddComponent(); + } + } + } + + protected bool TryGetSceneRef(out SceneRef sceneRef) { + var activeScene = SceneManager.GetActiveScene(); + if (activeScene.buildIndex < 0 || activeScene.buildIndex >= SceneManager.sceneCountInBuildSettings) { + sceneRef = default; + return false; + } else { + sceneRef = activeScene.buildIndex; + return true; + } + } + + /// + /// Start a single player instance. + /// + [BehaviourButtonAction(nameof(StartSinglePlayer), true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartSinglePlayer() { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Single, sceneRef, 0)); + } + } + + + /// + /// Start a server instance. + /// + [BehaviourButtonAction(nameof(StartServer), true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartServer() { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Server, sceneRef, 0)); + } + } + + /// + /// Start a host instance. This is a server instance, with a local player. + /// + [BehaviourButtonAction(nameof(StartHost), true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartHost() { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Host, sceneRef, 0)); + } + } + + /// + /// Start a client instance. + /// + [BehaviourButtonAction("Start Client", true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartClient() { + StartCoroutine(StartWithClients(GameMode.Client, default, 1)); + } + + [BehaviourButtonAction("Start Shared Client", true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartSharedClient() { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Shared, sceneRef, 1)); + } + } + + [BehaviourButtonAction("Start Auto Host Or Client", true, false, conditionMember: nameof(IsShutdown))] + public virtual void StartAutoClient() { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.AutoHostOrClient, sceneRef, 1)); + } + } + + /// + /// Start a Fusion server instance, and the number of client instances indicated by . + /// InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + /// + [BehaviourButtonAction("Start Server Plus Clients", true, false, nameof(IsShutdownAndMultiPeer))] + public virtual void StartServerPlusClients() { + StartServerPlusClients(AutoClients); + } + + /// + /// Start a Fusion host instance, and the number of client instances indicated by . + /// InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + /// + [BehaviourButtonAction("Start Host Plus Clients", true, false, nameof(IsShutdownAndMultiPeer))] + public void StartHostPlusClients() { + StartHostPlusClients(AutoClients); + } + + [BehaviourButtonAction("Shutdown", true, false, nameof(CurrentStage))] + public void Shutdown() { + ShutdownAll(); + } + + /// + /// Start a Fusion server instance, and the indicated number of client instances. + /// InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + /// + public virtual void StartServerPlusClients(int clientCount) { + if (NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Server, sceneRef, clientCount)); + } + } else { + Debug.LogWarning($"Unable to start multiple {nameof(NetworkRunner)}s in Unique Instance mode."); + } + } + + /// + /// Start a Fusion host instance (server with local player), and the indicated number of additional client instances. + /// InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + /// + public void StartHostPlusClients(int clientCount) { + if (NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Host, sceneRef, clientCount)); + } + } else { + Debug.LogWarning($"Unable to start multiple {nameof(NetworkRunner)}s in Unique Instance mode."); + } + } + + /// + /// Start a Fusion host instance (server with local player), and the indicated number of additional client instances. + /// InstanceMode must be set to Multi-Peer mode, as this requires multiple instances. + /// + public void StartMultipleClients(int clientCount) { + if (NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Client, sceneRef, clientCount)); + } + } else { + Debug.LogWarning($"Unable to start multiple {nameof(NetworkRunner)}s in Unique Instance mode."); + } + } + + /// + /// Start as Room on the Photon cloud, and connects as one or more clients. + /// + /// + public void StartMultipleSharedClients(int clientCount) { + if (NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.Shared, sceneRef, clientCount)); + } + } else { + Debug.LogWarning($"Unable to start multiple {nameof(NetworkRunner)}s in Unique Instance mode."); + } + } + + public void StartMultipleAutoClients(int clientCount) { + if (NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + if (TryGetSceneRef(out var sceneRef)) { + StartCoroutine(StartWithClients(GameMode.AutoHostOrClient, sceneRef, clientCount)); + } + } else { + Debug.LogWarning($"Unable to start multiple {nameof(NetworkRunner)}s in Unique Instance mode."); + } + } + + public void ShutdownAll() { + + var runners = NetworkRunner.GetInstancesEnumerator(); + while (runners.MoveNext()) { + var runner = runners.Current; + if (runner != null && runner.IsRunning) { + runner.Shutdown(); + } + } + + SceneManager.LoadSceneAsync(_initialScenePath); + // Destroy our DontDestroyOnLoad objects to finish the reset + Destroy(RunnerPrefab.gameObject); + Destroy(gameObject); + CurrentStage = Stage.Disconnected; + CurrentServerMode = 0; + } + + + protected IEnumerator StartWithClients(GameMode serverMode, SceneRef sceneRef, int clientCount) { + // Avoid double clicks or disallow multiple startup calls. + if (CurrentStage != Stage.Disconnected) { + yield break; + } + + bool includesServerStart = serverMode != GameMode.Shared && serverMode != GameMode.Client && serverMode != GameMode.AutoHostOrClient; + + if (!includesServerStart && clientCount == 0) { + Debug.LogError($"{nameof(GameMode)} is set to {serverMode}, and {nameof(clientCount)} is set to zero. Starting no network runners."); + yield break; + } + + CurrentStage = Stage.StartingUp; + + var currentScene = SceneManager.GetActiveScene(); + + // must have a runner + if (!RunnerPrefab) { + Debug.LogError($"{nameof(RunnerPrefab)} not set, can't perform debug start."); + yield break; + } + + // Clone the RunnerPrefab so we can safely delete the startup scene (the prefab might be part of it, rather than an asset). + RunnerPrefab = Instantiate(RunnerPrefab); + DontDestroyOnLoad(RunnerPrefab); + RunnerPrefab.name = "Temporary Runner Prefab"; + + // Single-peer can't start more than one peer. Validate clientCount to make sure it complies with current PeerMode. + var config = NetworkProjectConfig.Global; + if (config.PeerMode != NetworkProjectConfig.PeerModes.Multiple) { + int maxClientsAllowed = includesServerStart ? 0 : 1; + if (clientCount > maxClientsAllowed) { + Debug.LogWarning($"Instance mode must be set to {nameof(NetworkProjectConfig.PeerModes.Multiple)} to perform a debug start multiple peers. Restricting client count to {maxClientsAllowed}."); + clientCount = maxClientsAllowed; + } + } + + // If NDS is starting more than 1 shared or auto client, they need to use the same Session Name, otherwise, they will end up on different Rooms + // as Fusion creates a Random Session Name when no name is passed on the args + if ((serverMode == GameMode.Shared || serverMode == GameMode.AutoHostOrClient) && clientCount > 1 && config.PeerMode == NetworkProjectConfig.PeerModes.Multiple) { + DefaultRoomName = string.IsNullOrEmpty(DefaultRoomName) == false ? DefaultRoomName : Guid.NewGuid().ToString(); + } + + if (gameObject.transform.parent) { + Debug.LogWarning($"{nameof(NetworkDebugStart)} can't be a child game object, un-parenting."); + gameObject.transform.parent = null; + } + + DontDestroyOnLoad(gameObject); + CurrentServerMode = serverMode; + + // start server, just take address from it + if (includesServerStart) { + _server = Instantiate(RunnerPrefab); + _server.name = serverMode.ToString(); + + var serverTask = InitializeNetworkRunner(_server, serverMode, NetAddress.Any(ServerPort), sceneRef, (runner) => { +#if FUSION_DEV + var name = _server.name; // closures do not capture values, need a local var to save it + Debug.Log($"Server NetworkRunner '{name}' started."); +#endif + // this action is called after InitializeNetworkRunner for the server has completed startup + StartCoroutine(StartClients(clientCount, serverMode, sceneRef)); + }); + + while(serverTask.IsCompleted == false) { + yield return new WaitForEndOfFrame(); + } + + if (serverTask.IsFaulted) { + ShutdownAll(); + } + + } else { + StartCoroutine(StartClients(clientCount, serverMode, sceneRef)); + } + + // Add stats last, so any event systems that may be getting created are already in place. + if (includesServerStart && AlwaysShowStats && serverMode != GameMode.Shared) { + FusionStats.Create(runner: _server, screenLayout: FusionStats.DefaultLayouts.Left, objectLayout: FusionStats.DefaultLayouts.Left); + } + + } + + [BehaviourButtonAction("Add Additional Client", conditionMember: nameof(CanAddClients))] + public void AddClient() { + if (TryGetSceneRef(out var sceneRef)) { + AddClient(GameMode.Client, sceneRef); + } + } + + [BehaviourButtonAction("Add Additional Shared Client", conditionMember: nameof(CanAddSharedClients))] + public void AddSharedClient() { + if (TryGetSceneRef(out var sceneRef)) { + AddClient(GameMode.Shared, sceneRef); + } + } + + public Task AddClient(GameMode serverMode, SceneRef sceneRef) { + var client = Instantiate(RunnerPrefab); + DontDestroyOnLoad(client); + + client.name = $"Client {(Char)(65 + LastCreatedClientIndex++)}"; + + // if server mode is Shared or AutoHostOrClient, then game client mode is the same as the server, otherwise it is client + var mode = GameMode.Client; + switch (serverMode) { + case GameMode.Shared: + case GameMode.AutoHostOrClient: + mode = serverMode; + break; + } + +#if FUSION_DEV + var clientTask = InitializeNetworkRunner(client, mode, NetAddress.Any(), sceneRef, (runner) => { + var name = client.name; // closures do not capture values, need a local var to save it + Debug.Log($"Client NetworkRunner '{name}' started."); + }); +#else + var clientTask = InitializeNetworkRunner(client, mode, NetAddress.Any(), sceneRef, null); +#endif + + //clientTasks.Add(clientTask); + + // Add stats last, so that event systems that may be getting created are already in place. + if (AlwaysShowStats && LastCreatedClientIndex == 0) { + FusionStats.Create(runner: client, screenLayout: FusionStats.DefaultLayouts.Right, objectLayout: FusionStats.DefaultLayouts.Right); + } + + return clientTask; + } + + protected IEnumerator StartClients(int clientCount, GameMode serverMode, SceneRef sceneRef = default) { + + var clientTasks = new List(); + + CurrentStage = Stage.ConnectingClients; + + for (int i = 0; i < clientCount; ++i) { + var clientTask = AddClient(serverMode, sceneRef); + clientTasks.Add(clientTask); + } + + bool done; + do { + done = true; + + // yield until all tasks report as completed + foreach (var task in clientTasks) { + done &= task.IsCompleted; + + if (done == false) { + break; + } + + if (task.IsFaulted) { + Debug.LogWarning(task.Exception); + } + } + yield return new WaitForSeconds(0.5f); + + } while (done == false); + + CurrentStage = Stage.AllConnected; + //ForceGUIUpdate(); + } + + protected virtual Task InitializeNetworkRunner(NetworkRunner runner, GameMode gameMode, NetAddress address, SceneRef scene, Action initialized) { + + var sceneObjectProvider = runner.GetComponents(typeof(MonoBehaviour)).OfType().FirstOrDefault(); + if (sceneObjectProvider == null) { + Debug.Log($"NetworkRunner does not have any component implementing {nameof(INetworkSceneObjectProvider)} interface, adding {nameof(NetworkSceneManagerDefault)}.", runner); + sceneObjectProvider = runner.gameObject.AddComponent(); + } + + return runner.StartGame(new StartGameArgs { + GameMode = gameMode, + Address = address, + Scene = scene, + SessionName = DefaultRoomName, + Initialized = initialized, + SceneObjectProvider = sceneObjectProvider + }); + } + +#if UNITY_EDITOR + // Draws the button at the bottom of the inspector if scene currently is not added to Build Settings scene list. + [BehaviourAction()] + void DisplayAddToSceneButtonIfNeeded() { + if (Application.isPlaying) + return; + var currentScene = SceneManager.GetActiveScene(); + if (currentScene.TryGetSceneIndexInBuildSettings(out var _) == false) { + GUILayout.Space(4); + var clicked = BehaviourEditorUtils.DrawWarnButton(new GUIContent("Add Scene To Settings", "Will add current scene to Unity Build Settings list."), MessageType.Warning); + if (clicked) { + if (currentScene.name == "") { + UnityEditor.SceneManagement.EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); + } + + if (currentScene.name != "") { + currentScene.AddSceneToBuildSettings(); + } + } + } + } + [BehaviourAction()] + void ShowDetails() { + using (new EditorGUI.DisabledScope(true)) { + EditorGUILayout.LabelField(nameof(CurrentStage), CurrentStage.ToString()); + } + } +#endif +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs.meta new file mode 100644 index 0000000..91c28ff --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStart.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 88230848386761045af440d808ee3efa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs new file mode 100644 index 0000000..f70d81b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs @@ -0,0 +1,356 @@ +using System; +using UnityEngine; +using Fusion; +using System.Collections.Generic; + +/// +/// Companion component for . Automatically added as needed for rendering in-game networking IMGUI. +/// +[RequireComponent(typeof(NetworkDebugStart))] +[AddComponentMenu("Fusion/Network Debug Start GUI")] +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class NetworkDebugStartGUI : Fusion.Behaviour { + + /// + /// When enabled, the in-game user interface buttons can be activated with the keys H (Host), S (Server) and C (Client). + /// + public bool EnableHotkeys; + + /// + /// The GUISkin to use as the base for the scalable in-game UI. + /// + public GUISkin BaseSkin; + + NetworkDebugStart _networkDebugStart; + string _clientCount; + bool _isMultiplePeerMode; + + Dictionary _nicifiedStageNames; + +#if UNITY_EDITOR + + protected virtual void Reset() { + _networkDebugStart = EnsureNetworkDebugStartExists(); + _clientCount = _networkDebugStart.AutoClients.ToString(); + BaseSkin = GetAsset("e59b35dfeb4b6f54e9b2791b2a40a510"); + } + +#endif + + protected virtual void OnValidate() { + ValidateClientCount(); + } + + protected void ValidateClientCount() { + if (_clientCount == null) { + _clientCount = "1"; + } else { + _clientCount = System.Text.RegularExpressions.Regex.Replace(_clientCount, "[^0-9]", ""); + } + } + protected int GetClientCount() { + try { + return Convert.ToInt32(_clientCount); + } catch { + return 0; + } + } + + protected virtual void Awake() { + + _nicifiedStageNames = ConvertEnumToNicifiedNameLookup("Fusion Status: "); + _networkDebugStart = EnsureNetworkDebugStartExists(); + _clientCount = _networkDebugStart.AutoClients.ToString(); + ValidateClientCount(); + } + protected virtual void Start() { + _isMultiplePeerMode = NetworkProjectConfig.Global.PeerMode == NetworkProjectConfig.PeerModes.Multiple; + } + + protected NetworkDebugStart EnsureNetworkDebugStartExists() { + if (_networkDebugStart) { + if (_networkDebugStart.gameObject == gameObject) + return _networkDebugStart; + } + + if (TryGetBehaviour(out var found)) { + _networkDebugStart = found; + return found; + } + + _networkDebugStart = AddBehaviour(); + return _networkDebugStart; + } + + private void Update() { + + var nds = EnsureNetworkDebugStartExists(); + if (nds.StartMode != NetworkDebugStart.StartModes.UserInterface) { + return; + } + + var currentstage = nds.CurrentStage; + if (currentstage != NetworkDebugStart.Stage.Disconnected) { + return; + } + + if (EnableHotkeys) { + if (Input.GetKeyDown(KeyCode.I)) { + _networkDebugStart.StartSinglePlayer(); + } + + if (Input.GetKeyDown(KeyCode.H)) { + if (_isMultiplePeerMode) { + StartHostWithClients(_networkDebugStart); + } else { + _networkDebugStart.StartHost(); + } + } + + if (Input.GetKeyDown(KeyCode.S)) { + if (_isMultiplePeerMode) { + StartServerWithClients(_networkDebugStart); + } else { + _networkDebugStart.StartServer(); + } + } + + if (Input.GetKeyDown(KeyCode.C)) { + if (_isMultiplePeerMode) { + StartMultipleClients(nds); + } else { + nds.StartClient(); + } + } + + if (Input.GetKeyDown(KeyCode.A)) { + if (_isMultiplePeerMode) { + StartMultipleAutoClients(nds); + } else { + nds.StartAutoClient(); + } + } + + if (Input.GetKeyDown(KeyCode.P)) { + if (_isMultiplePeerMode) { + StartMultipleSharedClients(nds); + } else { + nds.StartSharedClient(); + } + } + } + } + + protected virtual void OnGUI() { + + var nds = EnsureNetworkDebugStartExists(); + if (nds.StartMode != NetworkDebugStart.StartModes.UserInterface) { + return; + } + + var currentstage = nds.CurrentStage; + if (nds.AutoHideGUI && currentstage == NetworkDebugStart.Stage.AllConnected) { + return; + } + + var holdskin = GUI.skin; + + GUI.skin = FusionScalableIMGUI.GetScaledSkin(BaseSkin, out var height, out var width, out var padding, out var margin, out var leftBoxMargin); + + GUILayout.BeginArea(new Rect(leftBoxMargin, margin, width, Screen.height)); + { + GUILayout.BeginVertical(GUI.skin.window); + { + GUILayout.BeginHorizontal(GUILayout.Height(height)); + { + var stagename = _nicifiedStageNames.TryGetValue(nds.CurrentStage, out var stage) ? stage : "Unrecognized Stage"; + GUILayout.Label(stagename, new GUIStyle(GUI.skin.label) { fontSize = (int)(GUI.skin.label.fontSize * .8f), alignment = TextAnchor.UpperLeft }); + + // Add button to hide Shutdown option after all connect, which just enables AutoHide - so that interface will reappear after a disconnect. + if (nds.AutoHideGUI == false && nds.CurrentStage == NetworkDebugStart.Stage.AllConnected) { + if (GUILayout.Button("X", GUILayout.ExpandHeight(true), GUILayout.Width(height))) { + nds.AutoHideGUI = true; + } + } + } + GUILayout.EndHorizontal(); + } + GUILayout.EndVertical(); + + GUILayout.BeginVertical(GUI.skin.window); + { + + if (currentstage == NetworkDebugStart.Stage.Disconnected) { + + GUILayout.BeginHorizontal(); + { + GUILayout.Label("Room:", GUILayout.Height(height), GUILayout.Width(width * .33f)); + nds.DefaultRoomName = GUILayout.TextField(nds.DefaultRoomName, 25, GUILayout.Height(height)); + } + GUILayout.EndHorizontal(); + + if (GUILayout.Button(EnableHotkeys ? "Start Single Player (I)" : "Start Single Player", GUILayout.Height(height))) { + nds.StartSinglePlayer(); + } + + if (GUILayout.Button(EnableHotkeys ? "Start Shared Client (P)" : "Start Shared Client", GUILayout.Height(height))) { + if (_isMultiplePeerMode) { + StartMultipleSharedClients(nds); + } else { + nds.StartSharedClient(); + } + } + + if (GUILayout.Button(EnableHotkeys ? "Start Server (S)" : "Start Server", GUILayout.Height(height))) { + if (_isMultiplePeerMode) { + StartServerWithClients(nds); + + } else { + nds.StartServer(); + } + } + + if (GUILayout.Button(EnableHotkeys ? "Start Host (H)" : "Start Host", GUILayout.Height(height))) { + if (_isMultiplePeerMode) { + StartHostWithClients(nds); + } else { + nds.StartHost(); + } + } + + if (GUILayout.Button(EnableHotkeys ? "Start Client (C)" : "Start Client", GUILayout.Height(height))) { + if (_isMultiplePeerMode) { + StartMultipleClients(nds); + } else { + nds.StartClient(); + } + } + + if (GUILayout.Button(EnableHotkeys ? "Start Auto Host Or Client (A)" : "Start Auto Host Or Client", GUILayout.Height(height))) { + if (_isMultiplePeerMode) { + StartMultipleAutoClients(nds); + } else { + nds.StartAutoClient(); + } + } + + if (_isMultiplePeerMode) { + + GUILayout.BeginHorizontal(/*GUI.skin.button*/); + { + GUILayout.Label("Client Count:", GUILayout.Height(height)); + GUILayout.Label("", GUILayout.Width(4)); + string newcount = GUILayout.TextField(_clientCount, 10, GUILayout.Width(width * .25f), GUILayout.Height(height)); + if (_clientCount != newcount) { + // Remove everything but numbers from our client count string. + _clientCount = newcount; + ValidateClientCount(); + } + } + GUILayout.EndHorizontal(); + } + } else { + + if (GUILayout.Button("Shutdown", GUILayout.Height(height))) { + _networkDebugStart.ShutdownAll(); + } + } + + GUILayout.EndVertical(); + } + } + GUILayout.EndArea(); + + GUI.skin = holdskin; + } + + private void StartHostWithClients(NetworkDebugStart nds) { + int count; + try { + count = Convert.ToInt32(_clientCount); + } catch { + count = 0; + } + nds.StartHostPlusClients(count); + } + + private void StartServerWithClients(NetworkDebugStart nds) { + int count; + try { + count = Convert.ToInt32(_clientCount); + } catch { + count = 0; + } + nds.StartServerPlusClients(count); + } + + private void StartMultipleClients(NetworkDebugStart nds) { + int count; + try { + count = Convert.ToInt32(_clientCount); + } catch { + count = 0; + } + nds.StartMultipleClients(count); + } + + private void StartMultipleAutoClients(NetworkDebugStart nds) { + int.TryParse(_clientCount, out int count); + nds.StartMultipleAutoClients(count); + } + + private void StartMultipleSharedClients(NetworkDebugStart nds) { + int count; + try { + count = Convert.ToInt32(_clientCount); + } catch { + count = 0; + } + nds.StartMultipleSharedClients(count); + } + + // TODO Move to a utility + public static Dictionary ConvertEnumToNicifiedNameLookup(string prefix = null, Dictionary nonalloc = null) where T : System.Enum { + + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + + if (nonalloc == null) { + nonalloc = new Dictionary(); + } else { + nonalloc.Clear(); + } + + var names = Enum.GetNames(typeof(T)); + var values = Enum.GetValues(typeof(T)); + for (int i = 0, cnt = names.Length; i < cnt; ++i) { + sb.Clear(); + if (prefix != null) { + sb.Append(prefix); + } + var name = names[i]; + for (int n = 0; n < name.Length; n++) { + // If this character is a capital and it is not the first character add a space. + // This is because we don't want a space before the word has even begun. + if (char.IsUpper(name[n]) == true && n != 0) { + sb.Append(" "); + } + + // Add the character to our new string + sb.Append(name[n]); + } + nonalloc.Add((T)values.GetValue(i), sb.ToString()); + } + return nonalloc; + } +#if UNITY_EDITOR + + public static T GetAsset(string Guid) where T : UnityEngine.Object { + var path = UnityEditor.AssetDatabase.GUIDToAssetPath(Guid); + if (string.IsNullOrEmpty(path)) { + return null; + } else { + return UnityEditor.AssetDatabase.LoadAssetAtPath(path); + } + } +#endif +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs.meta new file mode 100644 index 0000000..e691a30 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ba5764f4714bd64ab9efab127938e77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin new file mode 100644 index 0000000..9583434 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin @@ -0,0 +1,1560 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12001, guid: 0000000000000000e000000000000000, type: 0} + m_Name: NetworkDebugStartSkin + m_EditorClassIdentifier: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_box: + m_Name: box + m_Normal: + m_Background: {fileID: 11001, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_button: + m_Name: button + m_Normal: + m_Background: {fileID: 11001, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8, g: 0.8, b: 0.8, a: 1} + m_Hover: + m_Background: {fileID: 11023, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11002, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11005, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_OnHover: + m_Background: {fileID: 11004, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11002, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 8 + m_Bottom: 8 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 7 + m_Bottom: 7 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_toggle: + m_Name: toggle + m_Normal: + m_Background: {fileID: 11018, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.89112896, g: 0.89112896, b: 0.89112896, a: 1} + m_Hover: + m_Background: {fileID: 11014, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11013, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11016, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8901961, g: 0.8901961, b: 0.8901961, a: 1} + m_OnHover: + m_Background: {fileID: 11015, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11017, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 14 + m_Right: 0 + m_Top: 14 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 15 + m_Right: 0 + m_Top: 3 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: 0 + m_Top: -4 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_label: + m_Name: label + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 15 + m_Right: 15 + m_Top: 15 + m_Bottom: 15 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_Alignment: 3 + m_WordWrap: 1 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textField: + m_Name: textfield + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 24 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 3 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textArea: + m_Name: textarea + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_window: + m_Name: window + m_Normal: + m_Background: {fileID: 11023, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11022, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 8 + m_Bottom: 10 + m_Margin: + m_Left: 7 + m_Right: 7 + m_Top: 7 + m_Bottom: 7 + m_Padding: + m_Left: 15 + m_Right: 15 + m_Top: 15 + m_Bottom: 15 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: -18} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSlider: + m_Name: horizontalslider + m_Normal: + m_Background: {fileID: 11009, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 3 + m_Right: 3 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -2 + m_Bottom: -3 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSliderThumb: + m_Name: horizontalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 7 + m_Right: 7 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalSlider: + m_Name: verticalslider + m_Normal: + m_Background: {fileID: 11021, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Overflow: + m_Left: -2 + m_Right: -3 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalSliderThumb: + m_Name: verticalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 7 + m_Bottom: 7 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_horizontalScrollbar: + m_Name: horizontalscrollbar + m_Normal: + m_Background: {fileID: 11008, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 9 + m_Right: 9 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 1 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 15 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarThumb: + m_Name: horizontalscrollbarthumb + m_Normal: + m_Background: {fileID: 11007, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: 1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 13 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarLeftButton: + m_Name: horizontalscrollbarleftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarRightButton: + m_Name: horizontalscrollbarrightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbar: + m_Name: verticalscrollbar + m_Normal: + m_Background: {fileID: 11020, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 9 + m_Bottom: 9 + m_Margin: + m_Left: 1 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 1 + m_Bottom: 1 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarThumb: + m_Name: verticalscrollbarthumb + m_Normal: + m_Background: {fileID: 11019, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 6 + m_Bottom: 6 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalScrollbarUpButton: + m_Name: verticalscrollbarupbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarDownButton: + m_Name: verticalscrollbardownbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_ScrollView: + m_Name: scrollview + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_CustomStyles: + - m_Name: thumb + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: leftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + - m_Name: rightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_Settings: + m_DoubleClickSelectsWord: 1 + m_TripleClickSelectsLine: 1 + m_CursorColor: {r: 1, g: 1, b: 1, a: 1} + m_CursorFlashSpeed: -1 + m_SelectionColor: {r: 1, g: 0.38403907, b: 0, a: 0.7} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin.meta new file mode 100644 index 0000000..6c66f84 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkDebugStartSkin.guiskin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e59b35dfeb4b6f54e9b2791b2a40a510 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs new file mode 100644 index 0000000..f6a5659 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs @@ -0,0 +1,55 @@ +using UnityEngine.SceneManagement; + +namespace Fusion { + public static class NetworkRunnerExtensions { + public static bool SetActiveScene(this NetworkRunner runner, string sceneNameOrPath) { + if (TryGetSceneBuildIndex(sceneNameOrPath, out var buildIndex)) { + runner.SetActiveScene(buildIndex); + return true; + } else { + return false; + } + } + + static bool TryGetSceneBuildIndex(string nameOrPath, out int buildIndex) { + nameOrPath = System.IO.Path.GetFileNameWithoutExtension(nameOrPath); + if (nameOrPath.IndexOf('/') >= 0) { + buildIndex = SceneUtility.GetBuildIndexByScenePath(nameOrPath); + if (buildIndex < 0) { + buildIndex = -1; + return false; + } else { + return true; + } + } else { + for (int i = 0; i < SceneManager.sceneCountInBuildSettings; ++i) { + var scenePath = SceneUtility.GetScenePathByBuildIndex(i); + GetFileNameWithoutExtensionPosition(scenePath, out var nameIndex, out var nameLength); + if (nameLength == nameOrPath.Length && string.Compare(scenePath, nameIndex, nameOrPath, 0, nameLength, true) == 0) { + buildIndex = i; + return true; + } + } + + buildIndex = -1; + return false; + } + } + + static void GetFileNameWithoutExtensionPosition(string nameOrPath, out int index, out int length) { + var lastSlash = nameOrPath.LastIndexOf('/'); + if (lastSlash >= 0) { + index = lastSlash + 1; + } else { + index = 0; + } + + var lastDot = nameOrPath.LastIndexOf('.'); + if (lastDot > index) { + length = lastDot - index; + } else { + length = nameOrPath.Length - index; + } + } + } +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs.meta new file mode 100644 index 0000000..24dace4 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkRunnerExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 208ec9ab61a534c4d9c951cf1bf2536d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs new file mode 100644 index 0000000..bd12b3d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs @@ -0,0 +1,293 @@ +// #define FUSION_NETWORK_SCENE_MANAGER_TRACE + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + + +namespace Fusion { + + + public abstract class NetworkSceneManagerBase : Fusion.Behaviour, INetworkSceneObjectProvider { + + private static WeakReference s_currentlyLoading = new WeakReference(null); + + public bool ShowHierarchyWindowOverlay = true; + + private IEnumerator _runningCoroutine; + private bool _currentSceneOutdated = false; + private Dictionary _sceneObjects = new Dictionary(); + private SceneRef _currentScene; + + public NetworkRunner Runner { get; private set; } + + + protected virtual void OnEnable() { +#if UNITY_EDITOR + if (ShowHierarchyWindowOverlay) { + UnityEditor.EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowOverlay; + } +#endif + } + + protected virtual void OnDisable() { +#if UNITY_EDITOR + UnityEditor.EditorApplication.hierarchyWindowItemOnGUI -= HierarchyWindowOverlay; +#endif + } + + protected virtual void LateUpdate() { + if (!Runner) { + return; + } + + // store the flag in case scene changes during the load; this supports scene toggling as well + if (Runner.CurrentScene != _currentScene) { + _currentSceneOutdated = true; + } + + if (!_currentSceneOutdated || _runningCoroutine != null) { + // busy or up to date + return; + } + + if (s_currentlyLoading.TryGetTarget(out var target)) { + Assert.Check(target != this); + if (!target) { + // orphaned loader? + s_currentlyLoading.SetTarget(null); + } else { + LogTrace($"Waiting for {target} to finish loading"); + return; + } + } + + var prevScene = _currentScene; + _currentScene = Runner.CurrentScene; + _currentSceneOutdated = false; + + LogTrace($"Scene transition {prevScene}->{_currentScene}"); + _runningCoroutine = SwitchSceneWrapper(prevScene, _currentScene); + StartCoroutine(_runningCoroutine); + } + + public static bool IsScenePathOrNameEqual(Scene scene, string nameOrPath) { + return scene.path == nameOrPath || scene.name == nameOrPath; + } + + public static bool TryGetScenePathFromBuildSettings(SceneRef sceneRef, out string path) { + if (sceneRef.IsValid) { + path = SceneUtility.GetScenePathByBuildIndex(sceneRef); + if (!string.IsNullOrEmpty(path)) { + return true; + } + } + path = string.Empty; + return false; + } + + public bool IsScenePathOrNameEqual(Scene scene, SceneRef sceneRef) { + if (TryGetScenePathFromBuildSettings(sceneRef, out var path)) { + return IsScenePathOrNameEqual(scene, path); + } else { + return false; + } + } + + public List FindNetworkObjects(Scene scene, bool disable = true, bool addVisibilityNodes = false) { + + var networkObjects = new List(); + var gameObjects = scene.GetRootGameObjects(); + var result = new List(); + + // get all root gameobjects and move them to this runners scene + foreach (var go in gameObjects) { + networkObjects.Clear(); + go.GetComponentsInChildren(true, networkObjects); + + foreach (var sceneObject in networkObjects) { + if (sceneObject.Flags.IsSceneObject()) { + if (sceneObject.gameObject.activeInHierarchy || sceneObject.Flags.IsActivatedByUser()) { + Assert.Check(sceneObject.NetworkGuid.IsValid); + result.Add(sceneObject); + } + } + } + + if (addVisibilityNodes) { + // register all render related components on this gameobject with the runner, for use with IsVisible + RunnerVisibilityNode.AddVisibilityNodes(go, Runner); + } + } + + if (disable) { + // disable objects; each will be activated if there's a matching state object + foreach (var sceneObject in result) { + sceneObject.gameObject.SetActive(false); + } + } + + return result; + } + + + #region INetworkSceneObjectProvider + + void INetworkSceneObjectProvider.Initialize(NetworkRunner runner) { + Initialize(runner); + } + + void INetworkSceneObjectProvider.Shutdown(NetworkRunner runner) { + Shutdown(runner); + } + + bool INetworkSceneObjectProvider.IsReady(NetworkRunner runner) { + Assert.Check(Runner == runner); + if (_runningCoroutine != null) { + return false; + } + if (_currentSceneOutdated) { + return false; + } + if (runner.CurrentScene != _currentScene) { + return false; + } + return true; + } + + bool INetworkSceneObjectProvider.TryResolveSceneObject(NetworkRunner runner, Guid sceneObjectGuid, out NetworkObject instance) { + Assert.Check(Runner == runner); + return _sceneObjects.TryGetValue(sceneObjectGuid, out instance); + } + + #endregion + + protected virtual void Initialize(NetworkRunner runner) { + Assert.Check(!Runner); + Runner = runner; + } + + protected virtual void Shutdown(NetworkRunner runner) { + Assert.Check(Runner == runner); + + try { + // ongoing loading, dispose + if (_runningCoroutine != null) { + LogWarn($"There is an ongoing scene load ({_currentScene}), stopping and disposing coroutine."); + StopCoroutine(_runningCoroutine); + (_runningCoroutine as IDisposable)?.Dispose(); + } + } finally { + Runner = null; + _runningCoroutine = null; + _currentScene = SceneRef.None; + _currentSceneOutdated = false; + _sceneObjects.Clear(); + } + } + + protected delegate void FinishedLoadingDelegate(IEnumerable sceneObjects); + + protected abstract IEnumerator SwitchScene(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished); + + [System.Diagnostics.Conditional("FUSION_NETWORK_SCENE_MANAGER_TRACE")] + protected void LogTrace(string msg) { + Log.Debug($"[NetworkSceneManager] {(this != null ? this.name : "")}: {msg}"); + } + + protected void LogError(string msg) { + Log.Error($"[NetworkSceneManager] {(this != null ? this.name : "")}: {msg}"); + } + + protected void LogWarn(string msg) { + Log.Warn($"[NetworkSceneManager] {(this != null ? this.name : "")}: {msg}"); + } + + + private IEnumerator SwitchSceneWrapper(SceneRef prevScene, SceneRef newScene) { + bool finishCalled = false; + Dictionary sceneObjects = new Dictionary(); + Exception error = null; + FinishedLoadingDelegate callback = (objects) => { + finishCalled = true; + foreach (var obj in objects) { + sceneObjects.Add(obj.NetworkGuid, obj); + } + }; + + try { + Assert.Check(!s_currentlyLoading.TryGetTarget(out _)); + s_currentlyLoading.SetTarget(this); + Runner.InvokeSceneLoadStart(); + var coro = SwitchScene(prevScene, newScene, callback); + + for (bool next = true; next;) { + try { + next = coro.MoveNext(); + } catch (Exception ex) { + error = ex; + break; + } + + if (next) { + yield return coro.Current; + } + } + } finally { + Assert.Check(s_currentlyLoading.TryGetTarget(out var target) && target == this); + s_currentlyLoading.SetTarget(null); + + LogTrace($"Coroutine finished for {newScene}"); + _runningCoroutine = null; + } + + if (error != null) { + LogError($"Failed to switch scenes: {error}"); + } else if (!finishCalled) { + LogError($"Failed to switch scenes: SwitchScene implementation did not invoke finished delegate"); + } else { + _sceneObjects = sceneObjects; + Runner.RegisterUniqueObjects(_sceneObjects.Values); + Runner.InvokeSceneLoadDone(); + } + } + +#if UNITY_EDITOR + private static Lazy s_hierarchyOverlayLabelStyle = new Lazy(() => { + var result = new GUIStyle(UnityEditor.EditorStyles.miniButton); + result.alignment = TextAnchor.MiddleCenter; + result.fontSize = 9; + result.padding = new RectOffset(4, 4, 0, 0); + result.fixedHeight = 13f; + return result; + }); + + private void HierarchyWindowOverlay(int instanceId, Rect position) { + if (!Runner) { + return; + } + + if (!Runner.MultiplePeerUnityScene.IsValid()) { + return; + } + + if (Runner.MultiplePeerUnityScene.GetHashCode() == instanceId) { + + var rect = new Rect(position) { + xMin = position.xMax - 56, + xMax = position.xMax - 2, + yMin = position.yMin + 1, + }; + + if (GUI.Button(rect, $"{Runner.Mode} {(Runner.LocalPlayer.IsValid ? "P" + Runner.LocalPlayer.PlayerId.ToString() : "")}", s_hierarchyOverlayLabelStyle.Value)) { + UnityEditor.EditorGUIUtility.PingObject(Runner); + UnityEditor.Selection.activeGameObject = Runner.gameObject; + } + } + } + +#endif + } +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs.meta new file mode 100644 index 0000000..5a48379 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f195569b39425904b847cd8547c866f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs new file mode 100644 index 0000000..160299b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs @@ -0,0 +1,154 @@ +// #define FUSION_NETWORK_SCENE_MANAGER_TRACE + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.SceneManagement; + +namespace Fusion { + + + public class NetworkSceneManagerDefault : NetworkSceneManagerBase { + + [Header("Single Peer Options")] + public int PostLoadDelayFrames = 1; + + protected virtual YieldInstruction LoadSceneAsync(SceneRef sceneRef, LoadSceneParameters parameters, Action loaded) { + + if (!TryGetScenePathFromBuildSettings(sceneRef, out var scenePath)) { + throw new InvalidOperationException($"Not going to load {sceneRef}: unable to find the scene name"); + } + + var op = SceneManager.LoadSceneAsync(scenePath, parameters); + Assert.Check(op); + + bool alreadyHandled = false; + + // if there's a better way to get scene struct more reliably I'm dying to know + UnityAction sceneLoadedHandler = (scene, _) => { + if (IsScenePathOrNameEqual(scene, scenePath)) { + Assert.Check(!alreadyHandled); + alreadyHandled = true; + loaded(scene); + } + }; + SceneManager.sceneLoaded += sceneLoadedHandler; + op.completed += _ => { + SceneManager.sceneLoaded -= sceneLoadedHandler; + }; + + return op; + } + + protected virtual YieldInstruction UnloadSceneAsync(Scene scene) { + return SceneManager.UnloadSceneAsync(scene); + } + + protected override IEnumerator SwitchScene(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) { + if (Runner.Config.PeerMode == NetworkProjectConfig.PeerModes.Single) { + return SwitchSceneSinglePeer(prevScene, newScene, finished); + } else { + return SwitchSceneMultiplePeer(prevScene, newScene, finished); + } + } + + protected virtual IEnumerator SwitchSceneMultiplePeer(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) { + + Scene activeScene = SceneManager.GetActiveScene(); + + bool canTakeOverActiveScene = prevScene == default && IsScenePathOrNameEqual(activeScene, newScene); + + LogTrace($"Start loading scene {newScene} in multi peer mode"); + var loadSceneParameters = new LoadSceneParameters(LoadSceneMode.Additive, NetworkProjectConfig.ConvertPhysicsMode(Runner.Config.PhysicsEngine)); + + var sceneToUnload = Runner.MultiplePeerUnityScene; + var tempSceneSpawnedPrefabs = Runner.IsMultiplePeerSceneTemp ? sceneToUnload.GetRootGameObjects() : Array.Empty(); + + if (canTakeOverActiveScene && NetworkRunner.GetRunnerForScene(activeScene) == null && SceneManager.sceneCount > 1) { + LogTrace("Going to attempt to unload the initial scene as it needs a separate Physics stage"); + yield return UnloadSceneAsync(activeScene); + } + + if (SceneManager.sceneCount == 1 && tempSceneSpawnedPrefabs.Length == 0) { + // can load non-additively, stuff will simply get unloaded + LogTrace($"Only one scene remained, going to load non-additively"); + loadSceneParameters.loadSceneMode = LoadSceneMode.Single; + } else if (sceneToUnload.IsValid()) { + // need a new temp scene here; otherwise calls to PhysicsStage will fail + if (Runner.TryMultiplePeerAssignTempScene()) { + LogTrace($"Unloading previous scene: {sceneToUnload}, temp scene created"); + yield return UnloadSceneAsync(sceneToUnload); + } + } + + LogTrace($"Loading scene {newScene} with parameters: {JsonUtility.ToJson(loadSceneParameters)}"); + + Scene loadedScene = default; + yield return LoadSceneAsync(newScene, loadSceneParameters, scene => loadedScene = scene); + + LogTrace($"Loaded scene {newScene} with parameters: {JsonUtility.ToJson(loadSceneParameters)}: {loadedScene}"); + + if (!loadedScene.IsValid()) { + throw new InvalidOperationException($"Failed to load scene {newScene}: async op failed"); + } + + var sceneObjects = FindNetworkObjects(loadedScene, disable: true, addVisibilityNodes: true); + + // unload temp scene + var tempScene = Runner.MultiplePeerUnityScene; + Runner.MultiplePeerUnityScene = loadedScene; + if (tempScene.IsValid()) { + if (tempSceneSpawnedPrefabs.Length > 0) { + LogTrace($"Temp scene has {tempSceneSpawnedPrefabs.Length} spawned prefabs, need to move them to the loaded scene."); + foreach (var go in tempSceneSpawnedPrefabs) { + Assert.Check(go.GetComponent(), $"Expected {nameof(NetworkObject)} on a GameObject spawned on the temp scene {tempScene.name}"); + SceneManager.MoveGameObjectToScene(go, loadedScene); + } + } + LogTrace($"Unloading temp scene {tempScene}"); + yield return UnloadSceneAsync(tempScene); + } + + finished(sceneObjects); + } + + protected virtual IEnumerator SwitchSceneSinglePeer(SceneRef prevScene, SceneRef newScene, FinishedLoadingDelegate finished) { + + Scene loadedScene; + Scene activeScene = SceneManager.GetActiveScene(); + + bool canTakeOverActiveScene = prevScene == default && IsScenePathOrNameEqual(activeScene, newScene); + + if (canTakeOverActiveScene) { + LogTrace($"Not going to load initial scene {newScene} as this is the currently active scene"); + loadedScene = activeScene; + } else { + + LogTrace($"Start loading scene {newScene} in single peer mode"); + LoadSceneParameters loadSceneParameters = new LoadSceneParameters(LoadSceneMode.Single); + + loadedScene = default; + LogTrace($"Loading scene {newScene} with parameters: {JsonUtility.ToJson(loadSceneParameters)}"); + + yield return LoadSceneAsync(newScene, loadSceneParameters, scene => loadedScene = scene); + + LogTrace($"Loaded scene {newScene} with parameters: {JsonUtility.ToJson(loadSceneParameters)}: {loadedScene}"); + + if (!loadedScene.IsValid()) { + throw new InvalidOperationException($"Failed to load scene {newScene}: async op failed"); + } + } + + for (int i = PostLoadDelayFrames; i > 0; --i) { + yield return null; + } + + var sceneObjects = FindNetworkObjects(loadedScene, disable: true); + finished(sceneObjects); + } + + } +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs.meta new file mode 100644 index 0000000..373ed1c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/NetworkSceneManagerDefault.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 90a93abf1a391964a94e5c139605105b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs new file mode 100644 index 0000000..ff23cfb --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs @@ -0,0 +1,37 @@ + +using Fusion; + +/// +/// Prototyping component for spawning Player avatars. +/// +[SimulationBehaviour(Stages = SimulationStages.Forward, Modes = SimulationModes.Server | SimulationModes.Host)] +public class PlayerSpawnerPrototype : SpawnerPrototype, IPlayerJoined, IPlayerLeft, ISceneLoadDone { + +#if UNITY_EDITOR + + protected virtual void Reset() { + var protoPlayer = FusionPrototypingPrefabs.BasicPlayer; + if (protoPlayer) + PlayerPrefab = protoPlayer.GetComponent(); + } + + + [BehaviourButtonAction("Add Player Spawn Point Manager", false, true, nameof(_spawnPointManagerMissing))] + private void InspectorWarnMissingSpawnPointManager() { + AddBehaviour(); + } + +#endif + + protected override void RegisterPlayerAndObject(PlayerRef player, NetworkObject playerObject) { + base.RegisterPlayerAndObject(player, playerObject); + + Runner.SetPlayerObject(player, playerObject); + } + +} + + + + + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs.meta new file mode 100644 index 0000000..61df5d1 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/PlayerSpawnerPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e2e361bfb0186044286ddbd94f2546dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs.meta new file mode 100644 index 0000000..6e00e83 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73dd01bf9662e3148ae01012221a7ce2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab new file mode 100644 index 0000000..bb7fe94 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab @@ -0,0 +1,515 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &815038362430086094 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 815038362430085941} + - component: {fileID: 815038362430085940} + - component: {fileID: 815038362430086091} + - component: {fileID: 815038362430086095} + - component: {fileID: 815038362430086089} + - component: {fileID: 815038362430086088} + m_Layer: 0 + m_Name: Z + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &815038362430085941 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.1, y: 1, z: 0.10000001} + m_Children: [] + m_Father: {fileID: 815038364241965670} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &815038362430085940 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &815038362430086091 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 1e79a222d16731a4e96e7360bf14bdfd, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!114 &815038362430086095 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + AoiMode: 0 + DefaultPropertyGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPosition: {fileID: 0} + Flags: 3073 + NetworkGuid: + RawGuidValue: 00000000000000000000000000000000 + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 815038362430086088} + SimulationBehaviours: [] +--- !u!54 &815038362430086089 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!114 &815038362430086088 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038362430086094} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1710889294, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationTarget: {fileID: 0} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 +--- !u!1 &815038363609415738 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 815038363609415713} + - component: {fileID: 815038363609415718} + - component: {fileID: 815038363609415717} + - component: {fileID: 815038363609415716} + - component: {fileID: 815038363609415739} + m_Layer: 0 + m_Name: BallPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &815038363609415713 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363609415738} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.0083206, y: 1.0107286, z: 4.0235205} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 815038364241965670} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &815038363609415718 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363609415738} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &815038363609415717 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363609415738} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + AoiMode: 0 + DefaultPropertyGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPosition: {fileID: 0} + Flags: 2305 + NetworkGuid: + RawGuidValue: 61b6ef4ffff3d8f4498968869591d6bf + NestedObjects: + - {fileID: 815038362430086095} + NetworkedBehaviours: + - {fileID: 815038363609415739} + SimulationBehaviours: [] +--- !u!54 &815038363609415716 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363609415738} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!114 &815038363609415739 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363609415738} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1710889294, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationTarget: {fileID: 815038364241965670} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 +--- !u!1 &815038363759353808 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 815038363759353809} + - component: {fileID: 815038363759353820} + - component: {fileID: 815038363759353811} + m_Layer: 0 + m_Name: Y + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &815038363759353809 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363759353808} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.1, y: 1, z: 0.1} + m_Children: [] + m_Father: {fileID: 815038364241965670} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &815038363759353820 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363759353808} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &815038363759353811 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038363759353808} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8f9372f58500a3f46ba541ea24e6d105, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &815038364034096620 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 815038364034096621} + - component: {fileID: 815038364034096616} + - component: {fileID: 815038364034096623} + m_Layer: 0 + m_Name: X + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &815038364034096621 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364034096620} + m_LocalRotation: {x: -0, y: -0, z: 0.7071068, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.10000001, y: 1, z: 0.1} + m_Children: [] + m_Father: {fileID: 815038364241965670} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 90} +--- !u!33 &815038364034096616 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364034096620} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &815038364034096623 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364034096620} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 42b24d338df372049a18bd257e6bc550, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &815038364241965669 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 815038364241965670} + - component: {fileID: 815038364241965664} + - component: {fileID: 815038364241965671} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &815038364241965670 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364241965669} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 815038364034096621} + - {fileID: 815038363759353809} + - {fileID: 815038362430085941} + m_Father: {fileID: 815038363609415713} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &815038364241965664 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364241965669} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &815038364241965671 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 815038364241965669} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab.meta new file mode 100644 index 0000000..0aa9ed9 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/BallPrototype.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 61b6ef4ffff3d8f4498968869591d6bf +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs new file mode 100644 index 0000000..bcbe936 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs @@ -0,0 +1,32 @@ +#if UNITY_EDITOR + +using UnityEditor; +using UnityEngine; + +public static class FusionPrototypingPrefabs +{ + + private const string _basicPlayerGuid = "e4df49d0bf125a740a2c14ab6e887572"; + private static GameObject _basicPlayer; + public static GameObject BasicPlayer { get { return GetPrefab(_basicPlayerGuid, ref _basicPlayer); } } + + private const string _basicPlayerRB2DGuid = "dbc9b57ea26fbf84b8cc14b0882fe89b"; + private static GameObject _basicPlayerRB2D; + public static GameObject BasicPlayerRB2D { get { return GetPrefab(_basicPlayerRB2DGuid, ref _basicPlayerRB2D); } } + + private const string _ground2DGuid = "5998d41545749df4f82cfabc16cdd7a0"; + private static GameObject _ground2D; + public static GameObject Ground2D { get { return GetPrefab(_ground2DGuid, ref _ground2D); } } + + private static GameObject GetPrefab(string guid, ref GameObject backingField) { + if (backingField) + return backingField; + + var path = AssetDatabase.GUIDToAssetPath(guid); + backingField = AssetDatabase.LoadAssetAtPath(path); + return backingField; + } + +} + +#endif \ No newline at end of file diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs.meta new file mode 100644 index 0000000..2cb8d0c --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/FusionPrototypingPrefabs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 494a9e73fe46ad04dae98802fc8c26ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab new file mode 100644 index 0000000..c779a61 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab @@ -0,0 +1,142 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &3439814401116981165 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3439814401116981164} + - component: {fileID: 3439814401116981167} + m_Layer: 0 + m_Name: Cosmetic + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3439814401116981164 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3439814401116981165} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_Children: [] + m_Father: {fileID: 3439814402987491865} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &3439814401116981167 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3439814401116981165} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 21300000, guid: 5422498f780e1364b9989ffa3292a010, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.01, y: 0.01} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1 &3439814402987491863 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3439814402987491865} + - component: {fileID: 3439814402987491862} + m_Layer: 0 + m_Name: Ground2DPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3439814402987491865 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3439814402987491863} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -5, z: 0} + m_LocalScale: {x: 20, y: 10, z: 1} + m_Children: + - {fileID: 3439814401116981164} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!61 &3439814402987491862 +BoxCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3439814402987491863} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + m_SpriteTilingProperty: + border: {x: 0.049999997, y: 0.049999997, z: 0.049999997, w: 0.049999997} + pivot: {x: 0.5, y: 0.5} + oldSize: {x: 0.16, y: 0.16} + newSize: {x: 0.02, y: 0.02} + adaptiveTilingThreshold: 0.5 + drawMode: 0 + adaptiveTiling: 0 + m_AutoTiling: 0 + serializedVersion: 2 + m_Size: {x: 1, y: 1} + m_EdgeRadius: 0 diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab.meta new file mode 100644 index 0000000..7c5695b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/Ground2DPrototype.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5998d41545749df4f82cfabc16cdd7a0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab new file mode 100644 index 0000000..799624e --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab @@ -0,0 +1,484 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &5091169072216590952 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5091169072216590995} + - component: {fileID: 5091169072216590994} + - component: {fileID: 5091169072216590957} + m_Layer: 0 + m_Name: Z + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5091169072216590995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169072216590952} + m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.1, y: 1, z: 0.10000001} + m_Children: [] + m_Father: {fileID: 5091169074162951104} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &5091169072216590994 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169072216590952} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5091169072216590957 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169072216590952} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 1e79a222d16731a4e96e7360bf14bdfd, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5091169073196929436 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5091169073196929415} + - component: {fileID: 5091169073196929408} + - component: {fileID: 5091169073196929411} + - component: {fileID: 5091169073196929410} + - component: {fileID: 5091169073196929437} + - component: {fileID: -4575890587444351145} + - component: {fileID: -8889901884792729101} + m_Layer: 0 + m_Name: PlayerBallPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5091169073196929415 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.0083206, y: 1.0107286, z: 4.0235205} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5091169074162951104} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &5091169073196929408 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &5091169073196929411 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + AoiMode: 0 + DefaultPropertyGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPosition: {fileID: 0} + Flags: 2305 + NetworkGuid: + RawGuidValue: 6eca58070665ea64797a9b124cb6ab03 + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 5091169073196929437} + - {fileID: -4575890587444351145} + - {fileID: -8889901884792729101} + SimulationBehaviours: [] +--- !u!54 &5091169073196929410 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + serializedVersion: 2 + m_Mass: 1 + m_Drag: 0 + m_AngularDrag: 0.05 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 3 +--- !u!114 &5091169073196929437 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1710889294, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationTarget: {fileID: 5091169074162951104} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 +--- !u!114 &-4575890587444351145 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 00e2a674f00349f4ca3accde09d57809, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + _MovementDirection: {x: 0, y: 0, z: 0} + TransformLocal: 0 + Speed: 6 +--- !u!114 &-8889901884792729101 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073196929436} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 72872188c080b844a9c9d1015cf8e60a, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + TargetPlayerBy: 1 + DrawAreaOfInterestRadius: 0 + Radius: 32 +--- !u!1 &5091169073546120822 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5091169073546120823} + - component: {fileID: 5091169073546120826} + - component: {fileID: 5091169073546120821} + m_Layer: 0 + m_Name: Y + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5091169073546120823 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073546120822} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.1, y: 1, z: 0.1} + m_Children: [] + m_Father: {fileID: 5091169074162951104} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5091169073546120826 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073546120822} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5091169073546120821 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073546120822} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8f9372f58500a3f46ba541ea24e6d105, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5091169073820830794 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5091169073820830795} + - component: {fileID: 5091169073820830798} + - component: {fileID: 5091169073820830793} + m_Layer: 0 + m_Name: X + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5091169073820830795 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073820830794} + m_LocalRotation: {x: -0, y: -0, z: 0.7071068, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.10000001, y: 1, z: 0.1} + m_Children: [] + m_Father: {fileID: 5091169074162951104} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 90} +--- !u!33 &5091169073820830798 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073820830794} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5091169073820830793 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169073820830794} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 42b24d338df372049a18bd257e6bc550, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &5091169074162951107 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5091169074162951104} + - component: {fileID: 5091169074162951110} + - component: {fileID: 5091169074162951105} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5091169074162951104 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169074162951107} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 5091169073820830795} + - {fileID: 5091169073546120823} + - {fileID: 5091169072216590995} + m_Father: {fileID: 5091169073196929415} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5091169074162951110 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169074162951107} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5091169074162951105 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5091169074162951107} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab.meta new file mode 100644 index 0000000..78a9e93 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerBallPrototype.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6eca58070665ea64797a9b124cb6ab03 +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab new file mode 100644 index 0000000..c69ccaf --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab @@ -0,0 +1,496 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1847826552475042298 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 602043736063956190} + - component: {fileID: 3220044756575297883} + - component: {fileID: 1990234854503410183} + m_Layer: 6 + m_Name: Graphic + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &602043736063956190 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8428941732386064851} + - {fileID: 9154110190022252757} + - {fileID: 9117060263996415633} + m_Father: {fileID: 8815895893571785558} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3220044756575297883 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1990234854503410183 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &2079676896626406107 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9117060263996415633} + - component: {fileID: 40104596033558026} + - component: {fileID: 2651066860329146397} + m_Layer: 6 + m_Name: Left + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9117060263996415633 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.4, y: 0.2, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &40104596033558026 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2651066860329146397 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8f9372f58500a3f46ba541ea24e6d105, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3954424087475189028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9154110190022252757} + - component: {fileID: 3359740377460076343} + - component: {fileID: 2938412766755252970} + m_Layer: 6 + m_Name: Right + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9154110190022252757 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.4, y: 0.2, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3359740377460076343 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2938412766755252970 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 42b24d338df372049a18bd257e6bc550, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &8699586581833192621 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8428941732386064851} + - component: {fileID: 9138909502461233102} + - component: {fileID: 8650171744910201692} + m_Layer: 6 + m_Name: Eyes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8428941732386064851 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.25, z: 0.5} + m_LocalScale: {x: 0.8, y: 0.3, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &9138909502461233102 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8650171744910201692 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 5d1b896bc311a1d438c929c45b0c5fbc, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &8815895893571785564 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8815895893571785558} + - component: {fileID: 8815895893571785555} + - component: {fileID: 8815895893571785557} + - component: {fileID: 8358015882023670600} + - component: {fileID: 4065374778257232135} + - component: {fileID: 7291690687864886413} + - component: {fileID: -7655463980351433282} + m_Layer: 6 + m_Name: PlayerPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8815895893571785558 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 602043736063956190} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8815895893571785555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + ObjectInterest: 0 + DefaultInterestGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPositionSource: {fileID: 0} + Flags: 2305 + NetworkGuid: + RawGuidValue: e4df49d0bf125a740a2c14ab6e887572 + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 8815895893571785557} + - {fileID: 4065374778257232135} + - {fileID: -7655463980351433282} + SimulationBehaviours: [] +--- !u!114 &8815895893571785557 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 00e2a674f00349f4ca3accde09d57809, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + _MovementDirection: {x: 0, y: 0, z: 0} + TransformLocal: 0 + Speed: 6 +--- !u!136 &8358015882023670600 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &4065374778257232135 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 72872188c080b844a9c9d1015cf8e60a, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + TargetPlayerBy: 1 + DrawAreaOfInterestRadius: 0 + Radius: 32 +--- !u!143 &7291690687864886413 +CharacterController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Height: 2 + m_Radius: 0.5 + m_SlopeLimit: 45 + m_StepOffset: 0.3 + m_SkinWidth: 0.08 + m_MinMoveDistance: 0.001 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &-7655463980351433282 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 378411a89480da345945c2f888327a2d, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationSpace: 0 + InterpolationTarget: {fileID: 602043736063956190} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 + gravity: -20 + jumpImpulse: 8 + acceleration: 10 + braking: 10 + maxSpeed: 2 + rotationSpeed: 15 + _IsGrounded: 0 + _Velocity: {x: 0, y: 0, z: 0} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab.meta new file mode 100644 index 0000000..2fd7194 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerPrototype.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e4df49d0bf125a740a2c14ab6e887572 +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab new file mode 100644 index 0000000..ba35a6d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab @@ -0,0 +1,424 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6265764632314046015 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2402698497372269455} + m_Layer: 0 + m_Name: Cosmetics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2402698497372269455 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6265764632314046015} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 7408881512794641539} + m_Father: {fileID: 7408881512176793840} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &7408881512176793844 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7408881512176793840} + - component: {fileID: 7408881512176793841} + - component: {fileID: 7408881512176793842} + - component: {fileID: 7408881512176793843} + - component: {fileID: 9002791779461781453} + - component: {fileID: 7984011328971896858} + m_Layer: 0 + m_Name: PlayerRB2DPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7408881512176793840 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.36, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2402698497372269455} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!50 &7408881512176793841 +Rigidbody2D: + serializedVersion: 4 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_BodyType: 0 + m_Simulated: 1 + m_UseFullKinematicContacts: 0 + m_UseAutoMass: 0 + m_Mass: 1 + m_LinearDrag: 0 + m_AngularDrag: 0.05 + m_GravityScale: 0.5 + m_Material: {fileID: 6200000, guid: 46bf9225396fb7546bf2ff74e5a4cf9d, type: 2} + m_Interpolate: 0 + m_SleepingMode: 1 + m_CollisionDetection: 0 + m_Constraints: 0 +--- !u!114 &7408881512176793842 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + AoiMode: 0 + DefaultPropertyGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPosition: {fileID: 0} + Flags: 2305 + NetworkGuid: + RawGuidValue: dbc9b57ea26fbf84b8cc14b0882fe89b + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 7408881512176793843} + - {fileID: 9002791779461781453} + SimulationBehaviours: [] +--- !u!114 &7408881512176793843 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 00e2a674f00349f4ca3accde09d57809, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + _MovementDirection: {x: 0, y: 0, z: 0} + TransformLocal: 0 + Speed: 6 +--- !u!114 &9002791779461781453 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1779252708, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationTarget: {fileID: 2402698497372269455} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 +--- !u!58 &7984011328971896858 +CircleCollider2D: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512176793844} + m_Enabled: 1 + m_Density: 1 + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_UsedByEffector: 0 + m_UsedByComposite: 0 + m_Offset: {x: 0, y: 0} + serializedVersion: 2 + m_Radius: 0.5 +--- !u!1 &7408881512794641540 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7408881512794641539} + - component: {fileID: 7408881512794641538} + m_Layer: 0 + m_Name: Sprite + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7408881512794641539 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512794641540} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 100, y: 100, z: 100} + m_Children: + - {fileID: 2518355560255151043} + - {fileID: 4458487512651460808} + m_Father: {fileID: 2402698497372269455} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &7408881512794641538 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7408881512794641540} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 21300000, guid: 5422498f780e1364b9989ffa3292a010, type: 3} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.2, y: 0.2} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1 &7996721772862284618 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4458487512651460808} + - component: {fileID: 8493290845281769988} + m_Layer: 0 + m_Name: ArrowBottom + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4458487512651460808 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7996721772862284618} + m_LocalRotation: {x: 0, y: 0, z: 0.38268343, w: 0.92387956} + m_LocalPosition: {x: 0.0002, y: -0.0015, z: 0} + m_LocalScale: {x: 0.5, y: 0.1, z: 1} + m_Children: [] + m_Father: {fileID: 7408881512794641539} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 45} +--- !u!212 &8493290845281769988 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7996721772862284618} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 76 + m_Sprite: {fileID: 21300000, guid: 5422498f780e1364b9989ffa3292a010, type: 3} + m_Color: {r: 1, g: 0, b: 0.16484308, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.01, y: 0.01} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !u!1 &8990936507655629799 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2518355560255151043} + - component: {fileID: 5107255085478817496} + m_Layer: 0 + m_Name: ArrowTop + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2518355560255151043 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8990936507655629799} + m_LocalRotation: {x: 0, y: 0, z: -0.38268343, w: 0.92387956} + m_LocalPosition: {x: 0.0002, y: 0.0015, z: 0} + m_LocalScale: {x: 0.5, y: 0.1, z: 1} + m_Children: [] + m_Father: {fileID: 7408881512794641539} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: -45} +--- !u!212 &5107255085478817496 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8990936507655629799} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 76 + m_Sprite: {fileID: 21300000, guid: 5422498f780e1364b9989ffa3292a010, type: 3} + m_Color: {r: 1, g: 0, b: 0.16484308, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.01, y: 0.01} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab.meta new file mode 100644 index 0000000..9309bb5 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerRB2DPrototype.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: dbc9b57ea26fbf84b8cc14b0882fe89b +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab new file mode 100644 index 0000000..6c0ab26 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab @@ -0,0 +1,468 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1847826552475042298 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 602043736063956190} + - component: {fileID: 3220044756575297883} + - component: {fileID: 1990234854503410183} + m_Layer: 6 + m_Name: Graphic + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &602043736063956190 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 8428941732386064851} + - {fileID: 9154110190022252757} + - {fileID: 9117060263996415633} + m_Father: {fileID: 8815895893571785558} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3220044756575297883 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1990234854503410183 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1847826552475042298} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &2079676896626406107 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9117060263996415633} + - component: {fileID: 40104596033558026} + - component: {fileID: 2651066860329146397} + m_Layer: 6 + m_Name: Left + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9117060263996415633 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -0.4, y: 0.2, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &40104596033558026 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2651066860329146397 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2079676896626406107} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 8f9372f58500a3f46ba541ea24e6d105, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3954424087475189028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9154110190022252757} + - component: {fileID: 3359740377460076343} + - component: {fileID: 2938412766755252970} + m_Layer: 6 + m_Name: Right + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &9154110190022252757 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.4, y: 0.2, z: 0} + m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3359740377460076343 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2938412766755252970 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3954424087475189028} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 42b24d338df372049a18bd257e6bc550, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &8699586581833192621 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8428941732386064851} + - component: {fileID: 9138909502461233102} + - component: {fileID: 8650171744910201692} + m_Layer: 6 + m_Name: Eyes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8428941732386064851 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.25, z: 0.5} + m_LocalScale: {x: 0.8, y: 0.3, z: 0.5} + m_Children: [] + m_Father: {fileID: 602043736063956190} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &9138909502461233102 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8650171744910201692 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8699586581833192621} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 1ed66b76f883f11419ff9b35e5f31616, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &8815895893571785564 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8815895893571785558} + - component: {fileID: 8815895893571785555} + - component: {fileID: 8815895893571785557} + - component: {fileID: 8358015882023670600} + - component: {fileID: -3104474011694896616} + - component: {fileID: 4065374778257232135} + m_Layer: 6 + m_Name: PlayerTransformPrototype + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8815895893571785558 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 602043736063956190} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8815895893571785555 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -1552182283, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + AoiMode: 0 + DefaultPropertyGroups: [] + DestroyWhenStateAuthorityLeaves: 0 + AllowStateAuthorityOverride: 0 + AoiPosition: {fileID: 0} + Flags: 2305 + NetworkGuid: + RawGuidValue: 3251faa3ac8a66642bf6f9e8433499da + NestedObjects: [] + NetworkedBehaviours: + - {fileID: 8815895893571785557} + - {fileID: -3104474011694896616} + - {fileID: 4065374778257232135} + SimulationBehaviours: [] +--- !u!114 &8815895893571785557 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 00e2a674f00349f4ca3accde09d57809, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + _MovementDirection: {x: 0, y: 0, z: 0} + TransformLocal: 0 + Speed: 6 +--- !u!136 &8358015882023670600 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &-3104474011694896616 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 158639473, guid: e725a070cec140c4caffb81624c8c787, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + InterpolationTarget: {fileID: 602043736063956190} + InterpolateErrorCorrection: 1 + InterpolatedErrorCorrectionSettings: + MinRate: 3.3 + MaxRate: 10 + PosBlendStart: 0.25 + PosBlendEnd: 1 + PosMinCorrection: 0.025 + PosTeleportDistance: 2 + RotBlendStart: 0.1 + RotBlendEnd: 0.5 + RotTeleportRadians: 1.5 +--- !u!114 &4065374778257232135 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8815895893571785564} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 72872188c080b844a9c9d1015cf8e60a, type: 3} + m_Name: + m_EditorClassIdentifier: + WordOffset: 0 + WordCount: 0 + _interpolationDataSource: 0 + TargetPlayerBy: 1 + DrawAreaOfInterestRadius: 0 + Radius: 32 diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab.meta new file mode 100644 index 0000000..a23158a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Prefabs/PlayerTransformPrototype.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3251faa3ac8a66642bf6f9e8433499da +labels: +- FusionPrefab +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints.meta new file mode 100644 index 0000000..aebbba7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3e11200a15a15b0429f05d392d0ad485 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs new file mode 100644 index 0000000..4bc307d --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs @@ -0,0 +1,12 @@ + +using UnityEngine; +using Fusion; + +/// +/// Interface for behaviours. +/// +public interface ISpawnPointManagerPrototype where T : Component, ISpawnPointPrototype { + void CollectSpawnPoints(NetworkRunner runner); + Transform GetNextSpawnPoint(NetworkRunner runner, PlayerRef player, bool skipIfBlocked = true); +} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs.meta new file mode 100644 index 0000000..6d5d1b8 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointManagerPrototypeT.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 84b534763a46ef44cac3986a7f9f6fd2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs new file mode 100644 index 0000000..a163b2f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs @@ -0,0 +1,9 @@ + +/// +/// Flag interface to identify GameObjects that should be used as markers for spawn points. +/// Used by to locate spawn points in a scene. +/// +public interface ISpawnPointPrototype { + +} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs.meta new file mode 100644 index 0000000..d6a87fc --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/ISpawnPointPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ddbc8f5dae5f9d44a4e09c6d5c26e9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs new file mode 100644 index 0000000..81ce5e5 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs @@ -0,0 +1,5 @@ + + +public class PlayerSpawnPointManagerPrototype : SpawnPointManagerPrototype { + +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs.meta new file mode 100644 index 0000000..35ff629 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointManagerPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b55a6ca2d069a447a0201119c57bc9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs new file mode 100644 index 0000000..fd7b3ab --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using Fusion; + +/// +/// Flag component to identify GameObjects that should be used as markers for spawn points. +/// +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class PlayerSpawnPointPrototype : SimulationBehaviour, ISpawnPointPrototype +{ + //public static List EnabledSpawns = new List(); + + //protected virtual void OnEnable() { + // EnabledSpawns.Add(this); + //} + + //protected virtual void OnDisable() { + // EnabledSpawns.Remove(this); + //} + +#if UNITY_EDITOR + private static bool _spawnPointIsSelected; + + private void OnDrawGizmosSelected() { + // If the selected object contains a spawn point, show gizmos for ALL spawn points. + CheckIfSpawnPointIsSelected(); + } + + private void OnDrawGizmos() { + + // If one spawn point is selected, all spawn points will draw a gizmo. + if (_spawnPointIsSelected) { + + // Check if spawn point has since been deselected. + if (CheckIfSpawnPointIsSelected() == false) { + return; + } + const float FORWD_LEN = 2; + const float OTHER_LEN = 1f; + + var pos = transform.position; + var forward = transform.forward; + var right = transform.right; + var up = transform.up; + + //Gizmos.color = Color.yellow; + //Gizmos.DrawWireSphere(pos, 1); + Gizmos.DrawRay(pos, forward * FORWD_LEN); + Gizmos.DrawRay(pos, right * OTHER_LEN); + Gizmos.DrawRay(pos, up * OTHER_LEN); + Gizmos.color = Color.white; + Gizmos.DrawSphere(pos, .5f); + + Gizmos.color = Color.blue; + Gizmos.DrawSphere(pos + forward * FORWD_LEN, .25f); + Gizmos.color = Color.red; + Gizmos.DrawSphere(pos + right * OTHER_LEN, .25f); + Gizmos.DrawSphere(pos - right * OTHER_LEN, .25f); + Gizmos.color = Color.green; + Gizmos.DrawSphere(pos + up * OTHER_LEN, .25f); + } + } + + private bool CheckIfSpawnPointIsSelected() { + if (Selection.activeGameObject != null) + _spawnPointIsSelected = Selection.activeGameObject.GetComponent(); + else + _spawnPointIsSelected = false; + + return _spawnPointIsSelected; + } + +#endif + +} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs.meta new file mode 100644 index 0000000..b1c1376 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/PlayerSpawnPointPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2830d2ab7c401fc4ca70b1380059844d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs new file mode 100644 index 0000000..5730433 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs @@ -0,0 +1,184 @@ +using UnityEngine; +using Fusion; +using System.Collections.Generic; + +#if UNITY_EDITOR +using UnityEditor; +using Fusion.Editor; +#endif + +/// +/// Derive from this class for different types. +/// Derived manager will only find that spawn point type, allowing for separate handling of player spawn points from other spawn-able items such as AI. +/// +/// +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public abstract class SpawnPointManagerPrototype : Fusion.Behaviour, ISpawnPointManagerPrototype + where T : Component, ISpawnPointPrototype { + public enum SpawnSequence { + PlayerId, + RoundRobin, + Random + } + + /// + /// How spawn points will be selected from the collection. + /// + public SpawnSequence Sequence; + + /// + /// LayerMask for which physics layers should be used for blocked spawn point checks. + /// + public LayerMask BlockingLayers; + + /// + /// The search radius used for detecting if a spawn point is blocked by an object. + /// + public float BlockedCheckRadius = 2f; + + /// + /// Serialized collection of all of the type T found in the same scene as this component. + /// + [System.NonSerialized] + internal List _spawnPoints = new List(); + + [System.NonSerialized] + public int LastSpawnIndex = -1; + + NetworkRNG rng; + + private void Awake() { + rng = new NetworkRNG(0); + } + +#if UNITY_EDITOR + [BehaviourAction] + protected void DrawFoundSpawnPointCount() { + if (Application.isPlaying == false) { + GUILayout.BeginVertical(FusionGUIStyles.GroupBoxType.Info.GetStyle()); + GUILayout.Space(4); + if (GUI.Button(EditorGUILayout.GetControlRect(), "Find Spawn Points")) { + _spawnPoints.Clear(); + var found = UnityEngine.SceneManagement.SceneManager.GetActiveScene().FindObjectsOfTypeInOrder(); + _spawnPoints.AddRange(found); + } + GUILayout.Space(4); + + EditorGUI.BeginDisabledGroup(true); + foreach (var point in _spawnPoints) { + EditorGUILayout.ObjectField(point.name, point, typeof(T), true); + } + EditorGUI.EndDisabledGroup(); + + EditorGUILayout.LabelField($"{typeof(T).Name}(s): {_spawnPoints.Count}"); + GUILayout.EndVertical(); + } + } +#endif + + + /// + /// Find all instances in the same scene as this spawner. + /// This should only be done at development time if using the Photon relay for any spawn logic. + /// + public void CollectSpawnPoints(NetworkRunner runner) { + _spawnPoints.Clear(); + _spawnPoints.AddRange(runner.SimulationUnityScene.FindObjectsOfTypeInOrder()); + } + + /// + /// Select the next spawn point using the defined . Override this method to expand on this, such as detecting if a spawn point is blocked. + /// + public virtual Transform GetNextSpawnPoint(NetworkRunner runner, PlayerRef player, bool skipIfBlocked = true) { + + CollectSpawnPoints(runner); + + int spawnCount = _spawnPoints.Count; + + if (_spawnPoints == null || spawnCount == 0) + return null; + + Component next; + int nextIndex; + if (Sequence == SpawnSequence.PlayerId) { + nextIndex = player % spawnCount; + next = _spawnPoints[nextIndex]; + } + else if (Sequence == SpawnSequence.RoundRobin) { + nextIndex = (LastSpawnIndex + 1) % spawnCount; + next = _spawnPoints[nextIndex]; + } else { + nextIndex = rng.RangeInclusive(0, spawnCount); + next = _spawnPoints[nextIndex]; + } + + // Handling for blocked spawn points. By default this never happens, as the IsBlocked test always returns true. + if (skipIfBlocked && BlockingLayers.value != 0 && IsBlocked(next)) { + var unblocked = GetNextUnblocked(nextIndex); + if (unblocked.Item1 > -1) { + LastSpawnIndex = unblocked.Item1; + return unblocked.Item2.transform; + } + // leave LastSpawnIndex the same since we haven't arrived at a new spawn point. + next = unblocked.Item2; + } else { + LastSpawnIndex = nextIndex; + return next.transform; + } + return AllSpawnPointsBlockedFallback(); + } + + /// + /// Handling for if all spawn points are blocked. + /// + /// + public virtual Transform AllSpawnPointsBlockedFallback() { + return transform; + } + + /// + /// Cycles through all remaining spawn points searching for unblocked. Will return null if all points return == true. + /// + /// The index of the first tried SpawnPoints[] element, which was blocked. + /// ( index, ) + public virtual (int, Component) GetNextUnblocked(int failedIndex) { + for (int i = 1, cnt = _spawnPoints.Count; i < cnt; ++i) { + var sp = _spawnPoints[i % cnt]; + if (!IsBlocked(sp)) + return (i, sp); + } + return (-1, null); + } + + protected static Collider[] blocked3D; + /// + /// Override this method with any logic for checking if a spawn point is blocked. + /// + /// + /// + public virtual bool IsBlocked(Component spawnPoint) { + var physics3d = spawnPoint.gameObject.scene.GetPhysicsScene(); + if (physics3d != null) { + if (blocked3D == null) { + blocked3D = new Collider[1]; + } + var blockedCount = physics3d.OverlapSphere(spawnPoint.transform.position, BlockedCheckRadius, blocked3D, BlockingLayers.value, QueryTriggerInteraction.UseGlobal); + if (blockedCount > 0) + Debug.LogWarning(blocked3D[0].name + " is blocking " + spawnPoint.name); + + return blockedCount > 0; + } + + var physics2d = spawnPoint.gameObject.scene.GetPhysicsScene2D(); + + if (physics2d != null) { + + throw new System.NotImplementedException(); + //return false; + } + + return false; + } + +} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs.meta new file mode 100644 index 0000000..ff2ffc3 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnPointManagerPrototype.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8fca4d5ba0baeb641b706d34ccb857f1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs new file mode 100644 index 0000000..bf34da2 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs @@ -0,0 +1,172 @@ + +using Fusion; +using System.Collections.Generic; +using UnityEngine; + +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class SpawnerPrototype : SimulationBehaviour, IPlayerJoined, IPlayerLeft, ISpawned, ISceneLoadDone where T : Component, ISpawnPointPrototype { + + protected Dictionary> _spawnedLookup = new Dictionary>(); + + public enum SpawnMethods { AutoOnNetworkStart, ByScriptOnly } + public enum AuthorityOptions { Auto, Server, Player } + + public NetworkObject PlayerPrefab; + public SpawnMethods SpawnMethod; + + /// + /// Available if AllowClientObjects is enabled in the NetworkProjectConfig, this allows players to be spawned with client StateAuthority. + /// + [DrawIf(nameof(_AllowClientObjects), true, DrawIfHideType.Hide)] + public AuthorityOptions StateAuthority; + protected bool _AllowClientObjects { + get { + var config = (Runner && Runner.IsRunning) ? Runner.Config : NetworkProjectConfig.Global; + return config.Simulation.Topology == SimulationConfig.Topologies.Shared; + } + } + + protected ISpawnPointManagerPrototype spawnManager; + + protected virtual void Awake() { + spawnManager = GetComponent>(); + } + + public void Spawned() { + + if (SpawnMethod != SpawnMethods.AutoOnNetworkStart) + return; + + // Only spawn in the Scene Loaded timing if we are using NetworkObject for callbacks AND are spawning client state auth. + + if (Object) { + if (_AllowClientObjects && StateAuthority != AuthorityOptions.Server) { + NetworkObject playerNetworkObject = TrySpawn(Runner, Runner.LocalPlayer); + RegisterPlayerAndObject(Runner.LocalPlayer, playerNetworkObject); + } + } + } + + public void SceneLoadDone() { + + if (SpawnMethod != SpawnMethods.AutoOnNetworkStart) + return; + + // Only spawn in the Scene Loaded timing if we are using NetworkRunner for callbacks AND are spawning client state auth. + + if (Object) + return; + + if (!_AllowClientObjects || StateAuthority == AuthorityOptions.Server) + return; + + NetworkObject playerNetworkObject = TrySpawn(Runner, Runner.LocalPlayer); + RegisterPlayerAndObject(Runner.LocalPlayer, playerNetworkObject); + } + + public void PlayerJoined(PlayerRef player) { + PlayerJoined(Runner, player); + } + + public void PlayerLeft(PlayerRef player) { + PlayerLeft(Runner, player); + } + + void PlayerJoined(NetworkRunner runner, PlayerRef player) { + + if (SpawnMethod != SpawnMethods.AutoOnNetworkStart) + return; + + // Only use PlayerJoined callback if spawning with Server Authority + + if (_AllowClientObjects && StateAuthority != AuthorityOptions.Server) { + return; + } + + NetworkObject playerNetworkObject = TrySpawn(runner, player); + RegisterPlayerAndObject(player, playerNetworkObject); + } + + void PlayerLeft(NetworkRunner runner, PlayerRef player) { + DespawnPlayersObjects(runner, player); + UnregisterPlayer(player); + } + + + public NetworkObject TrySpawn(NetworkRunner runner, PlayerRef player) { + + if (PlayerPrefab == false || !player.IsValid) { + return null; + } else { + + // Try to get a spawn point from a spawn manager (if one is attached) - fallback to this components transform as the spawn point. + Transform spawnTransform = (spawnManager != null) ? spawnManager.GetNextSpawnPoint(runner, player) : null; + + if (spawnTransform == null) + spawnTransform = transform; + + Vector3 spawnPosition = spawnTransform.position; + Quaternion spawnRotation = spawnTransform.rotation; + + return runner.Spawn(PlayerPrefab, spawnPosition, spawnRotation, player); + } + } + + [BehaviourButtonAction("Spawn For All Players On Server", true, false)] + public void TrySpawnAll() { + var runners = NetworkRunner.GetInstancesEnumerator(); + while (runners.MoveNext()) { + var runner = runners.Current; + if (runner.IsRunning && runner.IsServer) { + foreach (var p in runner.ActivePlayers) { + var playerNetworkObject = TrySpawn(runner, p); + RegisterPlayerAndObject(p, playerNetworkObject); + } + } + } + } + + protected virtual void RegisterPlayerAndObject(PlayerRef player, NetworkObject playerObject) { + if (!_spawnedLookup.TryGetValue(player, out var objList)) { + objList = new List(); + _spawnedLookup.Add(player, objList); + } + if (playerObject) + objList.Add(playerObject); + + // For AOI handling, make the player aware of its own player object in all cases. + Runner.SetPlayerAlwaysInterested(player, playerObject, true); + } + + protected void DespawnPlayersObjects(NetworkRunner runner, PlayerRef player) { + if (_spawnedLookup.ContainsKey(player)) { + var playerObjects = _spawnedLookup[player]; + if (playerObjects.Count > 0) { + foreach (var obj in playerObjects) + runner.Despawn(obj); + } + + UnregisterPlayer(player); + // TODO: May need to unregister AOI always interested. + } + } + + protected void UnregisterPlayer(PlayerRef player) { + if (_spawnedLookup.ContainsKey(player)) + _spawnedLookup.Remove(player); + } + +#if UNITY_EDITOR + + [BehaviourWarn("No " + nameof(ISpawnPointManagerPrototype) + " found on this GameObject. This GameObject will be used as the spawn point transform.", nameof(_spawnPointManagerMissing))] + protected bool _spawnPointManagerMissing => TryGetComponent>(out var dummy) == false; + +#endif + +} + + + + + + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs.meta new file mode 100644 index 0000000..d106e18 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/SpawnPoints/SpawnerPrototypeT.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8d4d4e6569a4a19419cf341e14ff1adb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs new file mode 100644 index 0000000..14d4f0b --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs @@ -0,0 +1,80 @@ +using UnityEngine; +using Fusion; + +#if UNITY_EDITOR +using Fusion.Editor; +#endif + +[DisallowMultipleComponent] +[AddComponentMenu("Fusion/Prototyping/Toggle Runner Provide Input")] +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class ToggleRunnerProvideInput : Fusion.Behaviour { + + private static ToggleRunnerProvideInput _instance; + + public void Awake() { + + if (NetworkProjectConfig.Global.PeerMode != NetworkProjectConfig.PeerModes.Multiple) { + Debug.LogWarning($"{nameof(ToggleRunnerProvideInput)} only works in Multi-Peer mode. Destroying."); + Destroy(this); + return; + } + + // Enforce singleton across all Runners. + if (_instance) + Destroy(this); + _instance = this; + } + + public void Update() { + if ((Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftCommand)) && Input.GetKey(KeyCode.LeftShift)) { + if (Input.GetKeyDown(KeyCode.Alpha0)) + ToggleAll(-1); + else if (Input.GetKeyDown(KeyCode.Alpha1)) + ToggleAll(0); + else if (Input.GetKeyDown(KeyCode.Alpha2)) + ToggleAll(1); + else if (Input.GetKeyDown(KeyCode.Alpha3)) + ToggleAll(2); + else if (Input.GetKeyDown(KeyCode.Alpha4)) + ToggleAll(3); + else if (Input.GetKeyDown(KeyCode.Alpha5)) + ToggleAll(4); + else if (Input.GetKeyDown(KeyCode.Alpha6)) + ToggleAll(5); + else if (Input.GetKeyDown(KeyCode.Alpha7)) + ToggleAll(6); + else if (Input.GetKeyDown(KeyCode.Alpha8)) + ToggleAll(7); + else if (Input.GetKeyDown(KeyCode.Alpha9)) + ToggleAll(8); + } + } + + private void ToggleAll(int runnerIndex) { + + var runners = NetworkRunner.GetInstancesEnumerator(); + + int index = 0; + while (runners.MoveNext()) { + + var runner = runners.Current; + + // Ignore inactive runners - might just be unused scene objects or other orphans. + if (runner == null || !runner.IsRunning) + continue; + + bool enable = runnerIndex == -1 || index == runnerIndex; + runner.ProvideInput = enable; + index++; + } +#if UNITY_EDITOR + // If we have a RunnerVisiblityControlWindow open, it needs to know to refresh. + if (RunnerVisibilityControlsWindow.Instance) { + RunnerVisibilityControlsWindow.Instance.Repaint(); + } +#endif + } +} + + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs.meta new file mode 100644 index 0000000..0b86a4a --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerProvideInput.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cd29630174e32b4b996cd191ae2fdb7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs new file mode 100644 index 0000000..09afc32 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using UnityEngine; +using Fusion; + +#if UNITY_EDITOR +using Fusion.Editor; +#endif + +[DisallowMultipleComponent] +[AddComponentMenu("Fusion/Prototyping/Toggle Runner Visibility")] +[ScriptHelp(BackColor = EditorHeaderBackColor.Steel)] +public class ToggleRunnerVisibility : Fusion.Behaviour { + + private static ToggleRunnerVisibility _instance; + + public void Awake() { + + if (NetworkProjectConfig.Global.PeerMode != NetworkProjectConfig.PeerModes.Multiple) { + Debug.LogWarning($"{nameof(ToggleRunnerVisibility)} only works in Multi-Peer mode. Destroying."); + Destroy(this); + return; + } + + // Enforce singleton across all Runners. + if (_instance) { + Destroy(this); + + } + _instance = this; + } + + public void Update() { + + // Ignoring shift key if control/command is down + if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.LeftCommand)) + return; + + if ((Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) { + + if (Input.GetKeyDown(KeyCode.Alpha0)) { + ToggleAll(-1); + } else if (Input.GetKeyDown(KeyCode.Alpha1)) { + ToggleAll(0); + } else if (Input.GetKeyDown(KeyCode.Alpha2)) { + ToggleAll(1); + } else if (Input.GetKeyDown(KeyCode.Alpha3)) { + ToggleAll(2); + } else if (Input.GetKeyDown(KeyCode.Alpha4)) { + ToggleAll(3); + } else if (Input.GetKeyDown(KeyCode.Alpha5)) { + ToggleAll(4); + } else if (Input.GetKeyDown(KeyCode.Alpha6)) { + ToggleAll(5); + } else if (Input.GetKeyDown(KeyCode.Alpha7)) { + ToggleAll(6); + } else if (Input.GetKeyDown(KeyCode.Alpha8)) { + ToggleAll(7); + } else if (Input.GetKeyDown(KeyCode.Alpha9)) { + ToggleAll(8); + } + } + } + + private void ToggleAll(int runnerIndex) { + + var runners = NetworkRunner.GetInstancesEnumerator(); + + int index = 0; + while (runners.MoveNext()) { + + var runner = runners.Current; + + // Ignore inactive runners - might just be unused scene objects or other orphans. + if (runner == null || !runner.IsRunning) + continue; + + bool enable = runnerIndex == -1 || index == runnerIndex; + runner.IsVisible = enable; + index++; + } +#if UNITY_EDITOR + // If we have a RunnerVisiblityControlWindow open, it needs to know to refresh. + if (RunnerVisibilityControlsWindow.Instance) { + RunnerVisibilityControlsWindow.Instance.Repaint(); + } +#endif + } +} diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs.meta new file mode 100644 index 0000000..1d37cec --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/ToggleRunnerVisibility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a61036aa5509aec4ea631d4649f2bd0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Utilities.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities.meta new file mode 100644 index 0000000..3aa1d46 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c6a6fb3cca8287f4480efb9c991aa9fe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs new file mode 100644 index 0000000..f4fdc67 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs @@ -0,0 +1,28 @@ +#if UNITY_EDITOR + +using UnityEditor; + +namespace FusionPrototypingInternal { + internal static class FusionPrototypingAssetUtils { + public static T GetAsset(this string Guid, ref T backing) where T : UnityEngine.Object { + if (backing != null) + return backing; + + var path = AssetDatabase.GUIDToAssetPath(Guid); + if (path != null && path != "") + backing = AssetDatabase.LoadAssetAtPath(path); + return GetAsset(Guid); + } + + public static T GetAsset(this string Guid) where T : UnityEngine.Object { + var path = AssetDatabase.GUIDToAssetPath(Guid); + if (string.IsNullOrEmpty( path)) { + return null; + } else { + return AssetDatabase.LoadAssetAtPath(path); + } + } + } +} + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs.meta new file mode 100644 index 0000000..0ccaf40 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionPrototypingAssetUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 936c608520545b14898641186e97fc47 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs new file mode 100644 index 0000000..287deb7 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs @@ -0,0 +1,84 @@ +using System.Reflection; +using UnityEngine; + +/// +/// In-Game IMGUI style used for the interface. +/// +public static class FusionScalableIMGUI +{ + private static GUISkin _scalableSkin; + + private static void InitializedGUIStyles(GUISkin baseSkin) { + _scalableSkin = baseSkin == null ? GUI.skin : baseSkin; + + // If no skin was provided, make the built in GuiSkin more tolerable. + if (baseSkin == null) { + _scalableSkin = GUI.skin; + _scalableSkin.button.alignment = TextAnchor.MiddleCenter; + _scalableSkin.label.alignment = TextAnchor.MiddleCenter; + _scalableSkin.textField.alignment = TextAnchor.MiddleCenter; + + _scalableSkin.button.normal.background = _scalableSkin.box.normal.background; + _scalableSkin.button.hover.background = _scalableSkin.window.normal.background; + + _scalableSkin.button.normal.textColor = new Color(.8f, .8f, .8f); + _scalableSkin.button.hover.textColor = new Color(1f, 1f, 1f); + _scalableSkin.button.active.textColor = new Color(1f, 1f, 1f); + _scalableSkin.button.border = new RectOffset(6, 6, 6, 6); + _scalableSkin.window.border = new RectOffset(8, 8, 8, 10); + } else { + // Use the supplied skin as the base. + _scalableSkin = baseSkin; + } + } + + /// + /// Get the custom scalable skin, already resized to the current screen. Provides the height, width, padding and margin used. + /// + /// + public static GUISkin GetScaledSkin(GUISkin baseSkin, out float height, out float width, out int padding, out int margin, out float boxLeft) { + + if(_scalableSkin == null) { + InitializedGUIStyles(baseSkin); + } + + var dimensions = ScaleGuiSkinToScreenHeight(); + height = dimensions.Item1; + width = dimensions.Item2; + padding = dimensions.Item3; + margin = dimensions.Item4; + boxLeft = dimensions.Item5; + return _scalableSkin; + } + + /// + /// Modifies a skin to make it scale with screen height. + /// + /// + /// Returns (height, width, padding, top-margin, left-box-margin) values applied to the GuiSkin + public static (float, float, int, int, float) ScaleGuiSkinToScreenHeight() { + + bool isVerticalAspect = Screen.height > Screen.width; + bool isSuperThin = Screen.height / Screen.width > (17f / 9f); + + float height = Screen.height * .08f; + float width = System.Math.Min(Screen.width * .9f, Screen.height * .6f); + int padding = (int)(height / 4); + int margin = (int)(height / 8); + float boxLeft = (Screen.width - width) * .5f; + + int fontsize = (int)(isSuperThin ? (width - (padding * 2)) * .07f : height * .4f); + var margins = new RectOffset(0, 0, margin, margin); + + _scalableSkin.button.fontSize = fontsize; + _scalableSkin.button.margin = margins; + _scalableSkin.label.fontSize = fontsize; + _scalableSkin.label.padding = new RectOffset(padding, padding, padding, padding); + _scalableSkin.textField.fontSize = fontsize; + _scalableSkin.window.padding = new RectOffset(padding, padding, padding, padding); + _scalableSkin.window.margin = new RectOffset(margin, margin, margin, margin); + + return (height, width, padding, margin, boxLeft); + } +} + diff --git a/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs.meta b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs.meta new file mode 100644 index 0000000..bac3769 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Prototyping/Utilities/FusionScalableIMGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4cdd3463ece0019488353b76646a884f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Utilities.meta b/Assets/Photon/Fusion/Scripts/Utilities.meta new file mode 100644 index 0000000..cbf314f --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 32b9a37aa61950f4e93800705feb8d62 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs b/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs new file mode 100644 index 0000000..9976c31 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs @@ -0,0 +1,181 @@ +// --------------------------------------------------------------------------------------------- +// PhotonNetwork Framework for Unity - Copyright (C) 2020 Exit Games GmbH +// developer@exitgames.com +// --------------------------------------------------------------------------------------------- + +using System.Collections.Generic; +using UnityEngine; + +namespace Fusion.Editor { + public static class BoundsTools { + public enum BoundsType { Both, MeshRenderer, Collider, Manual } + + // 3d + private static readonly List meshFilters = new List(); + private static readonly List meshRenderers = new List(); + private static readonly List colliders = new List(); + private static readonly List validColliders = new List(); + + // 2d + private static readonly List spriteRenderers = new List(); +#if !DISABLE_PHYSICS_2D + private static readonly List colliders2D = new List(); + private static readonly List validColliders2D = new List(); +#endif + /// + /// Collect the bounds of the indicated types (MeshRenderer and/or Collider) on the object and all of its children, and returns bounds that are a sum of all of those. + /// + /// GameObject to start search from. + /// The types of bounds to factor in. + /// Whether to search all children for bounds. + /// + public static Bounds CollectMyBounds(this GameObject go, BoundsType factorIn, out int numOfBoundsFound, bool includeChildren = true, bool includeInactive = false) { + // if we are ignoring inactive, an inactive parent is already a null. Quit here. + if (!go.activeInHierarchy && !!includeInactive) { + numOfBoundsFound = 0; + return new Bounds(); + } + + bool bothtype = factorIn == BoundsType.Both; + bool rendtype = bothtype || factorIn == BoundsType.MeshRenderer; + bool colltype = bothtype || factorIn == BoundsType.Collider; + + // Clear the reusables so they have counts of zero + meshFilters.Clear(); + meshRenderers.Clear(); + colliders.Clear(); + spriteRenderers.Clear(); + validColliders.Clear(); +#if !DISABLE_PHYSICS_2D + validColliders2D.Clear(); +#endif + int myBoundsCount = 0; + + // Find all of the MeshRenderers and Colliders (as specified) + if (rendtype) { + if (go.activeInHierarchy) { + if (includeChildren) { + go.GetComponentsInChildren(includeInactive, meshRenderers); + go.GetComponentsInChildren(includeInactive, meshFilters); + go.GetComponentsInChildren(includeInactive, spriteRenderers); + } else { + go.GetComponents(meshRenderers); + go.GetComponents(meshFilters); + go.GetComponents(spriteRenderers); + } + } + } + + if (colltype) { + if (go.activeInHierarchy) { + if (includeChildren) { + go.GetComponentsInChildren(includeInactive, colliders); +#if !DISABLE_PHYSICS_2D + go.GetComponentsInChildren(includeInactive, colliders2D); +#endif + + } else { + go.GetComponents(colliders); +#if !DISABLE_PHYSICS_2D + go.GetComponents(colliders2D); +#endif + } + } + } + + // Add any MeshRenderer attached to the found MeshFilters to their own list. + // We want the MeshRenderer for its bounds, but only if there is a MeshFilter, otherwise there is a risk of a 0,0,0 + for (int i = 0; i < meshFilters.Count; i++) { + Renderer mr = meshFilters[i].GetComponent(); + + if (mr && (mr.enabled || includeInactive)) { + if (!meshRenderers.Contains(mr)) + meshRenderers.Add(mr); + } + } + + // Collect only the valid colliders (ignore inactive if not includeInactive) + for (int i = 0; i < colliders.Count; i++) { + if (colliders[i].enabled || includeInactive) + if (colliders[i]) + validColliders.Add(colliders[i]); + } + +#if !DISABLE_PHYSICS_2D + + // Collect only the valid colliders (ignore inactive if not includeInactive) + for (int i = 0; i < colliders2D.Count; i++) { + if (colliders2D[i] && colliders2D[i].enabled || includeInactive) + // 2d colliders arrive as null but present in scene changes, test for null + if (colliders2D[i]) + validColliders2D.Add(colliders2D[i]); + } +#endif + // Make sure we found some bounds objects, or we need to quit. + numOfBoundsFound = + meshRenderers.Count + + spriteRenderers.Count + + validColliders.Count +#if !DISABLE_PHYSICS_2D + + validColliders2D.Count +#endif + ; + // No values means no bounds will be found, and this will break things if we try to use it. + if (numOfBoundsFound == 0) { + return new Bounds(); + } + + // Get a starting bounds. We need this because the default of centered 0,0,0 will break things if the map is + // offset and doesn't encapsulate the world origin. + Bounds compositeBounds; + + if (meshRenderers.Count > 0) + compositeBounds = meshRenderers[0].bounds; + + else if (validColliders.Count > 0) + compositeBounds = validColliders[0].bounds; + +#if !DISABLE_PHYSICS_2D + else if (validColliders2D.Count > 0 && validColliders2D[0]) + compositeBounds = validColliders2D[0].bounds; +#endif + else if (spriteRenderers.Count > 0) + compositeBounds = spriteRenderers[0].bounds; + /// nothing found, return an empty bounds + else + return new Bounds(); + + for (int i = 0; i < spriteRenderers.Count; i++) { + myBoundsCount++; + compositeBounds.Encapsulate(spriteRenderers[i].bounds); + } + + // Encapsulate all outer found bounds into that. We will be adding the root to itself, but no biggy, this only runs once. + for (int i = 0; i < meshRenderers.Count; i++) { + myBoundsCount++; + compositeBounds.Encapsulate(meshRenderers[i].bounds); + } + + for (int i = 0; i < validColliders.Count; i++) { + myBoundsCount++; + compositeBounds.Encapsulate(validColliders[i].bounds); + } +#if !DISABLE_PHYSICS_2D + for (int i = 0; i < validColliders2D.Count; i++) { + myBoundsCount++; + if (validColliders2D[i]) + compositeBounds.Encapsulate(validColliders2D[i].bounds); + } +#endif + return compositeBounds; + + } + + public static Bounds CollectMyBounds(GameObject go, BoundsType factorIn, bool includeChildren = true) { + int dummy; + return CollectMyBounds(go, factorIn, out dummy, includeChildren); + } + + } +} + diff --git a/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs.meta b/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs.meta new file mode 100644 index 0000000..4774f42 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/BoundsTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b1f69b1731eb06e40bd0078cee10f83b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs b/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs new file mode 100644 index 0000000..41eebaa --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs @@ -0,0 +1,46 @@ +#if UNITY_EDITOR + +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace Fusion.Editor { + + // Editor scripts that are kept outside of the Editor folder, so they are accessible to MonoBehaviours inside of UNITY_EDITOR sections. + public static class FusionEditorUtilitiesExternal { + + public static void AddSceneToBuildSettings(this Scene scene) { + var buildScenes = EditorBuildSettings.scenes; + bool isInBuildScenes = false; + foreach (var bs in buildScenes) { + if (bs.path == scene.path) { + isInBuildScenes = true; + break; + } + } + if (isInBuildScenes == false) { + var buildList = new List(); + buildList.Add(new EditorBuildSettingsScene(scene.path, true)); + buildList.AddRange(buildScenes); + Debug.Log($"Added '{scene.path}' as first entry in Build Settings."); + EditorBuildSettings.scenes = buildList.ToArray(); + } + } + + public static bool TryGetSceneIndexInBuildSettings(this Scene scene, out int index) { + var buildScenes = EditorBuildSettings.scenes; + for (int i = 0; i < buildScenes.Length; ++i) { + var bs = buildScenes[i]; + if (bs.path == scene.path) { + index = i; + return true; + } + } + index = -1; + return false; + } + } +} + +#endif diff --git a/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs.meta b/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs.meta new file mode 100644 index 0000000..35fa6bb --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/FusionEditorUtilitiesExternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 831d6d32a7ec4fc4f928bf0881f2f0dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs b/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs new file mode 100644 index 0000000..76e1413 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs @@ -0,0 +1 @@ +// Deleted July 2 2021 - merged into NetworkProjectConfigUtilities.cs diff --git a/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs.meta b/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs.meta new file mode 100644 index 0000000..7da1a07 --- /dev/null +++ b/Assets/Photon/Fusion/Scripts/Utilities/NetworkProjectUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 11a31e18243dc3b4cb9740ab78a7f733 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/build_info.txt b/Assets/Photon/Fusion/build_info.txt new file mode 100644 index 0000000..2e1af2a --- /dev/null +++ b/Assets/Photon/Fusion/build_info.txt @@ -0,0 +1,3 @@ +build: 1.0.0 F 439 +date: 2022-03-17 08:29:38 +git: main (9cf0981d) \ No newline at end of file diff --git a/Assets/Photon/Fusion/build_info.txt.meta b/Assets/Photon/Fusion/build_info.txt.meta new file mode 100644 index 0000000..287581e --- /dev/null +++ b/Assets/Photon/Fusion/build_info.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7d903aaee8321384991b6195565dae93 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/Fusion/release_history.txt b/Assets/Photon/Fusion/release_history.txt new file mode 100644 index 0000000..071dde3 --- /dev/null +++ b/Assets/Photon/Fusion/release_history.txt @@ -0,0 +1,2192 @@ +# 1.0.0 + +## F + +### Build 439 (Mar 17, 2022) + +- Official release + +# 0.13.0 + +## RC Nightly + +### Build 438 (Mar 16, 2022) + +**Improvements** + +- `NetworkTransform` Teleport API now has the option to interpolate forward (based on From state) or backwards (default, based on To state) + +**Changes** + +- Transform behaviours (NT/NRB/NCCP) now have a virtual implementation of the Unity `OnEnable` callback +- `NetworkObject.Runner` gets set for predicted spawns + +### Build 437 (Mar 15, 2022) + +**What's New** + +- `[Fusion.NetworkAssemblyIgnore]` assembly level attribute; if added to an assembly, Fusion will not scan it for networked types + +**Changes** + +- A warning is emit if a `INetworkStruct` uses `string` property with backing field + +**Bug Fixes** + +- Fixed: `NetworkRunner.IsShutdown` being false when exiting play mode / handling `OnApplicationQuit`. +Test: More tests. Added `CreateSimplePrefab` and `CreateRunner` +- Fixed: NRB now normalizes the networked quaternion before setting it to the `Rigidbody.rotation` field, avoiding issues with values considered as not having unit length +- Fixed: Codegen: `NetworkString<>` raising an assertion if used in ref and pointer properties +- Fixed: Issue with FixedUpdateNetwork being called while InSimulation was false + +### Build 436 (Mar 14, 2022) + +**Breaking Changes** + +- `NetworkString` lengths are now narrowed down to: 2, 4, 8, 16, 32, 64, 128, 256, 512 + +**Changes** + +- SimulationCount in FusionStats has been broken into two stats now, ForwardSimCount and ResimCount. This altered the enum, so some existing FusionStats implementations may need to be updated + +**Bug Fixes** + +- Fixed: `NetworkObject` and `NetworkRunner` throwing `NullReferenceExceptions` when some of their properties are accessed with no simulation is running + +### Build 435 (Mar 11, 2022) + +**What's New** + +- Get(), Set(), and IndexOf() methods added to NetworkLinkedList + +**Bug Fixes** + +- Fixed: Shared non-master clients not attaching to a deactivated scene objects +- Fixed: `NetworkRunner.EnsureRunnerSceneIsActive` not changing active scene in multiple peer mode +- Fixed: Some issues relating to handing over state authority in shared mode + +### Build 434 (Mar 10, 2022) + +**What's New** + +- Host Migration Support + +**Changes** + +- Enable connect attempt to Symmetric NATs + +### Build 433 (Mar 09, 2022) + +**Bug Fixes** + +- Fixed: Realtime Region Ping method +- Fixed: Issue in the first interpolated rendering of a predict-spawned object with `NetworkTransform` + +### Build 432 (Mar 08, 2022) + +**What's New** + +- Added NetworkBehaviour.CopyStateFrom +- Added NetworkObject.CopyStateFrom + +**Bug Fixes** + +- Fixed: Issue with out of bounds write on very large objects in EC mode + +### Build 431 (Mar 07, 2022) + +**What's New** + +- NanoSockets for AppleSilicon M1 +- Base Switch NanoSockets project +- PS4 NanoSockets +- Link to PS4 static libs +- Basic PS4 NanoSockets project +- `Fusion.TextWriterLogger` + +**Changes** + +- Move Xbox NanoSockets +- Switch NanoSockets Project +- NanoSockets source for Switch Build +- Setup NanoSockets for Switch +- Ignore NanoSockets Switch Builds +- Rename PS4 NanoSockets output file +- PS4 NanoSockets Cleanup +- PS4 NanoSockets build StaticLibrary +- PS4 Macros cleanup +- `Fusion.ConsoleLogger` made public + +**Removed** + +- PS4 NanoSocket Unused static lib refs + +**Bug Fixes** + +- Fixed: Path of SwitchMacros.h +- Fixed: Insertion and ordering of non-networkobject attached simulation behaviours + +### Build 430 (Mar 04, 2022) + +**Breaking Changes** + +- RPC local invokes for host (in Host mode) have `RpcInfo.Source` set to `PlayerRef.None` (used to be: Host's local `PlayerRef`). If you want the old behavior back RPC, use `[Rpc(HostMode = RpcHostMode.SourceIsHostPlayer)]`. Note that in such case clients will also see host's player in `RpcInfo.Source` + +**What's New** + +- Unity-side logger implementation - FusionUnityLogger + +**Changes** + +- Deprecated Log.Init(Action, Action, Action, Action) + +**Bug Fixes** + +- Fixed: AssertExceptions being thrown when quitting application with scene `NetworkObjects` that never have been activated +- Fixed: AssertExceptions being thrown when exiting play mode with scene `NetworkObjects` that never have been activated + +### Build 429 (Mar 03, 2022) + +**What's New** + +- `NetworkCharacterControllerPrototype`, a prototype behaviour extending the base `NetworkTransform` that uses Unity's Character Controller +- `Log.Init` parameters: `useColorTags` and `usePrefix` +- [ScriptHelp(BackColor = EditorHeaderBackColor.None)] attribute can be added to custom Fusion scripts do disable the inspector Fusion header graphics, and revert to the normal Script field + +**Changes** + +- `RpcInfo.Source` value for host is now controlled with `Rpc.HostMode`. When the host calls RPC, `[Rpc(HostMode = RpcHostMode.SourceIsServer)]` results with `RpcInfo.Source` being none and `[Rpc(HostMode = RpcHostMode.SourceIsHostPlayer)]` results with `RpcInfo.Source` being equal to the host's local player + +**Bug Fixes** + +- Fixed: `RpcInfo.Source` inconsistency for when RPC is invoked by the host. Was: local player for local invoke, none for remote clients. Is: none for local invoke and remote clients by default, can be changed with `Rpc.HostMode` +- Fixed: Issue with AllowStateAuthorityOverride not working always +- Fixed: Objects not receiving callbacks when reused from a pool + +### Build 428 (Mar 02, 2022) + +**Bug Fixes** + +- Fixed: `NetworkArray` property with `[Accuracy(0)]` causing Il2cpp error + +### Build 427 (Mar 01, 2022) + +**What's New** + +- `NetworkTransform` now implements `IPredictedSpawnBehaviour` and provides built-in support to interpolated rendering and prediction error correction. + Behaviours that change the Transform state of the predict-spawned object should either be ordered before NT or call `PredictedSpawnCacheTransformState` after doing so + +### Build 426 (Feb 28, 2022) + +**Changes** + +- Objects are despawned when a runner is shutdown + +### Build 424 (Feb 25, 2022) + +**What's New** + +- `SimulationBehaviour.CanReceiveCallback` + +**Improvements** + +- `NetworkObject`: checks if the object has been properly despawned (in Debug builds) + +**Changes** + +- `NetworkObject` despawn is now instantaneous (was: deferred until the end of a tick) +- `NetworkObject` can be destroyed safely with `UnityEngine.Destroy` and don't need a despawn when scene changes +- `NetworkObject`s are despawned when exiting play mode +- `NetworkRunner.GetInterfaceListHead(Type, int)` made obsolete, use `NetworkRunner.GetInterfaceListHead(Type, int, out SimulationBehaviour)` instead + +**Removed** + +- `Behaviour.IsEnabled` + +**Bug Fixes** + +- Fixed: NanoSockets for Intel Macs +- Fixed: Issues with packet corruption when client sending too large inputs or server sending to much state in eventual consistency mode + +### Build 423 (Feb 24, 2022) + +**What's New** + +- Support DisableSessionCreation in SharedMode +- Reworked transform behaviours: `NetworkTransform`, `NetworkRigidbody` and `NetworkCharacterController`. + This build brings an important rework to all core transform behaviours. Here is a summary of why we needed those modifications, what to expect and the most important changes. + + **Why:** + - The previously available behaviours (NT/NRB/NCC) aimed to solve a very broad range of problems and scenarios, which led to a complex and hard to maintain codebase that sometimes misbehaved on core use-cases. + - While possible, user extension of these default functionalities and options provided by those behaviours was not easy. This prevented users from customizing them to their needs, often relying on modifications of the core implementation. + - NCC was unstable even when steering over simple scene colliders. The release of the new advanced KCC package further reduced its value even as a prototyping tool. + + **What to expect:** + - New behaviours are easier to configure and are expected to be more stable in most common use-cases. + - They are also easier to extend and adjust for game specific needs. A new prototype component exemplifies exactly that (more below). + + **Important changes:** + - All previously existing core transform behaviours are marked as obsolete and renamed, but were kept in the core libraries. Although their usage is no longer encouraged and provided support for them will be limited, they are still available if you want to upgrade but need more time to switch to the new behaviours. + - All new behaviours have the same name as the previous ones (e.g. `NetworkTransform`), except for `NetworkCharacterController` and `NetworkTransformAnchor`, which don't have a counterpart on new behaviours. To make this possible, old behaviours were renamed and have Obsolete added at the end of the class name: e.g. old `NetworkRigidbody` will be available as `NetworkRigidbodyObsolete` (the class is also marked as obsolete). Keeping the same name on the new behaviours also means that your existing prefabs and scene objects will automatically upgrade to use the new ones. + - There is no longer a core NCC behaviour (existing one is kept, but marked as obsolete). Instead, a new `NetworkCharacterControllerPrototype` is provided in source code on the SDK: it uses Unity's Character Controller and exemplifies how the base `NetworkTransform` behaviour can be extended. With this new prototype behaviour we aim to provide a simple, yet stable prototyping tool, while the KCC package remains the recommended one for production and more complex use-cases. + - Transform data is now always synchronized in world space. This is important to ensure Area of Interest (AOI) management works as expected. However, a configurable Interpolation Space will be available for all transform behaviours, so that the view (interpolation target) can be interpolated as if the networked data is in local space, allowing nested transforms to visually behave as expected. Interpolated prediction error correction is still always performed in world space and more complex hierarchies might require thoughtful control of the interpolation source, space and targets' hierarchies. + - There is no synchronization of neither scale nor parenting/hierarchy on the transform behaviours. If you need to synchronize these types of data/relations, the suggested approach is to extend the core behaviours, synchronizing scale in local space and controlling a decoupled hierarchy of the interpolation targets. + - Performing non-interpolated teleports no longer requires pre-steps. Optional interpolation velocities can be provided in order to smooth the transition. Check the new teleport API documentation for more details + +**Improvements** + +- Networked transform behaviours now have prediction error computed based on both From and To states used for rendering, instead of only on the latter + +**Bug Fixes** + +- Fixed: Issue with area of interest calculations when no compression was used for position +- Fixed: Detection of rotation prediction error on NetworkTransform and derived behaviours + +### Build 419 (Feb 22, 2022) + +**What's New** + +- `NetworkProjectConfig.NullChecksForNetworkedProperties` + +**Changes** + +- RpcInvokeInfo reworked +- Weaver: Rpcs always check if `NetworkObject` has been attached to (used to be: only with debug DLLs) +- Weaver: `[Networked]` properties check if `NetworkObject` has been attached to if `NetworkProjectConfig.NullChecksForNetworkedProperties` is set (used to be: only with debug DLLs) + +### Build 416 (Feb 18, 2022) + +**Changes** + +- `PredictedSpawnSuccess` is now delayed until activation for deactivated objects + +**Bug Fixes** + +- Fixed: Restart Background Connection handler + +### Build 415 (Feb 17, 2022) + +**Changes** + +- PredictiveSpawnTest updated for others to test with + +### Build 414 (Feb 16, 2022) + +**What's New** + +- `protected virtual NetworkObject.Awake()` +- `NetworkObject.DefaultExecutionOrder` + +**Changes** + +- `NetworkObject.OnDestroy` from protected to protected virtual +- `NetworkObject` script execution order to 500. `NetworkBehaviours` should have lower execution order to ensure consistent behaviour when activating initially deactivated `NetworkObjects` + +**Removed** + +- [ExecuteInEditMode] removed from NetworkObject - no longer needed + +**Bug Fixes** + +- Fixed: Issue with unity infinite loop/freeze when adding an simulation behaviour that was already in the update loop +- Fixed: Issue with AOI when Position accuracy compression is set to not compress +- Fixed: `Spawned` being called before `Awake` for initially deactivated objects. This only works if `NetworkBehaviour` subclasses have execution order (0 by default) lower than `NetworkObject` (500 by default). If this is not the case, a warning will be emit while baking scene/prefab with disabled objects containing such scripts +- Fixed: Issue with large initial delta snapshots causing crash + +### Build 413 (Feb 15, 2022) + +**Bug Fixes** + +- Fixed: Prefab import: Making prefab addressable no longer requires a manual reimport of `NetworkProjectConfig` +- Fixed: Prefab import: Prefabs in addressable folders causing an `InvalidOperationException` during `NetworkProjectConfig` import + +### Build 412 (Feb 14, 2022) + +**What's New** + +- `NetworkString<>` - an unmanaged and alloc-free alternative to `string` type. `NetworkString<>` holds its data as UTF32 string and the internal capacity is controlled with a generic parameter, e.g. `NetworkString<_16>` will hold up to 16 Unicode code points. Available capacities: 1 to 32, 64, 128 and 256 + +### Build 411 (Feb 11, 2022) + +**Bug Fixes** + +- Fixed: Massively reduced static memory pressure on server with a lot of players connected +- Fixed: Issue with calling SetInterestGroup something causing a null-ref exception if called in the wrong place + +### Build 410 (Feb 10, 2022) + +**Changes** + +- ILWeaver: error reported if a `[Networked]` property is static + +### Build 408 (Feb 08, 2022) + +**Bug Fixes** + +- Fixed: Force Max Allowed MTU + +### Build 406 (Feb 07, 2022) + +**Bug Fixes** + +- Fixed: Shutdown in case of Photon Cloud Timeout +- Fixed: Issue with FixedUpdate sometimes being called the same tick as a behaviour was spawned depending on its execution order +- Fixed: Issue with inconsistent behaviour call order between server/client, call order is now deterministic on all machines and based on network object id +- Fixed: CustomCallbackInterfaces in SinglePlayer Mode +- Fixed: Spawn API to accept a `PredictionKey` + +### Build 405 (Feb 04, 2022) + +**Bug Fixes** + +- Fixed: Cached Transform in NetworkTransform classes checks for null, now that Fusion may access NBs which are disabled at startup (Awake has not run) +- Fixed: Predicted Spawns will no longer fire PredictedSpawnUpdate on the same tick as PredictedSpawnSpawned +- Fixed: Error if switch Shared and ClientServer modes + +### Build 404 (Feb 03, 2022) + +**Bug Fixes** + +- Fixed: Assert logged when baking a `NetworkObject` without any `NetworkBehaviours` + +### Build 403 (Feb 02, 2022) + +**Changes** + +- Extended Server ping to local IPs, instead of only public IPs + +**Bug Fixes** + +- Fixed: DisableNATPunchthrough flag +- Fixed: MTU Default Size + +### Build 402 (Feb 01, 2022) + +**Bug Fixes** + +- Fixed: `SimulationBehaviour` static RPCs throwing `ArgumentOutOfRangeException` + +### Build 401 (Jan 31, 2022) + +**Changes** + +- Disabled/deactivated `NetworkObjects`, `NetworkBehaviours` and `SimulationBehaviours` are now picked up by the bake process; they are marked with `ActivatedByUser` flag and are not enabled/activated by Fusion when attaching a state object +- `NetworkBehaviours` on deactivated GameObjects no longer have their callbacks invoked +- `NetworkObjects` that are initially deactivated are no longer activated by Fusion. However, `NetworkBehaviours` on such objects still have `CopyBackingFieldsToState` (for server) and `Spawned` invoked +- `Fusion.Behaviour.IsEnabled` now returns `MonoBehaviour.isActiveOrEnabled` (was: `MonoBehaviour.enabled`) + +### Build 399 (Jan 28, 2022) + +**Bug Fixes** + +- Fixed: RunnerVisibilityNodes Find buttons in Inspector now dirty the scene/object +- Fixed: `NetworkObject` prefab showing outdated values and a rebake button (when opened) + +### Build 398 (Jan 27, 2022) + +**What's New** + +- FusionStats.NoGraphShader option added, for special VR cases where the shader will not render correctly in 3d space + +### Build 397 (Jan 26, 2022) + +**Bug Fixes** + +- Fixed: Overriding `NetworkBehaviour.CopyStateToBackingFields` and `NetworkBehaviour.CopyBackingFieldsToState`: `NetworkBehaviour.InvokeWeavedCode` needs to be called in overrides to instruct the weaver where to add instructions +- Fixed: SerializableDictionary<,> throwing ArgumentNullException if a serialized key happens to be a null +- Fixed: Unity 2022.1 compile error with `PrefabStageUtility.GetPrefabStage` + +### Build 396 (Jan 25, 2022) + +**Breaking Changes** + +- Session Custom Properties are now of type `Dictionary` + +**What's New** + +- RunnerVisibilityControlsWindow now shows PlayerRef ID (which when clicked will ping thePlayerObject for that runner), as well the UserID values +- Custom Session Property support to strings +- New SessionProperty type used to store supported Custom Session Properties +- New ShutdownReason.InvalidArguments + +**Changes** + +- Max number of Session Custom properties is now 10 properties +- Max size of Session Custom properties is now 500 bytes +- Only the Server/Host or Shared Master Client can now update the Session Properties +- SessionInfo.UpdateCustomProperties may fail if requirements are not met + +**Bug Fixes** + +- Fixed: NameServer when connecting to CN Region + +### Build 395 (Jan 24, 2022) + +**What's New** + +- FusionGraph meter bars now work with CanvasType GameObject , and not just Overlay +- FusionStats added controls for warning/error thresholds, and customization support for these color and threshold values + +### Build 394 (Jan 22, 2022) + +**Changes** + +- Button overlay in Hierarchy name now uses PlayerRef ID value rather than the runner name. NetworkDebugStart also now names any client runners using sequential letters rather than numbers, to avoid confusion - as the numbers used were arbitrary and not related to the PlayerRef ID of the peer + +**Bug Fixes** + +- Fixed: Changing scenes would result in null textures for Inline Help and Fusion GroupBox styles + +### Build 393 (Jan 21, 2022) + +**What's New** + +- Local PlayerObject related fields added to NetworkRunner and NetworkObject runtime inspector + +**Bug Fixes** + +- Fixed: PlayerRB2DPrototype used in the Setup Basic Fusion Scene 2D had interpolation disabled. Corrected that to auto + +### Build 392 (Jan 20, 2022) + +**What's New** + +- Interpolator now supports Boolean and Int32 types + +**Bug Fixes** + +- Fixed: Missing Properties after a Session Update + +### Build 391 (Jan 19, 2022) + +**Bug Fixes** + +- Fixed: Interpolator was not using specified Accuracy values (such as [Networked, Accuracy(.1f)]) and was defaulting to .001f + +### Build 390 (Jan 17, 2022) + +**What's New** + +- FusionStats handling for Good/Warn/Bad colors for bars +- FusionStats histogram handling for more stat types +- FusionStats color overrides for bar colors + +### Build 389 (Jan 16, 2022) + +**What's New** + +- FusionStats Histogram support for time based stats (like RTT) added + +### Build 388 (Jan 14, 2022) + +**What's New** + +- `NetworkBehaviour.InvokeOnChangedForInitialNonZeroValues`: returns true by default. Override and return false if you don't want `OnChanged` to be invoked for initial values + +**Improvements** + +- `NetworkObject` editor: inline help content for `IsSpawnable` property +- `NetworkObject` editor: `PrefabInfo` section is now also visible when editing a prefab + +**Changes** + +- `OnChanged`: restored the old behaviour, where the callback is also invoked for each initial non-zero value + +### Build 387 (Jan 13, 2022) + +**What's New** + +- NetworkDebugStart added runtime Add Additional Client and Add Additional Shared Client buttons to the inspector, which allow for more clients to be added at any time after startup. Useful for simulating late joiners +- FusionStats warning shown if no NetworkObject is found + +**Bug Fixes** + +- Fixed: Nested `NO` not attaching in some cases when using `EventualConsistency` without `AoI` +- Fixed: Despawned/culled nested `NetworkObjects` not being handled by late-joining clients. Currently such objects will remain deactivated +- Fixed: Referencing nested NetworkObjects is now possible +- Fixed: Collections in structs not being displayed correctly in the debugger + +### Build 386 (Jan 12, 2022) + +**Removed** + +- PlayerTtl, now the client is deactivated as it leaves + +**Bug Fixes** + +- Fixed: Extra Session Slot only if starting a Server + +### Build 385 (Jan 11, 2022) + +**What's New** + +- 'Add Fusion Stats' and other 'GameObject > Fusion' menus added to Fusion menu as well + +### Build 384 (Jan 10, 2022) + +**What's New** + +- NetworkObject.IsSpawnable property for getting/setting the NetworkObjectFlag.Ignore flag + +**Changes** + +- Use Alternatives UDP Ports by default + +**Bug Fixes** + +- Fixed: NetworkDebugStartGUI caches previous GUI.skin and restores it after update, to avoid interfering with user IMGUI that also may be in use + +### Build 383 (Jan 07, 2022) + +**Changes** + +- Scene NOs have `Spawned` invoked after all of them have been attached. This enables `[Networked]` object references to work reliably + +**Bug Fixes** + +- Fixed: Don't set scene dirty when assigning to non-serialized `NetworkBehaviour.Object` +- Fixed: Weaver: error when using collections with `INetworkStructs` from external assemblies +- Fixed: Dictionaries not being drawn for some properties in Unity 2020.3.25 + +### Build 382 (Jan 06, 2022) + +**Bug Fixes** + +- Fixed: An issue in the NRB rotation syncing when `Space` was set to `Local` + +### Build 381 (Jan 05, 2022) + +**Improvements** + +- Order of backing fields added to `[Networked]` properties + +**Bug Fixes** + +- Fixed: Dictionaries not being drawn in Unity 2020.3.25 + +### Build 379 (Jan 02, 2022) + +**Bug Fixes** + +- Fixed: Unable to delete object warning when using older generated FusionStats meters with newer Fusion version + +### Build 373 (Dec 23, 2021) + +**Removed** + +- Duplicated NetworkRunner.StartGame() + +### Build 372 (Dec 22, 2021) + +**What's New** + +- Lite skin support for RunnerVisibilityControls window + +**Bug Fixes** + +- Fixed: Nested objects never getting attached if their root object has been initially ruled out by AoI, in EventualConsistency mode +- Fixed: Nested object with global AoI not getting attached if parent is not in global AoI, in EventualConsistency mode + +### Build 371 (Dec 21, 2021) + +**What's New** + +- `RpcInvokeInfo` - a way to check how invoking a RPC went. The most basic syntax is `[Rpc] RpcInvokeInfo Test() { return default; }` and then `var info = Test(); Debug.Log(info);` + +**Changes** + +- Update Realtime SDK to v4.1.6.11 + +### Build 370 (Dec 20, 2021) + +**Removed** + +- SimulationBehaviourAttribute OrderBefore and OrderAfter properties removed. Use [OrderBefore] and [OrderAfter] attributes instead + +**Bug Fixes** + +- Fixed: NanoSockets build for Linux IL2CPP +- Fixed: Pinning the Guid of `Fusion.Runtime.xml.meta` + +### Build 368 (Dec 17, 2021) + +**Bug Fixes** + +- Fixed: GroupBoxInfo 'RectOffset?' was illegal + +### Build 367 (Dec 16, 2021) + +**Changes** + +- `OnChange` callback is no longer invoked for non-zeroed properties on object spawn + +### Build 366 (Dec 15, 2021) + +**What's New** + +- FusionStats can toggle between "avg per Tick", "avg per Sample" and "avg per Second" by clicking on the average value of a running fusion stat + +**Changes** + +- RPCs are now culled according to AoI (Area of Interest) in Eventual Consistency mode +- `NetworkRigidbody` and `NetworkRigidbody2D` are no longer sealed classes + +### Build 365 (Dec 14, 2021) + +**Breaking Changes** + +- NetworkRunner.Despawn now defers the actual despawn until end of the current tick (or start of next tick, if Despawn is called outside of the simulation loop). This is a breaking change as previously objects would be removed from the network runner instantly + +**Changes** + +- Expose NetworkRunner.AuthenticationValues + +**Bug Fixes** + +- Fixed: The accumulated prediction error not being reset on `NetworkTransform` behaviours when the interpolated correction is toggled off + +### Build 363 (Dec 13, 2021) + +**What's New** + +- `INetworkInput` support for `[Networked]` properties +- FusionStats Graphs will now gray out when not available, and will indicate why the stat is invalid in the current mode. Cases such as: Debug Only, Client Only, Non-State Authority Only, or EC Only - giving better indication why a stat isn't showing data + +**Bug Fixes** + +- Fixed: Added caching of Animator.parameters, Animator.layerCount and Animator.parameterCount properties to NetworkMecanAnimator +- Fixed: An issue on `NetworkRigidbody` that could cause the client state to not be synced to the state authority when the RB is disabled and re-enabled on the latter while being asleep +- Fixed: Issue with FixedUpdateNetwork skipping invokes if the .Next pointer on the simulation behaviour was invalidated during its own fixed update call +- Fixed: Unity crashing when inspecting `[Networked]` collections in VS debugger + +### Build 360 (Dec 10, 2021) + +**What's New** + +- `[Networked]` `string` properties for `INetworkStructs`. To keep the struct unmanaged use following syntax: `[Networked] string Foo { get => default; set {} }` + +**Changes** + +- `[Networked]` strings are now encoded with UTF32 + +### Build 359 (Dec 09, 2021) + +**Changes** + +- Nested NetworkObject inherit parent's input authority on spawn + +**Bug Fixes** + +- Fixed: `NetworkRigidbody` not syncing the transform on the client until the first modification in the transform is received, when running client-side physics prediction, which could result in an un-synced initial state +- Fixed: NetworkDebugStart no longer overrides ProvideInput value for Server/Host. All Runners should default to ProvideInput == true +- Fixed: Shutdown while resolving Reflexive address +- Fixed: Despawning object in a callback handler preventing other listeners from being notified +- Fixed: Possible `MissingReferenceException` when resolving scene objects + +### Build 358 (Dec 08, 2021) + +**What's New** + +- Runner.GetPlayerUserId(PlayerRef) +- Runner.UserId + +**Bug Fixes** + +- Fixed: Shutdown Runner when connected to Lobby +- Fixed: Large Rpcs overflow issue +- Fixed: `NetBitBuffer` allowing for out of bounds reads/writes +- Fixed: FusionStats correctly destroys the canvas guide object in builds now + +# 0.12.0 + +## RC + +### Build 357 (Dec 07, 2021) + +**Breaking Changes** + +- Clients now receive PlayerJoined and PlayerLeft callbacks for all players joining and leaving the game, not just themselves. Additionally ActivePlayers now contains all current players on clients also + +**What's New** + +- Added NetworkRunner.GetPlayerActorId to get the photon realtime actor id of a specific player in shared mode +- `NetworkRunner.EnsureRunnerSceneIsActive` +- Added NetworkRunner.SetPlayerObject and NetworkRunner.GetPlayerObject +- Added "bool? force = null" parameter to all methods on RawInterpolator +- Added RawInterpolator.TryGetStruct +- Added TryGetArray/TryGetDictionary/TryGetLinkedList to RawInterpolator to get interpolated arrays/dicts/lists +- Added SinglePlayerPause(bool paused) overload +- Added NetworkLinkedList.Remap to allow easy access to RawInterpolator data for linked lists +- Added NetworkDictionary.Remap to allow easy access to raw interpolator data for dictionaries +- Added NetworkArray.Remap to give easy access to raw interpolator arrays +- Support to specifying the tick 'from' and 'to' and the interpolation alpha when performing lag-compensated queries +- Can now add FusionStats at runtime to a gameobject or scene simply with FusionStats.Create(). Will detect if a NetworkObject is on the supplied parent, and enable Object stats if so. +Added more explicit control over graph stats display modes with the enum description +- Added SimulationCount stat to FusionStats +- `NetworkBehaviour.Id` property +- XML documentation to the `InterpolatedErrorCorrectionSettings`, used to interpolate the prediction error correction on NT/NCC/NRB +- Lag-compensated box overlaps, accessible through `Runner.LagCompensation.OverlapBox` overloads +- FusionAddressablePrefabsPreloader +- `NetworkPrefabTable.GetEntries()` - a way to iterate contents of the prefab table +- Inspector support for pointer properties +- Support for initializing pointer properties. The syntax is: `[Networked] public Foo* Prop => MakePtr(new Foo());` +- Support for ref properties. The syntax is: `[Networked] public ref Foo Prop => ref MakeRef(new Foo());` +- Added NetworkRunner.SinglePlayerPause and NetworkRunner.SinglePlayerContinue +- `NetworkLinkedList` inspector support +- Expanded FusionStats to also work as billboarding ingame objects in addition to screen overlays +- FusionStats can (and should) be added by selecting the menu 'GameObject>Fusion>Add Fusion Stats'. This will generate the appropriate stats UI based on whether a NetworkObject was selected or not +- Histogram stats handling +- FusionStatsMeterBar graph type +- NetworkObject and Sockets stats options to FusionStats + +**Changes** + +- `NetworkRunner.InstantiateInRunnerScene`, if invoked in multiple peer mode, switches active scene to the runner's one before instantiating the GameObject. Previously active scene is restored after instantiation +- Weaver: `OnChanged` callbacks signatures are now verified during weaving +- Weaver: Missing config results in a warning rather than an error +- ILWeaver reports an error if config is not found (previously: a warning) +- A warning is emit when a float/boolean/vector type is used in a pointer/ref property +- Addressable prefabs work out of the box and no longer need any preloading +- PlayerJoined and PlayerLeft is now called on client as well as server for all players joining/leaving +- ActivePlayers now returns all players on Clients also +- Exceptions that get happen inside of connection flow always get logged now + +**Bug Fixes** + +- Fixed: `NetworkObject.Ptr` not getting nullified when the object is destroyed in some situations +- Fixed: Using a struct with `[Networked]` property throwing an exception if `Allow unsafe code` is not enabled +- Fixed: Unity 2020.2.5 inspector errors +- Fixed: "Failed to unwrap" warnings spam for `NetworkObject`/`NetworkBehaviour` properties +- Fixed: NetworkRunner.ProvideInput can now be set at any point during runner startup and initialization +- Fixed: Issue with network object id conflicts in shared mode +- Fixed: `Fusion/Toggle Debug Dlls` having hardcoded Dlls paths +- Fixed: Issue with DestroyWhenStateAuthorityLeaves not working for master client +- Fixed: Issue with destroying objects in state auth changed callback +- Fixed: NetworkDebugStart AlwaysShowStats was producing two stats windows for clients in single peer mode +- Fixed: `NetworkSceneManagerBase` not cleaning up its state on shutdown +- Fixed: `NetworkObjectEditor` throwing exception if object header is missing +- Fixed: MasterClient not updated when Master leaves +- Fixed: Lag-compensated Hitboxes with negative radius/extents not being correctly detected by queries +- Fixed: Lag-compensated sphere overlaps and raycasts always being performed with sub-tick accuracy, despite the hit option flag +- Fixed: An issue on the computation of `NetworkRunner.InterpolationRenderTime` on the clients +- Fixed: InterpolationRenderTime now correctly returns SimulationRenderTime on server +- Fixed: NetworkRunner.SimulationRenderTime now uses correct calculation +- Fixed: ILWeaver not recognizing `[field: NonSerialized]` attribute +- Fixed: Issue with server/host trying to send player left message when no clients connected +- Fixed: RunnerVisibilityNode now automatically added for Runner Instantiated GameObjects. Previously these were only added for NetworkObjects + +# 0.11.0 + +## RC Nightly + +### Build 353 (Nov 17, 2021) + +**What's New** + +- Inspector support for struct `[Networked]` properties multi-edit +- Inspector support for `[Networked]` struct properties + +**Changes** + +- Field attributes (`[field: FooAttribute]`) applied to auto `[Networked]` properties are applied to the backing field + +### Build 352 (Nov 15, 2021) + +**Bug Fixes** + +- Fixed: Mono.Cecil being referenced if a struct had `[Networked]` property + +### Build 351 (Nov 12, 2021) + +**What's New** + +- Added support for state authority override toggle in shared mode +- Added support for IEnumerable to NetworkArray +- Added NetworkBehaviour.WordInfo which allows access to the low level word offset + count for each networkbehaviour +- Added support for NetworkLinkList + +### Build 350 (Nov 11, 2021) + +**What's New** + +- New ShutdownReasons + +**Changes** + +- Improved error handling of StartGame +- Runner.StartGame now outputs the StartGameResult + +### Build 348 (Nov 10, 2021) + +**What's New** + +- Added pre-built NetworkButtons struct to represent 32 buttons as a single int, including detecting pressed/released evnts +- Limited support for `[Networked]` properties for `INetworkStructs`. Currently there's no inspector for such properties. Also, `NetworkObject`/`NetworkBehaviour` properties are not supported +- `NetworkArray` and `NetworkDictionary` support for managed types (e.g. `NetworkBehaviour`, `NetworkObject`). Initializing from the inspector half-works, because referenced objects may not have been spawned yet by the runner +- `NetworkArray` and `NetworkDictionary` properties can now use `NetworkBehaviour` and `NetworkObject` types as well. Not that initializing from the inspector half-works, because some objects may not yet have been spawned, so their id is 0. +Remove: `ElementReader` and `ElementWriter` +- `IElemenetReaderWriter` (used internally by `NetworkArray` and `NetworkDictionary`) +- [Networked] array and dictionary support for structs +- Added implicit conversion operator from NetworkBehaviour to NetworkBehaviourId + +**Changes** + +- `NetworkArray` uses `ElementWriter` and `ElementReader` delegates rather than nested `Writer/Reader` ones + +**Bug Fixes** + +- Fixed: NetConnectFailedReason parsing + +### Build 347 (Nov 10, 2021) + +**What's New** + +- Optional bool parameter on `NetworkBehaviour.Interpolator.TryGetValues` and `GetValues` to force retrieving the interpolation data from the current simulation time frame or between snapshots + +**Bug Fixes** + +- Fixed: An issue in the NT prediction error correction when `Space` was set to `Local` +- Fixed: An issue in the NT rotation interpolation when `Space` was set to `Local` +- Fixed: An issue on the NT/NCC/NRB prediction error correction that would cause it to run on the peer that has state authority +- Fixed: An issue on the NT/NCC/NRB prediction error correction that could cause jitter, due to fields' limited accuracy + +### Build 346 (Nov 09, 2021) + +**What's New** + +- DisableClientSessionCreation to StartGame + +### Build 345 (Nov 08, 2021) + +**Bug Fixes** + +- Fixed: Issue with OnChanged callbacks being called continuously + +### Build 344 (Nov 05, 2021) + +**What's New** + +- Added Changed.Rescan() method that can be invoked inside of an OnChange callback if you want a change done to state inside of the callback to be checked for additional callbacks to be invoked without waiting for the next tick + +**Bug Fixes** + +- Fixed: Logging from simulation constructor is only logged with debug dlls now +- Fixed: Bug with OnChanged callback not being invoked on state authority if state was changed inside of an OnChanged callback + +### Build 343 (Nov 04, 2021) + +**Changes** + +- Moved RequestStateAuthority and ReleaseStateAuthority From NetworkRunner to NetworkObject +- AssignInputAuthority only works in Client/Server, Client/Host _or_ if you have StateAuthority in Share Mode +- Removed AssignStateAuthority and RemoveStateAuthority, use NetworkRunner.RequestStateAuthority and NetworkRunner.ReleaseStateAuthority + +**Bug Fixes** + +- Fixed: Issue with RPCs needing to be able toggled as "resim" to be usable in Spawned +- Fixed: Added [UnmanagedFunctionPointer(CallingConvention.Cdecl)] to BurstInsertAndResolveDelegate to resolve warning in Unity 2021.2 + +### Build 342 (Nov 03, 2021) + +**What's New** + +- Added support for 128kb and 256kb allocator pages + +**Changes** + +- Renamed PageShiftValues enum to PageSizes + +**Bug Fixes** + +- Fixed: Rendering of NRB not being predicted when running client-side physics and `InterpolationDataSources` was set to `Auto` +- Fixed: Snapshots not being freed in shared mode +- Fixed: Reduced static memory allocation on clients further +- Fixed: Small memory leak when stopping play mode in editor + +### Build 341 (Nov 02, 2021) + +**What's New** + +- NetworkRunner.SetActiveScene accepting a scene path or name (an extension method) +- New AutoHostOrClient GameMode +- Added DefaultState property to RunnerVisibilityNode, which allow devs to modify the default state of the associated object when NetworkRunner.IsVisible is set to true + +**Changes** + +- `NetworkSceneManagerDefault` component gets added and a warning is printed if no `INetworkSceneObjectProvider` is passed to the runner + +**Bug Fixes** + +- Fixed: FusionStats disabling canvases in builds +- Fixed: NetworkRunner not resetting IsMultiplePeerSceneTemp flag +- Fixed: "missing snapshot" error on clients when large frame skips or stutter happens +- Fixed: Reduced static memory consumption on clients +- Fixed: Null reference exception on shutdown + +### Build 340 (Nov 01, 2021) + +**Changes** + +- Removed ability to run socket on background thread + +### Build 339 (Oct 31, 2021) + +**What's New** + +- RunnerVisibilityControls now work with vary narrow window sizes (allowing for docking to the left side of windows) +- RunnerVisibilityControls now uses icons rather than default toggles for 'Visibility' and 'Provide Input', allowing for more condensed window sizes +- Runner names in RunnerVisibilityControls now are clickable, and will ping/select their associated runner gameobject. +Changed GUIStyle statics to use Lazy<> +- Runner overlays on the hierarchy now act as buttons and will ping/select the associated NetworkRunner gameobject + +**Bug Fixes** + +- Fixed: RunnerVisibilityControls use the Runner name rather than the associated Scene name (required due to changes in scene naming in Multi-Peer mode) + +### Build 338 (Oct 30, 2021) + +**Bug Fixes** + +- Fixed: CodeGen exception due to 'MakeInitializer' having no implementation + +### Build 336 (Oct 29, 2021) + +- Initial 0.11.0 version + +# 0.10.0 + +## RC + +### Build 335 (Oct 29, 2021) + +**What's New** + +- `[UnitySerializeField]` attribute - apply on a private `[Networked]` property to have the backing field available in the inspector +- `[UnityFormerlySerializedAsAttribute]` attribute - apply on a `[Networked]` property to have `[FormerlySerializedAsAttribute]` attribute added to the backing field +- Support for inline [Networked] property initializers. This is now valid: `[Networked] public int Foo { get; set; } = 1;`. For arrays and dictionaries use `MakeInitializer`, e.g. `[Networked] public NetworkArray Foo { get; } = MakeInitializer(new[] { 0, 1, 2 });` +- Support for inline [Networked] property initializers. For arrays and dictionaries use `MakeInitializer`, e.g. `[Networked] public NetworkArray Foo { get; } = MakeInitializer(new[] { 0, 1, 2 });` + +**Changes** + +- `[Networked]` array or dictionary properties can't have setters + +**Bug Fixes** + +- Fixed: NT/NCC state being reset on the State Authority peer, reverting any modifications done on the editor/backing fields + +# 0.9.0 + +## Beta Nightly + +### Build 333 (Oct 28, 2021) + +**What's New** + +- Custom PhotonAppSettings on StartGameArgs +- Raw `bool` `[Networked]` properties support for `NetworkBehaviour`. `NetworkBool` still needs to be used in structs + +**Bug Fixes** + +- Fixed: Reverted NetworkMecanAnimator.SetTrigger() method to previous handling. Input Authority no longer passes the trigger through to the Animator, and this call is only meant to be used on the State Authority + +### Build 332 (Oct 27, 2021) + +**What's New** + +- Added support for sending reliable data between players + +**Changes** + +- If an attempt to send reliable data to a local player or to the server from itself, the data will invoke the OnReliableData callback +- Renamed SendReliableDataFromServerToPlayer to SendReliableDataToPlayer +- Renamed SendReliableDataFromClientToServer to SendReliableDataToServer + +**Bug Fixes** + +- Fixed: `NetworkBool` is now `[Serializable]` and has a dedicated drawer +- Fixed: ILWeaver error for `[Networked]` pointer properties +- Fixed: Missing PhotonAppSettings +- Fixed: Spawning prefabs in `OnConnectedToServer` callback triggering an assertion in `NetworkSceneManagerDefault` in multiple peer mode + +### Build 331 (Oct 26, 2021) + +- Please remove the files at 'Assets\Photon\PhotonLibs\[Metro,WebSocket,Photon3Unity3D.*]' as they are not necessary anymore + +**What's New** + +- `NetworkArray` and `NetworkDictionary` properties are now Unity-editable +- Arrays and reference type `[Networked]` properties can now have its defaults set in the inspector +- A debug warning if no `INetworkSceneObjectProvider` is passed to the runner in Unity builds + +**Changes** + +- Photon Realtime SDK to v4.1.6.10 +- `NetworkBehaviour.Defaults` to `NetworkBehaviour.CopyBackingFieldsToState` and `NetworkBehaviour.ReadDefaults` to `NetworkBehaviour.CopyStateToBackingFields` +- `NetworkBehaviour` inspector: networked properties are no longer displayed in a separate foldout. Instead, the "default" variables are updated + +**Bug Fixes** + +- Fixed: An issue in the interpolated prediction error correction of NRBs modified after the `NetworkPhysicsSimulation` +- Fixed: Issue with shared mode backend not applying state updates from client and a regular interval +- Fixed: `NetworkSceneManagerDefault` not registering GameObjects in `RunnerVisibilityNode` +- Fixed: Nested scene `NetworkBehaviours` not being attached to +- Fixed: `NullReferenceException` in `BehaviourEditorUtils.GetDelegateFromMember` + +### Build 330 (Oct 25, 2021) + +**Changes** + +- Expose Current Runner.NATType + +**Bug Fixes** + +- Fixed: NetworkMecanAnimator.SetTrigger() calls now will pass-through to Animator.SetTrigger() when HasInputAuthority is true + +### Build 329 (Oct 23, 2021) + +**What's New** + +- NAT Type Discovery + +**Changes** + +- Optimization on the Reflexive Address Query + +**Bug Fixes** + +- Fixed: Issue with delta snapshot discarding valid snapshots on server + +### Build 328 (Oct 22, 2021) + +**What's New** + +- Added support for predictive despawn on clients in server<>client topology + +**Bug Fixes** + +- Fixed: Missing check of PlayerCount from StartArgs +- Fixed: A typo in `NetworkSceneManagerDefault` + +# 0.8.0 + +## Beta Nightly + +### Build 327 (Oct 21, 2021) + +**Breaking Changes** + +- Scene loading overhaul - Scene loading is now implemented on Unity's side with `INetworkSceneObjectProvider` interface. The default implementation is provided with `NetworkSceneManagerDefault` + +**What's New** + +- BitSet64, BitSet128, BitSet192, BitSet256 +- `BitSet` types +- Added support for sending large chunks of reliable data via the NetworkRunner.SendReliableDataFromServerToPlayer and NetworkRunner.SendReliableDataFromClientToServer + +**Changes** + +- Multiple peer mode no longer uses "wrapper" scenes. Scenes are loaded and held on to just like with single peer mode +- `OnBeforeSpawned` callback is now called before `PredictedSpawnSpawned` on the Client, instead of before the regular `Spawned` + +**Removed** + +- NetworkProjectConfig.Scenes. `NetworkSceneManagerDefault` treats `SceneRef` as the scene's build index instead, but generally speaking `SceneRef` can mean whatever `INetworkSceneObjectProvider` implementation wants it to - a build index, a bitfield etc + +**Bug Fixes** + +- Fixed: Bug with TimerDelta ignoring first delta + +### Build 325 (Oct 20, 2021) + +**What's New** + +- Interpolated error correction of the transform state on client-side prediction +- Added per-object rpc stats + +### Build 324 (Oct 19, 2021) + +**What's New** + +- Added per object bandwidth stats in EC mode + +**Changes** + +- Show MaxPlayers and PlayerCount on Lobby + +**Bug Fixes** + +- Fixed: `Runner.IsFirstTick` and `IsLastTick` being swapped on client-side resimulations +- Fixed: Shutdown State when reusing Fusion Runner + +### Build 322 (Oct 18, 2021) + +**What's New** + +- Support to Custom Authentication +- New OnCustomAuthenticationResponse on INetworkRunnerCallbacks +- New Shutdown Reasons related to Custom Authentication + +**Changes** + +- Re-work Async StartGame Handling exposing info about faulted initializations + +**Bug Fixes** + +- Fixed: Issue with spiking bandwidth when client would fall too far behind snapshot history +- Fixed: VisibilityNode handling for nested NetworkObject spawning fixed + +### Build 321 (Oct 14, 2021) + +**What's New** + +- More control over FusionStats window at edit time and runtime +- FusionStats enum to select which stats to display + +**Bug Fixes** + +- Fixed: FusionStats readable at all sizes and aspect ratios + +### Build 320 (Oct 13, 2021) + +**What's New** + +- NT/NRB/NCC/NTA all now encode extra state info for teleports, which act as lerp targets interpolating up to the teleport event (otherwise teleports freeze for one frame) +- Added LocalScale sync to NT/NRB +- Added more interpolation handling for parenting/scaling edge cases +- Experimental option added for InterpolationTarget to detach from NetworkObject, and reparent as needed to improve scaling interpolation + +**Bug Fixes** + +- Fixed: Infinite loop in NetworkRunner.TryFindBehaviour + +### Build 318 (Oct 08, 2021) + +**What's New** + +- Added NetwokRunner.TryGetNetworkBehaviour to convert NetworkBehaviourId to a NetworkBehaviour +- Added NetworkDictionary.Get +- Added NetworkDictionary.Set + +### Build 317 (Oct 07, 2021) + +**Changes** + +- Increased STUN Attempts + +**Bug Fixes** + +- Fixed: Occasional null error in NetworkRunner inspector editor code, when running in single player mode + +### Build 316 (Oct 07, 2021) + +**Bug Fixes** + +- Fixed: Inverted NetworkDictionary.TryGet case +- Fixed: `NetworkObjectGuid` and `NetworkPrefabRef` not being accepted as Rpc parameters + +### Build 315 (Oct 07, 2021) + +**What's New** + +- Added indexer on NetworkDictionary +- Added ContainsValue on NetworkDictionary + +**Changes** + +- Renamed NetworkDictionary.Contains to NetworkDictionary.ContainsKey to be consistent with .NET dictionary API +- Moved call to InvokeSceneLoadStart to happen in base class instead + +**Bug Fixes** + +- Fixed: AddCallbacks and RemoveCallbacks now check for existance of internal callback list and can be called before NetworkRunner.StartGame +- Fixed: Issues with network dictionary memory alignment + +### Build 314 (Oct 06, 2021) + +**What's New** + +- Standard Unity property attributes for `[Networked]` properties - UnityHeader, UnityRange etc + +**Improvements** + +- Odin compatibility +- ILWeaver - `[Networked]` property attributes that can also be used on a field (depending on their `AttributeTargets`) are now applied to the underlying field as well. This means Odin attributes can be used directly on `[Networked]` properties + +**Changes** + +- InlineHelpAttribute visibility to public +- ProvideInput is now true by default for Client and Host + +### Build 313 (Oct 06, 2021) + +**What's New** + +- Added NetworkDelegates which is similar to NetworkEvents but using regular C# delegates instead of Unity Events +- Added INetworkRunnerCallbacks.OnSceneLoadStart +- Added INetworkRunnerCallbacks.OnSceneLoadDone + +**Changes** + +- Removed INetworkRunnerCallbacks.OnObjectWordsChanged as it was no longer called/used + +**Bug Fixes** + +- Fixed: Constructor on NetworkDictionary is now public + +### Build 311 (Oct 05, 2021) + +**Changes** + +- NetworkBehaviourId now implements IEquatable, == and != operator overloads and GetHashCode() + +**Bug Fixes** + +- Fixed: Issue where it would not be possible to spawn networked objects in ISceneLoadDone callback +- Fixed: NetworkDictionary now accepts enums as K generic argument + +### Build 310 (Oct 05, 2021) + +**What's New** + +- Added support to weaver for System.Guid + +**Changes** + +- Exposed NetworkRunner.IsSharedModeMasterClient + +### Build 309 (Oct 04, 2021) + +**What's New** + +- Added support for NetworkDictionary + +# 0.7.0 + +## Beta Nightly + +### Build 308 (Oct 03, 2021) + +**What's New** + +- HelpURL references added to components + +**Changes** + +- Clicking headers always pings the script in the Project folder, even if the header is tied to launch a URL + +### Build 307 (Oct 02, 2021) + +**What's New** + +- IPv6 Support +- Support to Custom STUN Server + +**Changes** + +- STUN Servers Resolution +- Disable NAT Punch when no Reflexive Info is found + +### Build 306 (Oct 01, 2021) + +**What's New** + +- Added ISceneLoadStart +- Fixed issues with ISceneLoaded not being called + +**Changes** + +- Renamed ISceneLoaded to ISceneLoadDone + +**Bug Fixes** + +- Fixed: Issue with ISceneLoadStart not being registered as a callback interface +- Fixed: Issue with Refused connection not being sent from server + +### Build 304 (Sep 30, 2021) + +**Bug Fixes** + +- Fixed: Issue with scene object staying disabled on server in certain scenario +- Fixed: New Odin handling was hiding Odin's virtual OnEnable in NetworkBehaviourEditor.cs + +### Build 303 (Sep 29, 2021) + +**Changes** + +- Scene objects are now disabled by default after being loaded and only enabled once their replicated data is received + +**Bug Fixes** + +- Fixed: Inline help for Odin editor not being visible. Odin editor for Fusion types can be disabled with `FUSION_ODIN_DISABLED` define +- Fixed: Inline help sizing issues when there's no vertical scrollbar +- Fixed: Inline help does not try do load xml doc for Unity assemblies +- Fixed: Connection Accept/Refuse in OnConnectRequest callback + +### Build 302 (Sep 28, 2021) + +**Improvements** + +- Inspector inline help loading times + +**Bug Fixes** + +- Fixed: Auto-generated `NetworkBehaviour.Defaults()` overrides not invoking base class' implementation + +### Build 300 (Sep 27, 2021) + +**What's New** + +- `NetworkPrefabTable.Count` and `NetworkPrefabTable.LastId` +- Added non-generic NetworkBehaviour.GetInterpolator to get a RawInterpolator struct to allow for interpolation of user defined structs + +**Changes** + +- Inline help button is placed on the left again +- Exposed Simulation.SnapshotHistory buffer for low level access + +**Bug Fixes** + +- Fixed: Issue with objects not being released back to pool +- Fixed: Issue with calling shutdown inside of FUN + +### Build 299 (Sep 24, 2021) + +**Breaking Changes** + +- Removed Fusion.Odin.asmdef and all `Fusion.*Odin` behaviour types. Use regular Fusion behaviour classes instead, `Fusion.BehaviourEditor` is Odin-aware. Due to Unity limitations, `Assets/Photon/Scripts/Odin` needs to be removed manually, if this build is used to update existing Fusion integration +- Removed Fusion.Odin.asmdef and all `Fusion.*Odin` behaviour types. Use regular Fusion behaviour classes instead, `Fusion.BehaviourEditor` is Odin-aware + +### Build 298 (Sep 23, 2021) + +**What's New** + +- `AssemblyNameAttribute` and `AssemblyNameAttributeDrawer`, put to use in `NetworkProjectConfig.AssembliesToWeave` + +**Improvements** + +- Inspector: Inline help works for arrays now +- Inspector: Improved compatibility with custom drawers + +**Changes** + +- Inspector: Inline help button is displayed on the right side of a property value +- ILWeaver: assembly names are now case insensitive + +**Removed** + +- `Fusion.Behaviour.Editor*` properties. Use `[ScriptHelp]` attribute from now on + +### Build 297 (Sep 22, 2021) + +**Breaking Changes** + +- Due to changes in prefab pipeline, prefabs need to be reimported. This can be done with `Fusion/Rebuild Object Table` + +**What's New** + +- `[NetworkPrefab]` attribute. When added to a `NetworkObject` field, the object picker will only show prefabs with `FusionPrefab` label. +Remove: `NetworkProjectConfig.PrefabSources`: prefab source information is now stored in config's hidden nested assets and is generated based on prefabs marked with `FusionPrefab` label. This means that serialized config is now a lot smaller as it does not contain prefab information directly + +**Changes** + +- Prefabs with `NetworkObject` script are now labeled with `FusionPrefab` label during import +- `NetworkPrefabRef` property drawer: object picker shows prefabs with `FusionPrefab` label +- Config's initial `PrefabAssetsContainerPath` value to an empty string. This means that `NetworkPrefabAsset` (a reference-based alternative to `NetworkPrefabRef`/`NetworkObject` references) generation is now disabled by default. Existing configs are not affected +- `NetworkPrefabTable.TryAdd` now accepts a guid and an `INetworkPrefabSource` instance. If prefabs need to be added at runtime, `NetworkPrefabSourceStatic` and `NetworkPrefabSourceUnityBase` subclasses can be used as `INetworkPrefabSource` implementations +- NetworkPrefabAssets are disabled by default (they need `PrefabAssetsContainerPath` field set in `NetworkProjectConfig` inspector) + +**Bug Fixes** + +- Fixed: Config warning for initial weaving + +### Build 296 (Sep 22, 2021) + +**Bug Fixes** + +- Fixed: ILWeaver error when `NetworkBehaviour` subclass from another assembly was used in a `[Networked]` property + +### Build 294 (Sep 21, 2021) + +**What's New** + +- Fusion Matchmaking API +- SessionInfo class with all major Session related information +- NetworkRunner.StartGame(GameMode, SessionInfo) to join an specific Session +- NetworkRunner.JoinSessionLobby(SessionLobby, LobbyName) to join a Lobby +- INetworkRunnerCallbacks.OnSessionListUpdated to receive Session Lobby list updates +- Several new Shutdown Reasons for better handling of error cases + +**Changes** + +- NetworkRunner.GameInfo renamed to NetworkRunner.SessionInfo +- NetworkRunner.StartGame with Custom Params and Custom Lobby + +### Build 292 (Sep 17, 2021) + +**Bug Fixes** + +- Fixed: IL error in `[Networked]` properties returning `NetworkBehaviours` + +### Build 291 (Sep 15, 2021) + +**Changes** + +- `LagCompensatedHit.Object` is deprecated and renamed to `GameObject`, now also having a more descriptive documentation + +### Build 290 (Sep 13, 2021) + +**What's New** + +- [NetworkSerializeMethod] and [NetworkDeserializeMethod] attributes. Together they can make any type Fusion-serializable (usable in `[Networked]` properties and in Rpcs). Expected signatures, for type T, are `[NetworkSerializeMethod(MaxSize = )] public static int (NetworkRunner runner, T obj, byte* data)` (returns number of bytes written) and `[NetworkDeserializeMethod] public static int (NetworkRunner, byte* data, ref T result)` (returns number of bytes read). There's also an alternative signature supported, which "wraps" T with an existing `INetworkStruct` type (S): `[NetworkSerializeMethod] public static S (NetworkRunner runner, T obj)` and `[NetworkDeserializeMethod] public static T (NetworkRunner runner, S wrapper)` or `[NetworkDeserializeMethod] public static void (NetworkRunner runner, S wrapper, ref T result)` + +**Bug Fixes** + +- Fixed: NDS in SharedMode with Empty Session Name + +### Build 289 (Sep 09, 2021) + +**Bug Fixes** + +- Fixed: Physics2D.simulateMode automatically set to Script mode at runtime, when NetworkProjectConfig PhysicsEngine is set to 2D +- Fixed: Unexpected characters in `Fusion.Editor.cs` + +### Build 288 (Sep 08, 2021) + +**Bug Fixes** + +- Fixed: Inspector - `NetworkObject` multiple selection +- Fixed: ILWeaver throwing an exception if multiple `netstandard` assemblies are referenced +- Fixed: AssetDatabaseUtils compile for Unity 2021.2b + +### Build 287 (Sep 07, 2021) + +**What's New** + +- Tanknarok Demo link added to Fusion Hub Window +Refactored styles in Fusion hub to a single GuiSkin and unified light and dark themes for easier future maintenance +- Links to Fusion and Photon settings added to Fusion Hub Window + +**Bug Fixes** + +- Fixed: Removed localization from Fusion Hub links (all were pointing to en-us versions) + +### Build 286 (Sep 03, 2021) + +**Changes** + +- Added GuiSkin option to NetworkDebugStartGUI which uses a GuiSkin asset for its defaults. While not meant to be used for production interfaces, this does allow for some customization + +**Bug Fixes** + +- Fixed: NetworkDebugStart handling for Automatic with setting of Client should now work correctly in all cases (Multi-peer and Single-Peer) + +### Build 285 (Sep 01, 2021) + +**Changes** + +- NT/NRB/NCC Teleport changed to hold on interpolation To rather than From state, to be consistent with 'NoInterpolation' handling + +**Bug Fixes** + +- Fixed: Hit normal being inverted in some configurations when performing lag-compensated raycasts against boxes +- Fixed: NetworkDebugStartGUI null errors when editing text ilelds fixed + +### Build 283 (Aug 31, 2021) + +**What's New** + +- Shutdown button added to NetworkDebugStart and option to Hide GUI After Start +- Option in NetworkDebugStart for showing a shutdown button +- Shutdown handling and cleanup/reset to NetworkDebugStart and NetworkRunner +- NetworkRunner Shutdown button added to component inspector at runtime +- NetworkDebugStartGUI adeded current stage information label + +**Bug Fixes** + +- Fixed: Execute Order Inspector window will correctly warn of OrderBefore/OrderAfter conflicts again +- Fixed: DrawifAttribute was adding one vertical space to GUI for hidden elements +- Fixed: Missing covert to local space in NRB in some edge cases +- Fixed: NetworkDebugStartGUI handling of vertical aspect ratio screens, and extreme screen aspect ratios + +### Build 282 (Aug 30, 2021) + +**Bug Fixes** + +- Fixed: Issue with despawn in shared mode +- Fixed: Bug in shared mode with interest management + +### Build 280 (Aug 28, 2021) + +**Bug Fixes** + +- Fixed: TempAlloc now handles allocations larger than managed allocator page size + +### Build 279 (Aug 27, 2021) + +**What's New** + +- Added 'Property Groups' functionality + +**Bug Fixes** + +- Fixed: Issues with initial state of scene objects in shared mode +- Fixed: State auth handling when receiving data in shared mode you have state auth over already +- Fixed: Bug with NRB.SharedMode.Defaults init +- Fixed: Issue with corrupted shared mode object state + +### Build 276 (Aug 26, 2021) + +**What's New** + +- Added ILocalPrefabCreated and IRemotePrefabCreated interfaces that can be used on NetworkBehaviour to get a callback _before_ Spawned is invoked to setup internal state on the behaviour + +**Changes** + +- Simplified PlayerJoined/PlayerLeft callback to always happen during a tick instead of randomly before/after ticks + +**Bug Fixes** + +- Fixed: Issue with remote prefabs not being created +- Fixed: Issue with Defaults overriding received state for NRB/NT during first rollback + +### Build 274 (Aug 25, 2021) + +- Fusion SDK version 0.7.0 released + +# 0.6.0 + +## Beta Nightly + +### Build 273 (Aug 25, 2021) + +**What's New** + +- Added changed state cache sharing for EC + +**Changes** + +- Improved Allocator.Copy performance massively + +**Bug Fixes** + +- Fixed: Memory leak on client when relasing SimulationInput instances +- Fixed: `NetworkBehaviour` sub classes not calling base types' `Defaults(bool,bool)` +- Fixed: SimulationInput.Buffer.CopySortedTo alloc +- Fixed: Removed extra unnecessary state copy done in delta snapshots transformed mode by server + +### Build 272 (Aug 25, 2021) + +**Breaking Changes** + +- SimulationBehaviour.GetInput moved down to NetworkBehaviour, if you need to get input inside of a SimulationBehaviour use Object.Runner.TryGetInputForPlayer +- Spawned() and Despawned() virtual methods moved down to NetworkBehaviour, not available on SimulationBehaviour anymore. If you need Spawned/Despawned callbacks on a SimulationBehaviour use the ISpawned and IDespawned callback interface +- SimulationBehaviour.StateAuthorityChanged was removed, use the new IStateAuthorityChanged callback interface instead + +**What's New** + +- Added support for connection token supplied via StartGameArgs.ConnectionToken. Not used in shared mode +- Added StartGameArgs.CustomCallbackInterfaces to allow users to hook into our O(1) constant time callback lookup +- Added NetworkRunner.GetInterfaceListNext and NetworkRunner.GetInterfaceListPrev to get prev/next of an interface simulation behaviour linked list +- Added IsFirstTick and IsForward + +**Bug Fixes** + +- Fixed: GetInterfaceListHead now uses the type parameter properly +- Fixed: Cant use pointers outside of unsafe compiler error in NetworkObjectGuidDrawer +- Fixed: Prefabs for remote objects are now properly spawned inside of a tick +- Fixed: IsLastTick now has correct value during resimulations +- Fixed: Scan for matching predicted spawned object now uses PredictionKey and PrefabType instead of only PredictionKey +- Fixed: OnBeforeSpawned callback is now invoked for predicted spawns when they are successful +- Fixed: IBeforeClientPredictionReset and IAfterClientPredictionReset are now called properly +- Fixed: StateAuthorityChanged is now called properly in all cases +- Fixed: Burst integration parameter call amount and order +- Fixed: NetworkRigidBody now properly replicates IsKinematic == true to proxies +- Fixed: NetworkRigidbody now respects and uses Space Local setting + +### Build 271 (Aug 24, 2021) + +**What's New** + +- QueryTriggerInteraction parameter for lag compensated raycasts (when including PhysX) + +**Bug Fixes** + +- Fixed: Lag compensated API can also be used in shared mode (queries are local to client) +- Fixed: Bug with invalid message sequence number assert check +- Fixed: Same bit/byte copy issue on plugin code +- Fixed: Issue with CloudCommunicator copying memory using bit length instead of byte length of buffer +- Fixed: A rare memory leak related to connection token +- Fixed: Incorrect Free order on native socket shutdown that could lead to rare crash +- Fixed: Minor memory leak on net bit buffer stacks + +### Build 268 (Aug 23, 2021) + +**What's New** + +- Serialized Accuracy retains hash reference to user defined tags even if those tags are deleted in NetworkProjectConfig, and will use AccuracyDefaults.Default for their value until the tag is restored, or a different tag is selected for the Accuracy's global setting + +**Changes** + +- Accuracy struct defaults to AccuracyDefaults.Default rather than AccuracyDefaults.Uncompressed + +### Build 266 (Aug 20, 2021) + +**What's New** + +- `Fusion.CodeGen.Trigger` asset that forces weaver to run if `AssembliesToWeaver` or `Accuracy` of the project config changes + +**Changes** + +- Default prefab asset container changed to "Assets/Photon/Fusion/User/NetworkPrefabAssetContainer.asset" +- Removed 'Recompile' button from AccuracyDefaults drawer. Will be replaced by auto re-weave when needed after changes to config + +### Build 265 (Aug 19, 2021) + +**Breaking Changes** + +- OnConnectionRequest callback now takes an additional byte[] token parameter + +**What's New** + +- Added support for a 128 byte connection token +- Legacy `NetworkProjectConfig` assets automatic conversion + +**Changes** + +- NetworkRunner.Connect now accepts a byte[] token parameter +- SocketThreadingMode no longer configurable + +### Build 264 (Aug 19, 2021) + +**Breaking Changes** + +- `NetworkProjectConfig` format and file extension have changed. If upgrading, select your `NetworkProjectConfig` asset and click `Convert To The New Config Format` button + +**What's New** + +- ILWeaver suggest possible `NetworkProjectConfig` locations is the config is not found in the default location + +**Changes** + +- ILWeaver no longer requires global asset text serialization + +**Removed** + +- `NetworkProjectConfigAsset.Instance.Config` removed. Use `NetworkProjectConfig.Global` instead + +### Build 263 (Aug 19, 2021) + +**Breaking Changes** + +- Server/Host mode with Client Auth has been removed completely. Client authoritative games are only possible in shared mode from now on. Server/Host mode is always server authoritative +- Physics Prediction has been removed in Shared Mode + +**What's New** + +- Added Runner.Topology to easily query if you are running in Client/Server or Shared mode + +**Changes** + +- PhysicsMode in NetworkProjectConfiguration is now called Server Physics Mode as it does not longer apply to Shared mode +- ReplicationMode in NetworkProjectConfiguration now only has 'Delta Snapshots' and 'Eventual Consistency', ServerAuth prefixed and ClientAUth prefixed options are removed +- Removed NetworkRunner.GetActiveSimulationBehaviours, use NetworkRunner.GetAllBehaviours instead +- `HitboxManager` no longer runs on FUN if the runner is being shutdown + +**Bug Fixes** + +- Fixed: Predicted spawns are now factored into RunnerVisibilityNode handling +- Fixed: Possible NullReferenceException on `Hitbox.OnDrawGizmos` when the Runner is being shutdown and disposed + +### Build 262 (Aug 18, 2021) + +**Bug Fixes** + +- Fixed: Issue with predictively spawned objects being pre-emptively flagged as failure despite the correct data from server having arrived +- Fixed: Made sure Despawn cant be called on an object which belongs to another runner + +### Build 261 (Aug 18, 2021) + +**Bug Fixes** + +- Fixed: 'Add NetworkObject' button on NetworkBehaviour's inspector now works with mutli-selection + +### Build 260 (Aug 16, 2021) + +**What's New** + +- FUSION_ODIN_EDITOR_ONLY define added, which disables advanced Odin serialization (Sirenix.Serialization). This define needs to be added to your Unity project in order to use Odin's Editor Only mode +- Added NetworkObject.LastReceiveTick +- Added Simulation Culling feature +- Added layer masks to area of interest system + +**Changes** + +- `NetworkProjectConfig` uses scene paths consistently now. However, to ensure backwards and Unity compatibility `NetworkProjectConfig.TryGetSceneRef` accepts both full scene paths and scene names +- Renamed 'Player' config setting to 'Default Players' to better convey what it does +- Removed 'send client input to server' configuration option for client auth mode +- Renamed UseAreaOfInterest to UseInterestManagement in config + +**Bug Fixes** + +- Fixed: PredictionKey does not end up in game state when running in client auth mode anymore +- Fixed: Empty Room Name when starting Server +Now, if no Room Name is specified, a Server peer will always create a Room with a random name +- Fixed: `NetworkDebugStart` not adding the current scene to `NetworkProjectConfig` correctly +- Fixed: Corrected error message about missing compiler define to show correct constant FUSION_BURST +- Fixed: Issues with RPC forwarding in Shared Mode + +### Build 259 (Aug 10, 2021) + +**What's New** + +- Improved 'Lite' theme handling for inline-help and headers. +Simplified inline-help skin to just use basic GUIStyles +Added graphic source for gui styles/icons + +**Changes** + +- `HitOptions.DetailedHit` is obsolete: lag-compensated queries now always compute detailed info + +**Bug Fixes** + +- Fixed: An issue in the computation of the broad-phase volume of lag-compensated hitbox roots with position offset that could cause hitboxes to not be checked +- Fixed: An issue in the computation of lag-compensated `HitboxRoot` bounds that could reduce the efficiency of the broad-phase data structure +- Fixed: An issue in the lag-compensated ray-cast queries against sphere objects when the ray length was set to `Mathf.Infinity`, `float.MaxValue` or very large values, causing the hit point to be set at infinity +- Fixed: Rpcs not being invoked for client authority objects +- Fixed: Rpc with source=RpcSources.StateAuthority not being invokable in Shared mode +- Fixed: Inspector handling with inline-help and component headers on Retina/hi-res type displays +Changed header skins to use basic styles and single textures for simpiler reskinning. +Saved make files for headers to project +Added more header color/icon options +Added Fusion styles to make eventual skinning/look changes later easier + +### Build 258 (Aug 08, 2021) + +**Bug Fixes** + +- Fixed: NetworkBehaviour "Missing NetworkObject" button now correctly adds NetworkObject to transform.root when clicked + +### Build 257 (Aug 07, 2021) + +**Bug Fixes** + +- Fixed: ILWeaver throwing an exception if an assembly is referenced more than once +- Fixed: RunWeaver() force saves any changes to the NetworkProjectConfigAsset before weaving + +### Build 256 (Aug 06, 2021) + +**What's New** + +- `Fusion/Run Weaver` menu item. Can be run in case of `Type Foo has not been weaved` runtime error messages +- Improvements in layout/help on the HitboxRoot component. +Moved button UI code out of editor and into HitRoot class (trying to avoid editor scripts unless absolutely needed) + +**Bug Fixes** + +- Fixed: Auto-BroadRange button on HitboxRoot handles Box collider types correctly now +- Fixed: Auto-BroadRange button on HitboxRoot ignores scaling selectively, in agreement with Hitboxes handling of scaling and offset values + +### Build 255 (Aug 05, 2021) + +**What's New** + +- Added TickTimer.RemainingTime to get the remaining time in seconds +- Inline-help support for inspector buttons using BehaviourButtonActionAttribute +- XMLDocumentation added support for tooltip/summary handling for all member types (to allow inline buttons etc to make use of inline help/tooltips) +- `HitboxRoot.SetMinBoundingRadius` method that resets the root `BroadRadius` to be the minimum bounding radius for the current hitboxes. +The method can be triggered from a new button on the component editor and is automatically called if the root is initialized with a zeroed broad radius + +**Changes** + +- FusionInstaller installs com.unity.nuget.mono-cecil instead of the outdated nuget.mono-cecil +- If the config is not serialized in text mode, ILWeaver emits a warning + +**Bug Fixes** + +- Fixed: Recompile button in AccuracyDefaults not recompiling everything in Unity 2021.1 +- Fixed: In case of Mono-Cecil being missed, Fusion.CodeGen emits a warnings instead of not compiling + +### Build 254 (Aug 04, 2021) + +**What's New** + +- Added InterpolatorReaderDelegate to allow access to reading the interpolated data out +- Added Interpolator.InterpolationDelegate to allow you to override the interpolation delegate + +**Changes** + +- Lag Compensated `Hitbox` and `HitboxRoot` active states are now decoupled from the GameObject/Behavior states and can be accessed with new get/set methods and properties + +**Bug Fixes** + +- Fixed: An issue in the lag-compensated structure that could cause stack overflow when removing entries +- Fixed: Several fixes for share mode scene objects + +# 0.5.0 + +## Beta Nightly + +### Build 253 (Aug 03, 2021) + +**What's New** + +- DestroyOrphaned - a way to destroy "non-existent" NOs +- Support for advanced Odin serialization(such as interfaces) added. Use BehaviourSerializedOdin, SimulationBehaviourSerializedOdin, and NetworkBehaviourSerializedOdin as base classes for the equivalent of Odin's SerializedMonoBehaviour + +**Changes** + +- `NetworkSceneManager` - abstract `NetworkSceneManagerBase` can now be extended and added to the runner prefab to implement custom scene loading logic + +**Bug Fixes** + +- Fixed: Derived NetworkedBehaviours will show parent's private [Networked] values in their Networked Properties foldout. (Previously only protected/public values were shown) +- Fixed: NetworkSceneManager reloading initial scene +- Fixed: Scene baking for `SimulationBehaviours` not in `NetworkObject` hierarchy +- Fixed: `NetworkDebugStart` DontDestroyOnLoad - the containing game object unparents itself with a warning +- Fixed: An issue when a client joins while a scene switch is happening (multiple peers) +- Fixed: If NetworkDebugStart is parented, move it to root in runtime; otherwise DontDestroyOnLoad won't work +- Fixed: Map baking - orphaned SimulationBehaviours (i.e. ones without NetworkObjects up the hierarchy) are acceptable +- Fixed: Removing game objects in multiple peer mode cleans up SharedInstanceUnitySceneRoot correctly + +### Build 251 (Aug 02, 2021) + +**What's New** + +- InterpolationDataSource.NoInterpolation as an option to disable Fusion handling interpolation for a NetworkBehaviour +- Added quick setup of basic working 2D scene with menu 'GameObject>Fusion>Setup Basic Fusion Scene 2D' +- Component header graphics reduced garbage and added more caching. Long component names now gracefully truncate words in order to fit the width of the inspector + +**Changes** + +- HitboxManager is now automatically handled by Runner (config moved to NetworkProjectSettings). Instance accessible through Runner.LagCompensation +- Improved client disconnect behaviour in case of a forcible shutdown (ALT-F4, etc.) +- Fusion header graphics now display when using BehaviourOdin, NetworkBehaviourOdin and SimulationBehaviourOdin base classes +- NRB and NRB2D now derive from NetworkRigidbodyBase class +- Exception message when `NetworkBehaviourWeavedAttribute` is not found to a more meaningful one +- SimulationBehaviour.Runner and SimulationBehaviour.Object is now marked as NonSerialized +- ILWeaver has been moved to a separate assembly (Unity.Fusion.CodeGen) and is now using Unity's ILPostProcessor pipeline. As a result the weaver is now an order of magnitude faster and more reliable. Also, weaving errors are now treated as compile errors (can't be accidentally cleared with the Clear button). If NetworkProjectConfig is not stored in its default location, implement ILWeaverSettings.OverrideNetworkProjectConfigPath in Fusion.CodeGen.User.cs + +**Bug Fixes** + +- Fixed: NetworkTransformAnchor treats a null parent and a parent which is the MultiPeer root scene object both as null. Fixes teleport hitch when reparenting an object which has the MultiPeer root as its parent the first time +- Fixed: NetworkCharacterController now uses same Defaults() handling as NetworkTransform, which resolves a teleport from origin occurring on Spawn. +Renamed SharedInstance to MultiPeer in a few places there were missed +- Fixed: Spawning position issues related to Defaults() handling in NT/NRB/NCC classes. +Changed Before/AfterAllTicks from explicit to implicit interface implementations +- Fixed: Assembly resolve when building using IL2CPP and .NET 4.x on Unity 2021.1+ +- Fixed: Issue with ILWeaver trying to reference mscorelib when netstandard is being used +- Fixed: ILWeaver referencing .NET core libraries in Unity 2021.1 +- Fixed: Added null checks inside DestroyRemotePrefab to catch cases where unity already destroyed an object but it's still exists in the network id lookup table +- Fixed: Memory leak relating to un-sent and un-received packets when shutting down +- Fixed: Memory leak in unity editor when using latency simulation +- Fixed: Additional memory leaks +- Fixed: NetworkRigidbody2D functionality restored +- Fixed: Several issues with packet fragmentation and IPv6 +- Fixed: Issue with new Spawn overload not finding NetworkObject reference properly +- Fixed: FUSION_WEAVER_DEBUG with UnityEditor weaver compile error +- Fixed: Unity has hard time parsing call stacks from exceptions logged in ILPP, split them into lines and log individually then +- Fixed: NetworkObjects are no longer automatically set to DontDestroyOnLoad +- Fixed: Several memory leaks + +### Build 246 (Jul 27, 2021) + +**What's New** + +- Added NetworkRunner.Spawn(T prefab) where T : NetworkBehaviour as a complement to NetworkRunner.Spawn(NetworkObject prefab) +- Added NetworkBehaviour.Interpolator.TryGetValues + +### Build 245 (Jul 23, 2021) + +**What's New** + +- Double-clicking component header graphic now opens script for editing (same behaviour as the unity default script field) +- Added support for interpolating user defined networked properties of type float/vector2/vector3/vector4/quaternion + +**Bug Fixes** + +- Fixed: Importing new Fusion releases should now properly detect existing config files +- Fixed: An issue in the lag compensated queries performed on the server/host when the state authority is not a client and sub-tick accuracy is set in the query options + +### Build 244 (Jul 22, 2021) + +**Bug Fixes** + +- Fixed: Config files should now be able to create correctly, even with completely missing/deleted/moved resource folders + +### Build 243 (Jul 22, 2021) + +**Changes** + +- SimulationBehaviour.Object and NetworkBehaviour.Object is now assigned for predicted spawned objects + +**Bug Fixes** + +- Fixed: NetworkObject.IsProxy now checks if object exists also + +### Build 242 (Jul 22, 2021) + +**What's New** + +- Added NetworkObject.IsPredictedSpawn, Obsoleted NetworkObject.IsPredicted + +**Changes** + +- StartGameArgs now allows you to provide an INetworkObjectPool at runtime to set the pool object to use dynamically +- NetworkObjectPool is now a scriptable object instead of a mono behaviour to allow it to be hooked up in editor inspector +- Second Despawned parameter "bool hasState" will now be true when running in EC mode, if hasState is true it means you can access the networked state of the object being destroyed +- Despawned now takes NetworkRunner as first parameter so runner is always accessible during Despawn +- NetworkObject StateAuthority/InputAuthoirty/IsSceneObject will no longer throw null ref exceptions for un-attached objects + +**Bug Fixes** + +- Fixed: Removed bandwidth overhead for unused words in input delta compressor +- Fixed: NetworkRunner.Despawn will now properly check for null object +- Fixed: NetworkProjectConfig and PhotonConfig assets should not generate correctly when creating a new project + +### Build 241 (Jul 21, 2021) + +**What's New** + +- Added PlayerRef.PlayerId + +**Changes** + +- NetworkObject.IsPredicted is no longer true after object has been confirmed by server +- Predicted spawned objects now have access to the runner +- Despawn now takes an optional allowPredicted parameter (defaults to false) which allows you to "despawn" predicted spawned objects on clients also + +### Build 238 (Jul 20, 2021) + +**What's New** + +- NetworkProjectConfig and PhotonAppSettings assets are now created if they do not exist +- Added Inspector component graphic headers (replacement for default script field) +- Added recompile button to AccuracyDefaults with warning when defaults are changed (a recompile is needed before running/building in order to reweave the new accuracy values) + +**Changes** + +- Realtime Client Conn Timeout to 30secs +This aims to prevent unwanted disconnects for long-running sessions +- Removing `NetworkProjectConfig.asset` and `PhotonAppSettings.asset` from the SDK packages + +### Build 236 (Jul 12, 2021) + +**Removed** + +- Removed NetworkTransformParent. Has been replaced with NetworkTransformAnchor for syncing parenting + +**Bug Fixes** + +- Fixed: Longs and ulongs [Networked] properties causing ILWeaver error + +### Build 235 (Jul 10, 2021) + +**Bug Fixes** + +- Fixed: NetworkBehaviour's inspector buttons for adding a missing NetworkObject will now allow undo, and should dirty correctly in all use cases + +### Build 234 (Jul 09, 2021) + +**What's New** + +- Runner.GameInfo +This property exposes some Photon Cloud metadata like Room Name and current connected Region +- RpcAttribute.TickAligned - if set to false the Rpc will be handled without waiting for a target's tick to catch up, if there are not any tick aligned Rpcs waiting (order is preserved) + +**Bug Fixes** + +- Fixed: Longs and ulongs as Rpc parameters causing ILWeaver error +- Fixed: Fusion windows should now remember their state when Unity is restarted + +### Build 233 (Jul 08, 2021) + +**Bug Fixes** + +- Fixed: ILWeaver error when deriving from NetworkTransformAnchor + +### Build 232 (Jul 07, 2021) + +**Bug Fixes** + +- Fixed: Transient despawned objects are now ignored on lag compensated queries + +### Build 231 (Jul 06, 2021) + +**Bug Fixes** + +- Fixed: FusionStats are now created last in NetworkDebugStart, and check to see if any active EventSystems exist. Fixes issues with 'multiple event systems' warnings spamming the logs + +### Build 229 (Jul 02, 2021) + +**Changes** + +- NetworkBehaviour.Defaults now take two parameters: afterSpawned and isLocalSpawn +- Renamed AppIdRealtime to AppIdFusion in Photon App Settings scriptable object +- Photon Realtime SDK to v4.1.6.3 + +**Bug Fixes** + +- Fixed: Issues with transform.position not being consistent in Spawned callback between server/client in server mode, and also not being consistent in client auth mode + +### Build 228 (Jul 01, 2021) + +**Bug Fixes** + +- Fixed: Issue with [Accuracy(0)] on Vector2/Vector3/Quaternion +- Fixed: Bug with objects spawned by re-used player indices not showing up for other clients in shared mode +- Fixed: Weaver issue with static fields on structs implementing INetworkStruct + +### Build 227 (Jun 30, 2021) + +**Breaking Changes** + +- Added ShutdownReason to OnShutdown Event +You must modify any INetworkRunnerCallbacks to include the extra param on the OnShutdown callback + +**What's New** + +- Added shortcuts for 'Rebuild Object Table' and 'Import Scenes From Build Settings' to the Fusion menu in Unity +- Added AlwaysShowStats boolean setting on NetworkDebugStart +- Added area of interest support for shared mode +- Added transform parenting support to NetworkTransform/NetworkRigidbody/NetworkCharacterController +- Added Teleport to NetworkRigidbody and NetworkCharacterController + +**Changes** + +- Renamed NetworkAreaOfInterestBehaviour.PositionOffset to NetworkAreaOfInterestBehaviour.PositionWordOffset + +### Build 226 (Jun 29, 2021) + +**What's New** + +- Added NetworkRunner.RequestStateAuthority and NetworkRunner.ReleaseStateAuthority + +**Bug Fixes** + +- Fixed: Object destruction when clients leave in shared mode + +### Build 223 (Jun 25, 2021) + +**Bug Fixes** + +- Fixed: "Could not load NetworkProjectConfigAsset" errors after importing package on some Unity versions + +### Build 222 (Jun 24, 2021) + +**What's New** + +- Added NetworkObject.RemoveInputAuthority and NetworkObject.RemoveStateAuthority + +**Bug Fixes** + +- Fixed: Weaver will now longer throw an exception when Accuracy is specified with a double + +### Build 221 (Jun 23, 2021) + +**Changes** + +- Callback interfaces like ISceneLoaded, IPlayerJoined, IBeforeAllTicks, etc. will no longer be invoked on behaviours which are not enabled + +**Bug Fixes** + +- Fixed: An issues in the Lag-Compensated system when adding the first HitboxRoot object to a Manager that started with no initial objects +- Fixed: Issue with NetworkRigidbody throwing errors when physics mode is set to None + +### Build 220 (Jun 22, 2021) + +**What's New** + +- RaycastAll to lag compensated queries + +**Changes** + +- NetworkRigidbody now automatically disabled the built in unity rigidbody interpolation, if NetworkRigidbody is used fusion takes care of all interpolation for that rigidbody +- Changed order of SyncDragMass and InterpolationTarget in inspector for NetworkRigidbody +- Networked Properties in inspector now use a default closed foldout, monitoring these values can cause performance issues in the editor with complex NetworkBehaviours + +**Bug Fixes** + +- Fixed: SinglePlayer Mode +- Fixed: IL2CPP memory alignment issue on android + +### Build 218 (Jun 19, 2021) + +**Changes** + +- Runtime and Baked info exposed in the NetworkObject inspector + +### Build 217 (Jun 18, 2021) + +**What's New** + +- Lag compensated query for past pos/rot of any specific hitbox (two overloads: based on tick, or on player reference view) + +**Changes** + +- Improved documentation and ease of interpolation configuration options + +**Bug Fixes** + +- Fixed: All issues with nested INetworkInput and INetworkStruct types +- Fixed: Minor issue in interpolation time jitter delta calculation where a small amount of delta time would sometimes be discarded + +### Build 216 (Jun 17, 2021) + +**What's New** + +- Implemented new ingame stats + +**Changes** + +- NetworkDebugStart Auto mode will never show menu. Use Manual or UserInterface settings to start clients + +**Bug Fixes** + +- Fixed: MaxPlayers connected to a Dedicated Server +- Fixed: NetworkDebugStart menu for StartClients and StartClient working again +- Fixed: Bug where client side prediction time would get stuck in very high prediction offset in rare cases +- Fixed: Bug where client side prediction time would get stuck in very high prediction offset in rare cases +- Fixed: Assert bug in SmoothRandom constructor + +### Build 215 (Jun 16, 2021) + +**Bug Fixes** + +- Fixed: Issue with NetworkBehaviourUtils marked as private causing issues when weaving certain RPCs + +### Build 214 (Jun 15, 2021) + +**What's New** + +- Added NetworkRunner.GetAllBehaviours API to get all active network/simulation behaviours of a certain type +- IRunnerVisibilityRecognizedType can be added to Monobehaviours to flag them for inclusion in the RunnerVisibility system + +**Changes** + +- Obsoleted GetActiveSimulationBehaviours for GetAllBehaviours + +**Bug Fixes** + +- Fixed: SinglePlayer mode start + +### Build 213 (Jun 14, 2021) + +**Bug Fixes** + +- Fixed: Networked Properties UI added handling for [Neworked] pointer types + +### Build 211 (Jun 11, 2021) + +**Bug Fixes** + +- Fixed: Nanosockets for macOS +- Fixed: Singleplayer game sdtart + +### Build 208 (Jun 09, 2021) + +- Initial beta release + +**Changes** + +- Cleared alpha changelog + diff --git a/Assets/Photon/Fusion/release_history.txt.meta b/Assets/Photon/Fusion/release_history.txt.meta new file mode 100644 index 0000000..423d882 --- /dev/null +++ b/Assets/Photon/Fusion/release_history.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5705adcaad7c31b439ed88af045846a1 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables.meta b/Assets/Photon/FusionAddressables.meta new file mode 100644 index 0000000..5e9f0bc --- /dev/null +++ b/Assets/Photon/FusionAddressables.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8ea22e60478763f4b9cd9d3309c64c62 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef b/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef new file mode 100644 index 0000000..a10933a --- /dev/null +++ b/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef @@ -0,0 +1,30 @@ +{ + "name": "Fusion.Addressables", + "rootNamespace": "", + "references": [ + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:69448af7b92c7f342b298e06a37122aa", + "GUID:4d449a4732e064d26a3069f2cfd88917", + "GUID:84651a3751eca9349aac36a66bba901b" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.addressables", + "expression": "1.0", + "define": "FUSION_USE_ADDRESSABLES" + }, + { + "name": "com.unity.addressables", + "expression": "1.8", + "define": "FUSION_ADDRESSABLES_HAVE_MODIFICATION_EVENTS" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef.meta b/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef.meta new file mode 100644 index 0000000..c6c96a6 --- /dev/null +++ b/Assets/Photon/FusionAddressables/Fusion.Addressables.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5e4a091669a32a04b82201f8aae7e8fa +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs b/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs new file mode 100644 index 0000000..7ef3984 --- /dev/null +++ b/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using UnityEngine; + +#if FUSION_USE_ADDRESSABLES +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +#endif + +namespace Fusion { + public class FusionAddressablePrefabsPreloader : MonoBehaviour { +#if FUSION_USE_ADDRESSABLES + private List> _handles = new List>(); + + private async System.Threading.Tasks.Task Start() { + var config = NetworkProjectConfig.Global; + + // there are a few ways to load an asset with Addressables (by label, by IResourceLocation, by address etc.) + // but it seems that they're not fully interchangeable, i.e. loading by label will not make loading by address + // be reported as done immediately; hence the only way to preload an asset for Quantum is to replicate + // what it does internally, i.e. load with the very same parameters + + foreach (var (id, source) in config.PrefabTable.GetEntries()) { + if (source is NetworkPrefabSourceUnityAddressable addressable) { + // we can't just LoadAssetAsync() because source does it, too: + // https://forum.unity.com/threads/1-15-1-assetreference-not-allow-loadassetasync-twice.959910/ + var key = addressable.Address.RuntimeKey; + var handle = Addressables.LoadAssetAsync(key); + await handle.Task; + _handles.Add(handle); + } + } + } + + private void OnDestroy() { + foreach (var handle in _handles) { + Addressables.Release(handle); + } + } +#endif + } +} \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs.meta b/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs.meta new file mode 100644 index 0000000..87e95a1 --- /dev/null +++ b/Assets/Photon/FusionAddressables/FusionAddressablePrefabsPreloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ccb29b1c478478c418765bc307a473ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs new file mode 100644 index 0000000..98586bc --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs @@ -0,0 +1 @@ +// removed \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs.meta b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs.meta new file mode 100644 index 0000000..629f237 --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfo.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4bb61b388ba7b94bbb37179beea0cfe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs new file mode 100644 index 0000000..98586bc --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs @@ -0,0 +1 @@ +// removed \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs.meta b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs.meta new file mode 100644 index 0000000..11b23c2 --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkAddressablePrefabLoadInfoFactory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2f607afeb06ca04a9a94dfc0821de97 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs b/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs new file mode 100644 index 0000000..98586bc --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs @@ -0,0 +1 @@ +// removed \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs.meta b/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs.meta new file mode 100644 index 0000000..4c2bafc --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceAddressables.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f9f9621d687f4b44a0e8d88ae2b6818 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs b/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs new file mode 100644 index 0000000..98586bc --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs @@ -0,0 +1 @@ +// removed \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs.meta b/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs.meta new file mode 100644 index 0000000..d79241c --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceFactoryAddressables.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85d078ca49dae1f439647846895553c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs b/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs new file mode 100644 index 0000000..b930c21 --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs @@ -0,0 +1,143 @@ +#if FUSION_USE_ADDRESSABLES + +using System; +using System.Collections.Generic; +using System.Linq; + +#if UNITY_EDITOR +using Fusion.Editor; +using UnityEditor; +using UnityEditor.AddressableAssets; +using UnityEditor.AddressableAssets.Settings; +#endif + +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Fusion { + + public class NetworkPrefabSourceUnityAddressable : NetworkPrefabSourceUnityBase { + + public AssetReferenceGameObject Address; + + public override string EditorSummary => $"[Address: {Address}]"; + + public override void Load(in NetworkPrefabLoadContext context) { + Debug.Assert(!Address.OperationHandle.IsValid()); + var op = Address.LoadAssetAsync(); + if (op.IsDone) { + context.Loaded(op.Result); + } else { + if (context.HasFlag(NetworkPrefabLoadContext.FLAGS_PREFER_ASYNC)) { + var c = context; + op.Completed += (_op) => { + c.Loaded(_op.Result); + }; + } else { + var result = op.WaitForCompletion(); + context.Loaded(result); + } + } + } + + public override void Unload() { + Address.ReleaseAsset(); + } + } + +#if UNITY_EDITOR + public class NetworkPrefabAssetFactoryAddressable : INetworkPrefabSourceFactory { + public const int DefaultOrder = 800; + + private ILookup _lookup = default; + + private ILookup Lookup { + get { + if (_lookup == null) { + _lookup = CreateAddressablesLookup(); + EditorApplication.delayCall += () => _lookup = null; + } + return _lookup; + } + } + + int INetworkPrefabSourceFactory.Order => DefaultOrder; + Type INetworkPrefabSourceFactory.SourceType => typeof(NetworkPrefabSourceUnityAddressable); + + NetworkPrefabSourceUnityBase INetworkPrefabSourceFactory.TryCreate(string assetPath) { + var guid = AssetDatabase.AssetPathToGUID(assetPath); + var addressableEntry = Lookup[guid].SingleOrDefault(); + if (addressableEntry != null) { + var result = ScriptableObject.CreateInstance(); + result.Address = new AssetReferenceGameObject(addressableEntry.guid); + return result; + } + return null; + } + + UnityEngine.GameObject INetworkPrefabSourceFactory.EditorResolveSource(NetworkPrefabSourceUnityBase prefabAsset) { + return ((NetworkPrefabSourceUnityAddressable)prefabAsset).Address.editorAsset; + } + + private static ILookup CreateAddressablesLookup() { + var assetList = new List(); + var assetsSettings = AddressableAssetSettingsDefaultObject.Settings; + if (assetsSettings != null) { + foreach (var settingsGroup in assetsSettings.groups) { + if (settingsGroup.ReadOnly) + continue; + settingsGroup.GatherAllAssets(assetList, true, true, true); + } + } + + return assetList.Where(x => !string.IsNullOrEmpty(x.guid)).ToLookup(x => x.guid); + } + +#if FUSION_ADDRESSABLES_HAVE_MODIFICATION_EVENTS + [InitializeOnLoadMethod] + static void Initialize() { + + void RefreshConfig() { + var importer = NetworkProjectConfigUtilities.GlobalConfigImporter; + if (importer != null) { + importer.SaveAndReimport(); + } + } + + AddressableAssetSettings.OnModificationGlobal += (settings, modificationEvent, data) => { + switch (modificationEvent) { + case AddressableAssetSettings.ModificationEvent.EntryAdded: + case AddressableAssetSettings.ModificationEvent.EntryCreated: + case AddressableAssetSettings.ModificationEvent.EntryModified: + case AddressableAssetSettings.ModificationEvent.EntryMoved: + + IEnumerable entries; + if (data is AddressableAssetEntry singleEntry) { + entries = Enumerable.Repeat(singleEntry, 1); + } else { + entries = (IEnumerable)data; + } + + List allEntries = new List(); + foreach (var entry in entries) { + entry.GatherAllAssets(allEntries, true, true, true); + if (allEntries.Any(x => AssetDatabaseUtils.HasLabel(x.AssetPath, "FusionPrefab"))) { + RefreshConfig(); + break; + } + allEntries.Clear(); + } + break; + + case AddressableAssetSettings.ModificationEvent.EntryRemoved: + RefreshConfig(); + break; + }; + }; + } +#endif + } +#endif +} + +#endif \ No newline at end of file diff --git a/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs.meta b/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs.meta new file mode 100644 index 0000000..99e70b9 --- /dev/null +++ b/Assets/Photon/FusionAddressables/NetworkPrefabSourceUnityAddressable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0905136065ef6c4d8cc261872480d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionCodeGen.meta b/Assets/Photon/FusionCodeGen.meta new file mode 100644 index 0000000..c48c343 --- /dev/null +++ b/Assets/Photon/FusionCodeGen.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 362ea1b96d6cac647abaa4e15eb542a9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger new file mode 100644 index 0000000..7c4a013 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger @@ -0,0 +1 @@ +aaa \ No newline at end of file diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger.meta b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger.meta new file mode 100644 index 0000000..297bb30 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.Trigger.fusionweavertrigger.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f4bd6c24648e05c4fa838da70f847a76 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {instanceID: 0} diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs new file mode 100644 index 0000000..1b877f5 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs @@ -0,0 +1,9 @@ +#if FUSION_WEAVER && FUSION_WEAVER_ILPOSTPROCESSOR +namespace Fusion.CodeGen { + static partial class ILWeaverSettings { + + static partial void OverrideNetworkProjectConfigPath(ref string path) { + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs.meta b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs.meta new file mode 100644 index 0000000..b6f752e --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.User.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f9faa3ffc014c941995a81138385040 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef new file mode 100644 index 0000000..2980929 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef @@ -0,0 +1,40 @@ +{ + "name": "Unity.Fusion.CodeGen", + "rootNamespace": "Fusion.CodeGen", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": true, + "precompiledReferences": [ + "Mono.Cecil.dll", + "Mono.Cecil.Rocks.dll", + "Mono.Cecil.Pdb.dll", + "Fusion.Runtime.dll", + "Fusion.Common.dll", + "Fusion.Realtime.dll", + "Fusion.Sockets.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [ + { + "name": "Unity", + "expression": "0", + "define": "FUSION_WEAVER_ILPOSTPROCESSOR" + }, + { + "name": "com.unity.nuget.mono-cecil", + "expression": "1", + "define": "FUSION_HAS_MONO_CECIL" + }, + { + "name": "nuget.mono-cecil", + "expression": "0.1", + "define": "FUSION_HAS_MONO_CECIL" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef.meta b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef.meta new file mode 100644 index 0000000..c09f525 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d9d8635f8670c0c44ba7a8140f7ac7b9 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs new file mode 100644 index 0000000..95315a8 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs @@ -0,0 +1,6309 @@ +#if !FUSION_DEV + +#region Assets/Photon/FusionCodeGen/AssemblyInfo.cs + +[assembly: Fusion.NetworkAssemblyIgnore] + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaver.Cache.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Runtime.CompilerServices; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + using static Fusion.CodeGen.ILWeaverOpCodes; + + partial class ILWeaver { + + private Dictionary _fixedBuffers = new Dictionary(); + private Dictionary _readerWriters = new Dictionary(); + private Dictionary _unitySurrogateTypes = new Dictionary(); + private Dictionary _blittableType = new Dictionary(); + private ElementReaderWriterInfo _instanceReaderWriter = new ElementReaderWriterInfo() { + InitializeInstance = il => { }, + LoadInstance = il => il.Append(Ldarg_0()), + }; + + private TypeReference CacheGetBlittableType(ILWeaverAssembly asm, PropertyDefinition property, TypeReference elementType) { + + if (_blittableType.TryGetValue(elementType.FullName, out var blittableType)) { + return blittableType; + } else if (elementType.IsFloat() || elementType.IsVector2() || elementType.IsVector3()) { + blittableType = new TypeDefinition("Fusion.CodeGen", $"Blittable{elementType.Name}@{elementType.Name}", + TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.Serializable | TypeAttributes.BeforeFieldInit, + asm.ValueType); + + blittableType.AddTo(asm.CecilAssembly); + blittableType.AddInterface(asm); + blittableType.AddAttribute(asm, GetTypeWordCount(asm, elementType)); + + if (elementType.IsFloat()) { + blittableType.Fields.Add(new FieldDefinition("Value", FieldAttributes.Public, asm.WordSizedPrimitive)); + } else if (elementType.IsVector2()) { + blittableType.Fields.Add(new FieldDefinition("X", FieldAttributes.Public, asm.WordSizedPrimitive)); + blittableType.Fields.Add(new FieldDefinition("Y", FieldAttributes.Public, asm.WordSizedPrimitive)); + } else if (elementType.IsVector3()) { + blittableType.Fields.Add(new FieldDefinition("X", FieldAttributes.Public, asm.WordSizedPrimitive)); + blittableType.Fields.Add(new FieldDefinition("Y", FieldAttributes.Public, asm.WordSizedPrimitive)); + blittableType.Fields.Add(new FieldDefinition("Z", FieldAttributes.Public, asm.WordSizedPrimitive)); + } + + for (int i = 0; i < blittableType.Fields.Count; ++i) { + blittableType.Fields[i].Offset = i * Allocator.REPLICATE_WORD_SIZE; + } + + _blittableType.Add(elementType.FullName, blittableType); + return blittableType; + } else { + return null; + } + } + + private FixedBufferInfo CacheGetFixedBuffer(ILWeaverAssembly asm, int wordCount) { + if (!_fixedBuffers.TryGetValue(wordCount, out var entry)) { + // fixed buffers could be included directly in structs, but then again it would be impossible to provide a custom drawer; + // that's why there's this proxy struct + var storageType = new TypeDefinition("Fusion.CodeGen", $"FixedStorage@{wordCount}", + TypeAttributes.ExplicitLayout | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit | TypeAttributes.Serializable, + asm.ValueType); + + storageType.AddTo(asm.CecilAssembly); + storageType.AddInterface(asm); + storageType.AddAttribute(asm, wordCount); + + FieldDefinition bufferField; + if (Allocator.REPLICATE_WORD_SIZE == sizeof(int)) { + bufferField = CreateFixedBufferField(asm, storageType, $"Data", asm.Import(typeof(int)), wordCount); + bufferField.Offset = 0; + + // Unity debugger seems to copy only the first element of a buffer, + // the rest is garbage when inspected; let's add some additional + // fields to help it + for (int i = 1; i < wordCount; ++i) { + var unityDebuggerWorkaroundField = new FieldDefinition($"_{i}", FieldAttributes.Private | FieldAttributes.NotSerialized, asm.Import()); + unityDebuggerWorkaroundField.Offset = Allocator.REPLICATE_WORD_SIZE * i; + unityDebuggerWorkaroundField.AddTo(storageType); + } + + } + + entry = new FixedBufferInfo() { + Type = storageType, + PointerField = bufferField + }; + + _fixedBuffers.Add(wordCount, entry); + } + return entry; + } + + static FieldDefinition CreateFixedBufferField(ILWeaverAssembly asm, TypeDefinition type, string fieldName, TypeReference elementType, int elementCount) { + var fixedBufferType = new TypeDefinition("", $"<{fieldName}>e__FixedBuffer", TypeAttributes.SequentialLayout | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit | TypeAttributes.NestedPublic) { + BaseType = asm.Import(typeof(ValueType)), + PackingSize = 0, + ClassSize = elementCount * elementType.GetPrimitiveSize(), + }; + fixedBufferType.AddAttribute(asm); + fixedBufferType.AddAttribute(asm); + fixedBufferType.AddTo(type); + + var elementField = new FieldDefinition("FixedElementField", FieldAttributes.Private, elementType); + elementField.AddTo(fixedBufferType); + + var field = new FieldDefinition(fieldName, FieldAttributes.Public, fixedBufferType); + field.AddAttribute(asm, elementType, elementCount); + field.AddTo(type); + + return field; + } + + + private ElementReaderWriterInfo MakeElementReaderWriter(ILWeaverAssembly asm, PropertyDefinition property, TypeReference elementType) { + + var interfaceType = asm.Import(typeof(IElementReaderWriter<>)).MakeGenericInstanceType(elementType); + + if (TryGetNetworkWrapperType(elementType, out var wrapInfo)) { + if (!property.DeclaringType.Is()) { + throw new ILWeaverException($"{elementType} needs wrapping - such types are only supported as NetworkBehaviour properties."); + } + + var wordCount = GetTypeWordCount(asm, elementType); + + // let's add an interface! + var behaviour = property.DeclaringType; + if (!behaviour.Interfaces.Any(x => x.InterfaceType.FullName == interfaceType.FullName)) { + Log.Debug($"Adding interface {behaviour} {interfaceType}"); + AddIElementReaderWriterImplementation(asm, behaviour, property, elementType, wordCount, isExplicit: true); + } + return _instanceReaderWriter; + + } else { + var readerWriterName = "ReaderWriter@" + elementType.FullName.Replace(".", "_"); + + int wordCount; + + if (property.PropertyType.IsString()) { + wordCount = GetPropertyWordCount(asm, property); + readerWriterName += $"@{wordCount}"; + } else { + wordCount = GetTypeWordCount(asm, elementType); + if (TryGetFloatAccuracy(property, out var accuracy)) { + uint value = BitConverter.ToUInt32(BitConverter.GetBytes(accuracy), 0); + readerWriterName += $"@{value:x}"; + } + } + + if (_readerWriters.TryGetValue(readerWriterName, out var entry)) { + return entry; + } + + var readerWriterType = new TypeDefinition("Fusion.CodeGen", readerWriterName, + TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.BeforeFieldInit, asm.ValueType); + + // without this, VS debugger will crash + readerWriterType.PackingSize = 0; + readerWriterType.ClassSize = 1; + + readerWriterType.AddTo(asm.CecilAssembly); + + AddIElementReaderWriterImplementation(asm, readerWriterType, property, elementType, wordCount); + + var instanceField = new FieldDefinition("Instance", FieldAttributes.Public | FieldAttributes.Static, interfaceType); + instanceField.AddTo(readerWriterType); + + var initializeMethod = new MethodDefinition($"EnsureInitialized", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, asm.Import(typeof(void))); + initializeMethod.AddAttribute(asm, MethodImplOptions.AggressiveInlining); + initializeMethod.AddTo(readerWriterType); + + { + var il = initializeMethod.Body.GetILProcessor(); + var ret = Instruction.Create(OpCodes.Ret); + + var tmpVar = new VariableDefinition(readerWriterType); + il.Body.Variables.Add(tmpVar); + + il.Append(Ldsfld(instanceField)); + il.Append(Brtrue_S(ret)); + + il.Append(Instruction.Create(OpCodes.Ldloca_S, tmpVar)); + il.Append(Instruction.Create(OpCodes.Initobj, readerWriterType)); + il.Append(Instruction.Create(OpCodes.Ldloc_0)); + il.Append(Instruction.Create(OpCodes.Box, readerWriterType)); + il.Append(Instruction.Create(OpCodes.Stsfld, instanceField)); + + il.Append(ret); + } + + entry = new ElementReaderWriterInfo() { + InitializeInstance = il => il.Append(Call(initializeMethod)), + LoadInstance = il => il.Append(Ldsfld(instanceField)), + Type = readerWriterType + }; + + _readerWriters.Add(readerWriterName, entry); + return entry; + } + } + + private void AddIElementReaderWriterImplementation(ILWeaverAssembly asm, TypeDefinition readerWriterType, PropertyDefinition property, TypeReference elementType, int elementWordCount, bool isExplicit = false) { + + var dataType = asm.Import(typeof(byte*)); + var indexType = asm.Import(typeof(int)); + var interfaceType = asm.Import(typeof(IElementReaderWriter<>)).MakeGenericInstanceType(elementType); + + readerWriterType.Interfaces.Add(new InterfaceImplementation(interfaceType)); + + var visibility = isExplicit ? MethodAttributes.Private : MethodAttributes.Public; + var namePrefix = isExplicit ? $"CodeGen@ElementReaderWriter<{elementType.FullName}>." : ""; + + var readMethod = new MethodDefinition($"{namePrefix}Read", + visibility | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + elementType); + + readMethod.Parameters.Add(new ParameterDefinition("data", ParameterAttributes.None, dataType)); + readMethod.Parameters.Add(new ParameterDefinition("index", ParameterAttributes.None, indexType)); + readMethod.AddAttribute(asm, MethodImplOptions.AggressiveInlining); + readMethod.AddTo(readerWriterType); + + var writeMethod = new MethodDefinition($"{namePrefix}Write", + visibility | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + asm.Void); + + writeMethod.Parameters.Add(new ParameterDefinition("data", ParameterAttributes.None, dataType)); + writeMethod.Parameters.Add(new ParameterDefinition("index", ParameterAttributes.None, indexType)); + writeMethod.Parameters.Add(new ParameterDefinition("val", ParameterAttributes.None, elementType)); + writeMethod.AddAttribute(asm, MethodImplOptions.AggressiveInlining); + writeMethod.AddTo(readerWriterType); + + var getElementWordCountMethod = new MethodDefinition($"{namePrefix}GetElementWordCount", + visibility | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual, + asm.Import(typeof(int))); + + getElementWordCountMethod.AddAttribute(asm, MethodImplOptions.AggressiveInlining); + + if (isExplicit) { + readMethod.Overrides.Add(interfaceType.GetGenericInstanceMethodOrThrow(nameof(IElementReaderWriter.Read))); + writeMethod.Overrides.Add(interfaceType.GetGenericInstanceMethodOrThrow(nameof(IElementReaderWriter.Write))); + getElementWordCountMethod.Overrides.Add(interfaceType.GetGenericInstanceMethodOrThrow(nameof(IElementReaderWriter.GetElementWordCount))); + } + + var getterMethodIL = readMethod.Body.GetILProcessor(); + var setterMethodIL = writeMethod.Body.GetILProcessor(); + + InjectValueAccessor(asm, getterMethodIL, setterMethodIL, property, elementType, OpCodes.Ldarg_3, (il, offset) => { + LoadArrayElementAddress(il, OpCodes.Ldarg_1, OpCodes.Ldarg_2, elementWordCount, offset); + }, false); + + getElementWordCountMethod.AddTo(readerWriterType); + { + var il = getElementWordCountMethod.Body.GetILProcessor(); + il.Append(Ldc_I4(elementWordCount)); + il.Append(Ret()); + } + } + + + private string TypeNameToIdentifier(TypeReference type, string prefix) { + string result = type.FullName; + result = result.Replace("`1", ""); + result = result.Replace("`2", ""); + result = result.Replace("`3", ""); + result = result.Replace(".", "_"); + result = prefix + result; + return result; + } + + private TypeDefinition CacheGetUnitySurrogate(ILWeaverAssembly asm, PropertyDefinition property) { + var type = property.PropertyType; + + GenericInstanceType baseType; + string surrogateName; + + if (type.IsNetworkDictionary(out var keyType, out var valueType)) { + keyType = asm.Import(keyType); + valueType = asm.Import(valueType); + var keyReaderWriterType = MakeElementReaderWriter(asm, property, keyType).Type; + var valueReaderWriterType = MakeElementReaderWriter(asm, property, valueType).Type; + baseType = asm.Import(typeof(Fusion.Internal.UnityDictionarySurrogate<,,,>)).MakeGenericInstanceType(keyType, keyReaderWriterType, valueType, valueReaderWriterType); + surrogateName = "UnityDictionarySurrogate@" + keyReaderWriterType.Name + "@" + valueReaderWriterType.Name; + } else if (type.IsNetworkArray(out var elementType)) { + elementType = asm.Import(elementType); + var readerWriterType = MakeElementReaderWriter(asm, property, elementType).Type; + baseType = asm.Import(typeof(Fusion.Internal.UnityArraySurrogate<,>)).MakeGenericInstanceType(elementType, readerWriterType); + surrogateName = "UnityArraySurrogate@" + readerWriterType.Name; + } else if (type.IsNetworkList(out elementType)) { + elementType = asm.Import(elementType); + var readerWriterType = MakeElementReaderWriter(asm, property, elementType).Type; + baseType = asm.Import(typeof(Fusion.Internal.UnityLinkedListSurrogate<,>)).MakeGenericInstanceType(elementType, readerWriterType); + surrogateName = "UnityLinkedListSurrogate@" + readerWriterType.Name; + } else { + var readerWriterType = MakeElementReaderWriter(asm, property, property.PropertyType).Type; + baseType = asm.Import(typeof(Fusion.Internal.UnityValueSurrogate<,>)).MakeGenericInstanceType(property.PropertyType, readerWriterType); + surrogateName = "UnityValueSurrogate@" + readerWriterType.Name; + } + + if (!_unitySurrogateTypes.TryGetValue(surrogateName, out var surrogateType)) { + surrogateType = new TypeDefinition("Fusion.CodeGen", surrogateName, + TypeAttributes.NotPublic | TypeAttributes.AnsiClass | TypeAttributes.Serializable | TypeAttributes.BeforeFieldInit, + baseType); + + surrogateType.AddTo(asm.CecilAssembly); + surrogateType.AddEmptyConstructor(asm); + + _unitySurrogateTypes.Add(surrogateName, surrogateType); + } + return surrogateType; + } + + struct ElementReaderWriterInfo { + public Action InitializeInstance; + public Action LoadInstance; + public TypeDefinition Type; + } + + struct FixedBufferInfo { + public FieldDefinition PointerField; + public TypeDefinition Type; + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaver.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.Compilation; + using UnityEngine; + using System.Runtime.CompilerServices; + using static Fusion.CodeGen.ILWeaverOpCodes; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + using Mono.Collections.Generic; + using CompilerAssembly = UnityEditor.Compilation.Assembly; + using FieldAttributes = Mono.Cecil.FieldAttributes; + using MethodAttributes = Mono.Cecil.MethodAttributes; + using ParameterAttributes = Mono.Cecil.ParameterAttributes; + + public unsafe partial class ILWeaver { + + + void AddUnmanagedType() where T : unmanaged { + AddUnmanagedType(sizeof(T)); + } + + void AddUnmanagedType(int size) where T : unmanaged { + unsafe { + var wordCount = Native.WordCount(size, Allocator.REPLICATE_WORD_SIZE); + _typeData.Add(typeof(T).FullName, new TypeMetaData(wordCount)); + } + } + + void SetDefaultTypeData() { + _networkedBehaviourTypeData = new Dictionary(); + _typeData = new Dictionary(); + _rpcCount = new Dictionary(); + + _typeData.Add("NetworkedObject", new TypeMetaData(2)); + + AddUnmanagedType(sizeof(int)); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + AddUnmanagedType(); + AddUnmanagedType(); + + AddUnmanagedType(); + } + + public static bool IsEditorAssemblyPath(string path) { + return path.Contains("-Editor") || path.Contains(".Editor"); + } + + struct WrapInfo { + public MethodDefinition WrapMethod; + public MethodDefinition UnwrapMethod; + public TypeReference WrapperType; + public TypeReference Type; + public int MaxRawByteCount; + public bool UnwrapByRef; + public bool IsRaw => MaxRawByteCount > 0; + } + + bool TryGetNetworkWrapperType(TypeReference type, out WrapInfo result) { + if (type == null) { + result = default; + return false; + } + + var definition = type.TryResolve(); + if (definition == null) { + result = default; + return false; + } + + int rawByteCount = 0; + + if (definition.GetSingleOrDefaultMethodWithAttribute(out var wrapAttribute, out var wrapMethod)) { + wrapAttribute.TryGetAttributeProperty(nameof(NetworkSerializeMethodAttribute.MaxSize), out rawByteCount); + + if (rawByteCount > 0) { + try { + wrapMethod.ThrowIfNotStatic() + .ThrowIfNotPublic() + .ThrowIfParameterCount(3) + .ThrowIfParameter(0, typeof(NetworkRunner)) + .ThrowIfParameter(1, type) + .ThrowIfParameter(2, typeof(byte*)) + .ThrowIfReturnType(typeof(int)); + } catch (Exception ex) { + throw new ILWeaverException($"Method marked with {nameof(NetworkSerializeMethodAttribute)} has an invalid signature RAW", ex); + } + } else { + try { + wrapMethod.ThrowIfNotStatic() + .ThrowIfNotPublic() + .ThrowIfParameterCount(2) + .ThrowIfParameter(0, typeof(NetworkRunner)) + .ThrowIfParameter(1, type); + } catch (Exception ex) { + throw new ILWeaverException($"Method marked with {nameof(NetworkSerializeMethodAttribute)} has an invalid signature", ex); + } + } + } + + bool cacheUnwrap = false; + + if (definition.GetSingleOrDefaultMethodWithAttribute(out var unwrapAttribute, out var unwrapMethod)) { + if (wrapMethod == null) { + throw new ILWeaverException($"Method marked with {nameof(NetworkDeserializeMethodAttribute)}, but there is no method marked with {nameof(NetworkSerializeMethodAttribute)}: {unwrapMethod}"); + } + + if (rawByteCount > 0) { + try { + unwrapMethod.ThrowIfNotStatic() + .ThrowIfNotPublic() + .ThrowIfReturnType(typeof(int)) + .ThrowIfParameterCount(3) + .ThrowIfParameter(0, typeof(NetworkRunner)) + .ThrowIfParameter(1, typeof(byte*)) + .ThrowIfParameter(2, type, isByReference: true); + cacheUnwrap = true; + } catch (Exception ex) { + throw new ILWeaverException($"Method marked with {nameof(NetworkDeserializeMethodAttribute)} has an invalid signature RAW", ex); + } + } else { + try { + unwrapMethod.ThrowIfNotStatic() + .ThrowIfNotPublic() + .ThrowIfParameter(0, typeof(NetworkRunner)) + .ThrowIfParameter(1, wrapMethod.ReturnType); + + if (unwrapMethod.Parameters.Count == 3) { + unwrapMethod.ThrowIfReturnType(typeof(void)) + .ThrowIfParameter(2, type, isByReference: true); + cacheUnwrap = true; + } else if (unwrapMethod.Parameters.Count == 2) { + unwrapMethod.ThrowIfReturnType(type); + } else { + throw new ILWeaverException($"Expected 2 or 3 parameters"); + } + } catch (Exception ex) { + throw new ILWeaverException($"Method marked with {nameof(NetworkDeserializeMethodAttribute)} has an invalid signature", ex); + } + + } + } else if (wrapMethod != null) { + throw new ILWeaverException($"Method marked with {nameof(NetworkSerializeMethodAttribute)}, but there is no method marked with {nameof(NetworkDeserializeMethodAttribute)}: {wrapMethod}"); + } + + + if (wrapMethod != null && unwrapMethod != null) { + result = new WrapInfo() { + MaxRawByteCount = rawByteCount, + UnwrapMethod = unwrapMethod, + WrapMethod = wrapMethod, + WrapperType = rawByteCount > 0 ? null : wrapMethod.ReturnType, + Type = type, + UnwrapByRef = cacheUnwrap, + }; + return true; + } + + return TryGetNetworkWrapperType(definition.BaseType, out result); + } + + TypeDefinition FindNetworkedBehaviourTypeDef(TypeDefinition type) { + if (type == null || type.IsClass == false || type.BaseType == null) { + return null; + } + + if (type.BaseType.Name == nameof(NetworkBehaviour)) { + return type.BaseType.TryResolve(); + } + + return FindNetworkedBehaviourTypeDef(type.BaseType.TryResolve()); + } + + class TypeMetaData { + public TypeReference Reference; + public TypeDefinition Definition; + public int WordCount; + + public TypeMetaData() { + } + + public TypeMetaData(int wordCount) { + WordCount = wordCount; + } + } + + class BehaviourMetaData { + public TypeDefinition Definition; + public int BlockCount; + } + + + + const string REF_FIELD_NAME = "Ref"; + const string PTR_FIELD_NAME = nameof(NetworkBehaviour.Ptr); + const string OBJECT_FIELD_NAME = nameof(NetworkBehaviour.Object); + const string RUNNER_FIELD_NAME = nameof(NetworkBehaviour.Runner); + + const string FIND_OBJECT_METHOD_NAME = nameof(NetworkRunner.FindObject); + + Dictionary _typeData = new Dictionary(); + Dictionary _networkedBehaviourTypeData = new Dictionary(); + Dictionary _rpcCount = new Dictionary(); + + + internal readonly ILWeaverLog Log; + + public ILWeaver(ILWeaverLog log) { + Log = log; + SetDefaultTypeData(); + } + + + + FieldReference GetFieldFromNetworkedBehaviour(ILWeaverAssembly assembly, TypeDefinition type, string fieldName) { + if (type.Name == nameof(NetworkBehaviour)) { + foreach (var fieldDefinition in type.Fields) { + if (fieldDefinition.Name == fieldName) { + return assembly.CecilAssembly.MainModule.ImportReference(fieldDefinition); + } + } + } + + if (type.IsSubclassOf()) { + return GetFieldFromNetworkedBehaviour(assembly, type.BaseType.TryResolve(), fieldName); + } + + Assert.Fail(); + return null; + } + + MethodReference GetMetaAttributeConstructor(ILWeaverAssembly asm) { + return asm.CecilAssembly.MainModule.ImportReference(typeof(NetworkBehaviourWeavedAttribute).GetConstructors()[0]); + } + + TypeReference ImportType(ILWeaverAssembly asm) { + return asm.CecilAssembly.MainModule.ImportReference(typeof(T)); + } + + int GetNetworkBehaviourWordCount(ILWeaverAssembly asm, TypeDefinition type) { + if (type.Name == nameof(NetworkBehaviour)) { + return 0; + } + + // has to be network behaviour + Assert.Always(type.IsSubclassOf()); + + // make sure parent is weaved + WeaveBehaviour(asm, type); + + // assert this.. but should always be true + Assert.Always(type.HasAttribute()); + + // this always has to exist + return _networkedBehaviourTypeData[type.FullName].BlockCount; + } + + FieldDefinition FindBackingField(TypeDefinition type, string property) { + // compute backing field name... + var backingFieldName = $"<{property}>k__BackingField"; + + // find backing field + return type.Fields.FirstOrDefault(x => x.IsPrivate && x.Name == backingFieldName); + } + + void LoadDataAddress(ILProcessor il, FieldReference field, int wordCount) { + il.Append(Instruction.Create(OpCodes.Ldarg_0)); + il.Append(Instruction.Create(OpCodes.Ldfld, field)); + il.Append(Instruction.Create(OpCodes.Ldc_I4, wordCount * Allocator.REPLICATE_WORD_SIZE)); + il.Append(Instruction.Create(OpCodes.Add)); + } + + int GetWordCount(ILWeaverAssembly asm, WrapInfo wrapInfo) { + return wrapInfo.WrapperType != null ? GetTypeWordCount(asm, wrapInfo.WrapperType) : Native.WordCount(wrapInfo.MaxRawByteCount, Allocator.REPLICATE_WORD_SIZE); + } + + int GetByteCount(ILWeaverAssembly asm, WrapInfo wrapInfo) { + return GetWordCount(asm, wrapInfo) * Allocator.REPLICATE_WORD_SIZE; + } + + int GetTypeWordCount(ILWeaverAssembly asm, TypeReference type) { + if (type.IsPointer || type.IsByReference) { + type = type.GetElementTypeEx(); + } + + // TODO: what is this? + if (type.IsNetworkArray(out var elementType)) { + type = elementType; + } else if (type.IsNetworkList(out elementType)) { + type = elementType; + } + + if (_typeData.TryGetValue(type.FullName, out var data) == false) { + var typeDefinition = type.TryResolve(); + if (typeDefinition == null) { + throw new ILWeaverException($"Could not resolve type {type}"); + } + + if (typeDefinition.IsEnum) { + _typeData.Add(type.FullName, data = new TypeMetaData { + WordCount = 1, + Definition = typeDefinition + }); + } else if (typeDefinition.IsValueType && typeDefinition.Is()) { + // weave this struct + WeaveStruct(asm, typeDefinition, type); + + // grab type data + data = _typeData[type.FullName]; + } else if (type.IsFixedBuffer(out var size)) { + _typeData.Add(type.FullName, data = new TypeMetaData { + WordCount = Native.WordCount(size, Allocator.REPLICATE_WORD_SIZE), + Definition = typeDefinition + }); + } else if (TryGetNetworkWrapperType(type, out var wrapInfo)) { + _typeData.Add(type.FullName, data = new TypeMetaData { + WordCount = GetWordCount(asm, wrapInfo), + Definition = typeDefinition + }); + } else { + if (type.IsValueType) { + throw new ILWeaverException($"Value type {type} does not implement {nameof(INetworkStruct)}"); + } else { + throw new ILWeaverException($"Reference type {type} is not supported"); + } + } + } + + return data.WordCount; + } + + int GetByteCount(ILWeaverAssembly asm, TypeReference type) { + return GetTypeWordCount(asm, type) * Allocator.REPLICATE_WORD_SIZE; + } + + int GetPropertyWordCount(ILWeaverAssembly asm, PropertyDefinition property) { + //if (property.PropertyType.Is() || property.PropertyType.Is()) { + // return 2; + //} + + if (property.PropertyType.IsString()) { + if (property.DeclaringType.IsValueType) { + return 1 + GetStringCapacity(property); + } else { + return 2 + GetStringCapacity(property); + } + } + + if (property.PropertyType.IsNetworkArray()) { + return GetStaticArrayCapacity(property) * GetTypeWordCount(asm, GetStaticArrayElementType(property.PropertyType)); + } + + if (property.PropertyType.IsNetworkList()) { + return NetworkLinkedList.META_WORDS + (GetStaticListCapacity(property) * (GetTypeWordCount(asm, GetStaticListElementType(property.PropertyType)) + NetworkLinkedList.ELEMENT_WORDS)); + } + + if (property.PropertyType.IsNetworkDictionary()) { + + var capacity = GetStaticDictionaryCapacity(property); + + return + // meta data (counts, etc) + NetworkDictionary.META_WORD_COUNT + + + // buckets + (capacity) + + + // entry + + // next + (capacity) + + + // key + (capacity * GetTypeWordCount(asm, GetStaticDictionaryKeyType(property.PropertyType))) + + + // value + (capacity * GetTypeWordCount(asm, GetStaticDictionaryValType(property.PropertyType))); + } + + + return GetTypeWordCount(asm, property.PropertyType); + } + + static void FloatDecompress(ILProcessor il, float accuracy) { + if (accuracy == 0) { + il.Append(Instruction.Create(OpCodes.Conv_R4)); + } else { + il.Append(Instruction.Create(OpCodes.Conv_R4)); + il.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy)); + il.Append(Instruction.Create(OpCodes.Mul)); + } + } + + void FloatCompress(ILProcessor il, float accuracy) { + il.Append(Instruction.Create(OpCodes.Ldc_R4, 1.0f / accuracy)); + il.Append(Instruction.Create(OpCodes.Mul)); + il.Append(Instruction.Create(OpCodes.Ldc_R4, 0.5f)); + il.Append(Instruction.Create(OpCodes.Add)); + il.Append(Instruction.Create(OpCodes.Conv_I4)); + } + + int GetStringCapacity(PropertyDefinition property) { + return Math.Max(1, GetCapacity(property, 16)); + } + + int GetStaticArrayCapacity(PropertyDefinition property) { + return Math.Max(1, GetCapacity(property, 1)); + } + + int GetStaticListCapacity(PropertyDefinition property) { + return Math.Max(1, GetCapacity(property, 1)); + } + + int GetStaticDictionaryCapacity(PropertyDefinition property) { + return Primes.GetNextPrime(Math.Max(1, GetCapacity(property, 1))); + } + + int GetCapacity(PropertyDefinition property, int defaultCapacity) { + int size; + + if (property.TryGetAttribute(out var attr)) { + size = (int)attr.ConstructorArguments[0].Value; + } else { + size = defaultCapacity; + } + + return size; + } + + bool TryGetFloatAccuracy(PropertyDefinition property, out float accuracy ) { + if (property.TryGetAttribute(out var attr)) { + var obj = attr.ConstructorArguments[0]; + + // If the argument is a string, this is using a global Accuracy. Need to look up the value. + if (obj.Value is string str) { + accuracy = ILWeaverSettings.GetNamedFloatAccuracy(str); + } else { + var val = attr.ConstructorArguments[0].Value; + if (val is float fval) { + accuracy = fval; + } else if (val is double dval) { + accuracy = (float)dval; + } else { + throw new Exception($"Invalid argument type: {val.GetType()}"); + } + } + return true; + } else { + accuracy = 0.0f; ; + return false; + } + } + + float GetFloatAccuracy(PropertyDefinition property) { + float accuracy; + + if (property.TryGetAttribute(out var attr)) { + var obj = attr.ConstructorArguments[0]; + + // If the argument is a string, this is using a global Accuracy. Need to look up the value. + if (obj.Value is string str) { + accuracy = ILWeaverSettings.GetNamedFloatAccuracy(str); + } else { + var val = attr.ConstructorArguments[0].Value; + if (val is float fval) { + accuracy = fval; + } else if (val is double dval) { + accuracy = (float)dval; + } else { + throw new Exception($"Invalid argument type: {val.GetType()}"); + } + } + } else { + accuracy = AccuracyDefaults.DEFAULT_ACCURACY; + } + + return accuracy; + } + + TypeReference GetStaticArrayElementType(TypeReference type) { + return ((GenericInstanceType)type).GenericArguments[0]; + } + + TypeReference GetStaticListElementType(TypeReference type) { + return ((GenericInstanceType)type).GenericArguments[0]; + } + + TypeReference GetStaticDictionaryKeyType(TypeReference type) { + return ((GenericInstanceType)type).GenericArguments[0]; + } + + TypeReference GetStaticDictionaryValType(TypeReference type) { + return ((GenericInstanceType)type).GenericArguments[1]; + } + + void LoadArrayElementAddress(ILProcessor il, OpCode arrayOpCode, OpCode indexOpCode, int elementWordCount, int wordOffset = 0) { + il.Append(Instruction.Create(arrayOpCode)); + il.Append(Instruction.Create(indexOpCode)); + il.Append(Instruction.Create(OpCodes.Ldc_I4, elementWordCount * Allocator.REPLICATE_WORD_SIZE)); + il.Append(Instruction.Create(OpCodes.Mul)); + + if (wordOffset != 0) { + il.Append(Instruction.Create(OpCodes.Ldc_I4, wordOffset * Allocator.REPLICATE_WORD_SIZE)); + il.Append(Instruction.Create(OpCodes.Add)); + } + + il.Append(Instruction.Create(OpCodes.Add)); + } + + string GetCacheName(string name) { + return $"cache_<{name}>"; + } + + string GetInspectorFieldName(string name) { + return $"_{name}"; + } + + void InjectPtrNullCheck(ILWeaverAssembly asm, ILProcessor il, PropertyDefinition property) { + if (ILWeaverSettings.NullChecksForNetworkedProperties()) { + var nop = Instruction.Create(OpCodes.Nop); + + il.Append(Instruction.Create(OpCodes.Ldarg_0)); + il.Append(Instruction.Create(OpCodes.Ldfld, asm.NetworkedBehaviour.GetField(PTR_FIELD_NAME))); + il.Append(Instruction.Create(OpCodes.Ldc_I4_0)); + il.Append(Instruction.Create(OpCodes.Conv_U)); + il.Append(Instruction.Create(OpCodes.Ceq)); + il.Append(Instruction.Create(OpCodes.Brfalse, nop)); + + var ctor = typeof(InvalidOperationException).GetConstructors().First(x => x.GetParameters().Length == 1); + var exnCtor = asm.Import(ctor); + + il.Append(Instruction.Create(OpCodes.Ldstr, $"Error when accessing {property.DeclaringType.Name}.{property.Name}. Networked properties can only be accessed when Spawned() has been called.")); + il.Append(Instruction.Create(OpCodes.Newobj, exnCtor)); + il.Append(Instruction.Create(OpCodes.Throw)); + il.Append(nop); + } + } + + + + void InjectValueAccessor(ILWeaverAssembly asm, ILProcessor getIL, ILProcessor setIL, PropertyDefinition property, TypeReference type, OpCode valueOpCode, Action addressLoader, bool injectNullChecks) { + if (injectNullChecks) { + InjectPtrNullCheck(asm, getIL, property); + + if (setIL != null) { + InjectPtrNullCheck(asm, setIL, property); + } + } + + // for pointer types we can simply just return the address we loaded on the stack + if (type.IsPointer || type.IsByReference) { + // load address + addressLoader(getIL, 0); + + // return + getIL.Append(Instruction.Create(OpCodes.Ret)); + + // this has to be null + Assert.Check(setIL == null); + } else if (property.PropertyType.IsString()) { + + if (property.DeclaringType.IsValueType) { + + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Call, asm.ReadWriteUtils.GetMethod(nameof(ReadWriteUtilsForWeaver.ReadStringUtf32NoHash), 1))); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(OpCodes.Ldc_I4, GetStringCapacity(property))); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Call, asm.ReadWriteUtils.GetMethod(nameof(ReadWriteUtilsForWeaver.WriteStringUtf32NoHash), 3))); + setIL.Append(Instruction.Create(OpCodes.Ret)); + + } else { + + var cache = new FieldDefinition(GetCacheName(property.Name), FieldAttributes.Private, asm.CecilAssembly.MainModule.ImportReference(typeof(string))); + + property.DeclaringType.Fields.Add(cache); + + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldarg_0)); + getIL.Append(Instruction.Create(OpCodes.Ldflda, cache)); + getIL.Append(Instruction.Create(OpCodes.Call, asm.ReadWriteUtils.GetMethod(nameof(ReadWriteUtilsForWeaver.ReadStringUtf32WithHash), 2))); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(OpCodes.Ldc_I4, GetStringCapacity(property))); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Ldarg_0)); + setIL.Append(Instruction.Create(OpCodes.Ldflda, cache)); + setIL.Append(Instruction.Create(OpCodes.Call, asm.ReadWriteUtils.GetMethod(nameof(ReadWriteUtilsForWeaver.WriteStringUtf32WithHash), 4))); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } + } + + // for primitive values + else if (type.IsPrimitive) { + // floats + if (type.IsFloat()) { + var accuracy = GetFloatAccuracy(property); + + // getter + addressLoader(getIL, 0); + + if (accuracy == 0) { + getIL.Append(Instruction.Create(OpCodes.Ldind_R4)); + } else { + getIL.Append(Instruction.Create(OpCodes.Ldind_I4)); + getIL.Append(Instruction.Create(OpCodes.Conv_R4)); + getIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy)); + getIL.Append(Instruction.Create(OpCodes.Mul)); + } + + getIL.Append(Instruction.Create(OpCodes.Ret)); + + // setter + addressLoader(setIL, 0); + + if (accuracy == 0) { + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Stind_R4)); + } else { + setIL.Append(Instruction.Create(OpCodes.Ldc_R4, 1f / accuracy)); + setIL.Append(Instruction.Create(valueOpCode)); + var write = asm.ReadWriteUtils.GetMethod("WriteFloat"); + setIL.Append(Instruction.Create(OpCodes.Call, write)); + } + + setIL.Append(Instruction.Create(OpCodes.Ret)); + } else if (type.IsBool()) { + { + // return *ptr == 0 ? false : true + var load_0 = Ldc_I4(0); + addressLoader(getIL, 0); + getIL.Append(Ldind_I4()); + getIL.Append(Brfalse_S(load_0)); + getIL.Append(Ldc_I4(1)); + getIL.Append(Ret()); + getIL.Append(load_0); + getIL.Append(Ret()); + } + { + // *ptr = value ? 1 : 0; + var store = Stind_I4(); + var load_1 = Ldc_I4(1); + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Brtrue_S(load_1)); + setIL.Append(Ldc_I4(0)); + setIL.Append(Br_S(store)); + setIL.Append(load_1); + setIL.Append(store); + setIL.Append(Ret()); + } + } else { + // byte, sbyte, short, ushort, int, uint + addressLoader(getIL, 0); + getIL.Append(Ldind(type)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Stind(type)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } + } else if (TryGetNetworkWrapperType(type, out var wrapInfo)) { + + FieldDefinition field = null; + //if (wrapInfo.UnwrapByRef) { + // cache = new FieldDefinition(GetCacheName(property.Name), FieldAttributes.Private, wrapInfo.Type); + //} + + // getter + using (var ctx = new MethodContext(asm, getIL.Body.Method, addressGetter: (x) => addressLoader(x, 0))) { + if (field != null) { + property.DeclaringType.Fields.Add(field); + WeaveNetworkUnwrap(asm, getIL, ctx, wrapInfo, type, previousValue: field); + } else { + WeaveNetworkUnwrap(asm, getIL, ctx, wrapInfo, type); + } + getIL.Append(Ret()); + } + + + if (setIL != null) { + // setter + using (var ctx = new MethodContext(asm, setIL.Body.Method, addressGetter: (x) => addressLoader(x, 0))) { + WeaveNetworkWrap(asm, setIL, ctx, il => il.Append(Instruction.Create(valueOpCode)), wrapInfo); + //if (cache != null) { + // setIL.Append(Ldarg_0()); + // setIL.Append(Ldarg_1()); + // setIL.Append(Stfld(cache)); + //} + setIL.Append(Ret()); + } + } + } + // other value types + else if (type.IsValueType) { + var resolvedPropertyType = type.TryResolve(); + if (resolvedPropertyType == null) { + throw new ILWeaverException($"Can't resolve type for property {property.FullName} with type {property.PropertyType}"); + } + + if (resolvedPropertyType.IsEnum) { + addressLoader(getIL, 0); + var underlayingEnumType = resolvedPropertyType.GetEnumUnderlyingType(); + getIL.Append(Ldind(underlayingEnumType)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Stind(underlayingEnumType)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } else if (type.IsQuaternion()) { + var accuracy = GetFloatAccuracy(property); + + // getter + var read = asm.ReadWriteUtils.GetMethod("ReadQuaternion"); + + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy)); + getIL.Append(Instruction.Create(OpCodes.Call, read)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + // setter + var write = asm.ReadWriteUtils.GetMethod("WriteQuaternion"); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy == 0 ? 0 : (1f / accuracy))); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Call, write)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } else if (type.IsVector2()) { + var accuracy = GetFloatAccuracy(property); + + // getter + var read = asm.ReadWriteUtils.GetMethod("ReadVector2"); + + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy)); + getIL.Append(Instruction.Create(OpCodes.Call, read)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + // setter + var write = asm.ReadWriteUtils.GetMethod("WriteVector2"); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy == 0 ? 0 : (1f / accuracy))); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Call, write)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } else if (type.IsVector3()) { + var accuracy = GetFloatAccuracy(property); + + // getter + var read = asm.ReadWriteUtils.GetMethod("ReadVector3"); + + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy)); + getIL.Append(Instruction.Create(OpCodes.Call, read)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + // setter + var write = asm.ReadWriteUtils.GetMethod("WriteVector3"); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(OpCodes.Ldc_R4, accuracy == 0 ? 0 : (1f / accuracy))); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Call, write)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } else if (type.IsNetworkDictionary(out var keyType, out var valType)) { + + if (setIL != null) { + throw new ILWeaverException($"NetworkDictionary properties can't have setters."); + } + + var capacity = GetStaticDictionaryCapacity(property); + + var keyInfo = MakeElementReaderWriter(asm, property, keyType); + var valInfo = MakeElementReaderWriter(asm, property, valType); + + // load address + keyInfo.InitializeInstance(getIL); + valInfo.InitializeInstance(getIL); + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_I4, capacity)); + keyInfo.LoadInstance(getIL); + valInfo.LoadInstance(getIL); + + var ctor = asm.CecilAssembly.MainModule.ImportReference(type.Resolve().GetConstructors().First(x => x.Parameters.Count > 1)); + ctor.DeclaringType = ctor.DeclaringType.MakeGenericInstanceType(keyType, valType); + + getIL.Append(Instruction.Create(OpCodes.Newobj, ctor)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + } else if (type.IsNetworkList(out var elementType)) { + if (setIL != null) { + throw new ILWeaverException($"NetworkList properties can't have setters."); + } + + var listCapacity = GetStaticListCapacity(property); + + var elementInfo = MakeElementReaderWriter(asm, property, elementType); + + elementInfo.InitializeInstance(getIL); + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_I4, listCapacity)); + elementInfo.LoadInstance(getIL); + + var ctor = asm.CecilAssembly.MainModule.ImportReference(type.Resolve().GetConstructors().First(x => x.Parameters.Count > 1)); + ctor.DeclaringType = ctor.DeclaringType.MakeGenericInstanceType(elementType); + + getIL.Append(Instruction.Create(OpCodes.Newobj, ctor)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + } else if (type.IsNetworkArray(out elementType)) { + if (setIL != null) { + throw new ILWeaverException($"NetworkArray properties can't have setters."); + } + + var arrayLength = GetStaticArrayCapacity(property); + var elementInfo = MakeElementReaderWriter(asm, property, elementType); + + // load address + elementInfo.InitializeInstance(getIL); + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldc_I4, arrayLength)); + elementInfo.LoadInstance(getIL); + + var ctor = asm.CecilAssembly.MainModule.ImportReference(type.Resolve().GetConstructors().First(x => x.Parameters.Count > 1)); + ctor.DeclaringType = ctor.DeclaringType.MakeGenericInstanceType(elementType); + + getIL.Append(Instruction.Create(OpCodes.Newobj, ctor)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + } else { + addressLoader(getIL, 0); + getIL.Append(Instruction.Create(OpCodes.Ldobj, type)); + getIL.Append(Instruction.Create(OpCodes.Ret)); + + addressLoader(setIL, 0); + setIL.Append(Instruction.Create(valueOpCode)); + setIL.Append(Instruction.Create(OpCodes.Stobj, type)); + setIL.Append(Instruction.Create(OpCodes.Ret)); + } + } + } + + void RemoveBackingField(PropertyDefinition property) { + var backing = FindBackingField(property.DeclaringType, property.Name); + if (backing != null) { + property.DeclaringType.Fields.Remove(backing); + } + } + + (MethodDefinition getter, MethodDefinition setter) PreparePropertyForWeaving(PropertyDefinition property) { + var getter = property.GetMethod; + var setter = property.SetMethod; + + // clear getter + getter.CustomAttributes.Clear(); + getter.Body.Instructions.Clear(); + + // clear setter if it exists + setter?.CustomAttributes?.Clear(); + setter?.Body?.Instructions?.Clear(); + + return (getter, setter); + } + + bool IsWeavableProperty(PropertyDefinition property) { + return IsWeavableProperty(property, out _); + } + + struct WeavablePropertyMeta { + public string DefaultFieldName; + public FieldDefinition BackingField; + public bool ReatainIL; + public string OnChanged; + } + + bool IsWeavableProperty(PropertyDefinition property, out WeavablePropertyMeta meta) { + if (property.TryGetAttribute(out var attr) == false) { + meta = default; + return false; + } + + string onChanged; + attr.TryGetAttributeProperty(nameof(NetworkedAttribute.OnChanged), out onChanged); + + // check getter ... it has to exist + var getter = property.GetMethod; + if (getter == null) { + meta = default; + return false; + } + + // check setter ... + var setter = property.SetMethod; + if (setter == null) { + // if it doesn't exist we allow either array or pointer + if (property.PropertyType.IsByReference == false && property.PropertyType.IsPointer == false && property.PropertyType.IsNetworkArray() == false && property.PropertyType.IsNetworkDictionary() == false && property.PropertyType.IsNetworkList() == false) { + throw new ILWeaverException($"Simple properties need a setter."); + } + } + + // check for backing field ... + var backing = FindBackingField(property.DeclaringType, property.Name); + if (backing == null) { + var il = attr.Properties.FirstOrDefault(x => x.Name == "RetainIL"); + + if (il.Argument.Value is bool retainIL && retainIL) { + meta = new WeavablePropertyMeta() { + ReatainIL = true, + OnChanged = onChanged, + }; + return true; + } + } + + meta = new WeavablePropertyMeta() { + BackingField = backing, + ReatainIL = false, + OnChanged = onChanged, + }; + + attr.TryGetAttributeProperty(nameof(NetworkedAttribute.Default), out meta.DefaultFieldName); + + + return true; + } + + bool IsRpcCompatibleType(TypeReference property) { + if (property.IsPointer) { + return false; + } + + if (property.IsNetworkArray() || property.IsNetworkList() || property.IsNetworkDictionary()) { + return false; + } + + if (property.IsString()) { + return true; + } + + if (property.IsValueType) { + return true; + } + + if (TryGetNetworkWrapperType(property, out var wrapInfo)) { + if (wrapInfo.MaxRawByteCount > 0) { + return true; + } else { + return IsRpcCompatibleType(wrapInfo.WrapperType); + } + } + + return false; + } + + void WeaveInput(ILWeaverAssembly asm, TypeDefinition type) { + if (type.TryGetAttribute(out var attribute)) { + if (_typeData.ContainsKey(type.FullName) == false) { + _typeData.Add(type.FullName, new TypeMetaData { + WordCount = (int)attribute.ConstructorArguments[0].Value, + Definition = type, + Reference = type + }); + } + + return; + } + + using (Log.ScopeInput(type)) { + + int wordCount = WeaveStructInner(asm, type); + + // add new attribute + type.AddAttribute(asm, wordCount); + + // track type data + _typeData.Add(type.FullName, new TypeMetaData { + WordCount = wordCount, + Definition = type, + Reference = type + }); + } + } + + + + MethodReference FindMethodInParent(ILWeaverAssembly asm, TypeDefinition type, string name, string stopAtType = null, int? argCount = null) { + type = type.BaseType.Resolve(); + + while (type != null) { + if (type.Name == stopAtType || type.FullName == "System.Object") { + return null; + } + + var method = type.Methods.FirstOrDefault(x => x.Name == name && ((argCount == null) || (x.Parameters.Count == argCount.Value))); + if (method != null) { + return asm.CecilAssembly.MainModule.ImportReference(method); + } + + type = type.BaseType.Resolve(); + } + + return null; + } + + string InvokerMethodName(string method, Dictionary nameCache) { + nameCache.TryGetValue(method, out var count); + nameCache[method] = ++count; + return $"{method}@Invoker{(count == 1 ? "" : count.ToString())}"; + } + + bool HasRpcPrefixOrSuffix(MethodDefinition def) { + return def.Name.StartsWith("rpc", StringComparison.OrdinalIgnoreCase) || def.Name.EndsWith("rpc", StringComparison.OrdinalIgnoreCase); + } + + void WeaveRpcs(ILWeaverAssembly asm, TypeDefinition type, bool allowInstanceRpcs = true) { + if (type.HasGenericParameters) { + return; + } + + // rpc list + var rpcs = new List<(MethodDefinition, CustomAttribute)>(); + + + bool hasStaticRpc = false; + + foreach (var rpc in type.Methods) { + if (rpc.TryGetAttribute(out var attr)) { + + if (HasRpcPrefixOrSuffix(rpc) == false) { + Log.Warn($"{rpc.DeclaringType}.{rpc.Name} name does not start or end with the \"Rpc\" prefix or suffix. Starting from Beta this will result in an error."); + } + + if (rpc.IsStatic && rpc.Parameters.FirstOrDefault()?.ParameterType.FullName != asm.NetworkRunner.Reference.FullName) { + throw new ILWeaverException($"{rpc}: Static RPC needs {nameof(NetworkRunner)} as the first parameter"); + } + + hasStaticRpc |= rpc.IsStatic; + + if (!allowInstanceRpcs && !rpc.IsStatic) { + throw new ILWeaverException($"{rpc}: Instance RPCs not allowed for this type"); + } + + foreach (var parameter in rpc.Parameters) { + if (rpc.IsStatic && parameter == rpc.Parameters[0]) { + continue; + } + + if (IsInvokeOnlyParameter(parameter)) { + continue; + } + + var parameterType = parameter.ParameterType.IsArray ? parameter.ParameterType.GetElementType() : parameter.ParameterType; + + if (IsRpcCompatibleType(parameterType) == false) { + throw new ILWeaverException($"{rpc}: parameter {parameter.Name} is not Rpc-compatible."); + } + } + + if (!rpc.ReturnType.Is(asm.RpcInvokeInfo) && !rpc.ReturnType.IsVoid()) { + throw new ILWeaverException($"{rpc}: RPCs can't return a value."); + } + + rpcs.Add((rpc, attr)); + } + } + + if (!rpcs.Any()) { + return; + } + + int instanceRpcKeys = GetInstanceRpcCount(type.BaseType); + + Dictionary invokerNameCounter = new Dictionary(); + + foreach (var (rpc, attr) in rpcs) { + int sources; + int targets; + + if (attr.ConstructorArguments.Count == 2) { + sources = attr.GetAttributeArgument(0); + targets = attr.GetAttributeArgument(1); + } else { + sources = AuthorityMasks.ALL; + targets = AuthorityMasks.ALL; + } + + ParameterDefinition rpcTargetParameter = rpc.Parameters.SingleOrDefault(x => x.HasAttribute()); + if (rpcTargetParameter != null && !rpcTargetParameter.ParameterType.Is()) { + throw new ILWeaverException($"{rpcTargetParameter}: {nameof(RpcTargetAttribute)} can only be used for {nameof(PlayerRef)} type argument"); + } + + attr.TryGetAttributeProperty(nameof(RpcAttribute.InvokeLocal), out var invokeLocal, defaultValue: true); + attr.TryGetAttributeProperty(nameof(RpcAttribute.InvokeResim), out var invokeResim); + attr.TryGetAttributeProperty(nameof(RpcAttribute.Channel), out var channel); + attr.TryGetAttributeProperty(nameof(RpcAttribute.TickAligned), out var tickAligned, defaultValue: true); + attr.TryGetAttributeProperty(nameof(RpcAttribute.HostMode), out var hostMode); + + // rpc key + int instanceRpcKey = -1; + var returnsRpcInvokeInfo = rpc.ReturnType.Is(asm.RpcInvokeInfo); + + + + using (var ctx = new RpcMethodContext(asm, rpc, rpc.IsStatic)) { + + // local variables + ctx.DataVariable = new VariableDefinition(asm.Import(typeof(byte)).MakePointerType()); + ctx.OffsetVariable = new VariableDefinition(asm.Import(typeof(int))); + var message = new VariableDefinition(asm.SimulationMessage.Reference.MakePointerType()); + VariableDefinition localAuthorityMask = null; + + rpc.Body.Variables.Add(ctx.DataVariable); + rpc.Body.Variables.Add(ctx.OffsetVariable); + rpc.Body.Variables.Add(message); + rpc.Body.InitLocals = true; + + // get il processes and our jump instruction + var il = rpc.Body.GetILProcessor(); + var jmp = Nop(); + var inv = Nop(); + var prepareInv = Nop(); + + Instruction targetedInvokeLocal = null; + + + // instructions for our branch + var ins = new List(); + + if (returnsRpcInvokeInfo) { + // find local variable that's used for return(default); + ctx.RpcInvokeInfoVariable = new VariableDefinition(asm.RpcInvokeInfo); + rpc.Body.Variables.Add(ctx.RpcInvokeInfoVariable); + ins.Add(Ldloca(ctx.RpcInvokeInfoVariable)); + ins.Add(Initobj(ctx.RpcInvokeInfoVariable.VariableType)); + + // fix each ret + var instructions = il.Body.Instructions; + for (int i = 0; i < instructions.Count; ++i) { + var instruction = instructions[i]; + if (instruction.OpCode == OpCodes.Ret) { + if (instructions[i - 1].IsLdlocWithIndex(out _)) { + // replace indexed load + instructions[i - 1].OpCode = OpCodes.Ldloc; + instructions[i - 1].Operand = ctx.RpcInvokeInfoVariable; + } else if (instructions[i - 1].OpCode == OpCodes.Ldloc) { + // replace named load + instructions[i - 1].Operand = ctx.RpcInvokeInfoVariable; + } else { + throw new ILWeaverException($"{rpc}: return pattern of {nameof(RpcInvokeInfo)} not recognised at {instruction}. Context: {string.Join("\n", instructions)}"); + } + } + } + } + + if (rpc.IsStatic) { + ins.Add(Ldsfld(asm.NetworkBehaviourUtils.GetField(nameof(NetworkBehaviourUtils.InvokeRpc)))); + ins.Add(Brfalse(jmp)); + ins.Add(Ldc_I4(0)); + ins.Add(Stsfld(asm.NetworkBehaviourUtils.GetField(nameof(NetworkBehaviourUtils.InvokeRpc)))); + } else { + ins.Add(Ldarg_0()); + ins.Add(Ldfld(asm.NetworkedBehaviour.GetField(nameof(NetworkBehaviour.InvokeRpc)))); + ins.Add(Brfalse(jmp)); + ins.Add(Ldarg_0()); + ins.Add(Ldc_I4(0)); + ins.Add(Stfld(asm.NetworkedBehaviour.GetField(nameof(NetworkBehaviour.InvokeRpc)))); + } + ins.Add(inv); + + + // insert instruction into method body + var prev = rpc.Body.Instructions[0]; //.OpCode == OpCodes.Nop ? rpc.Body.Instructions[1] : rpc.Body.Instructions[0]; + + for (int i = ins.Count - 1; i >= 0; --i) { + il.InsertBefore(prev, ins[i]); + prev = ins[i]; + } + + // jump target + il.Append(jmp); + + + + var returnInstructions = returnsRpcInvokeInfo + ? new[] { Ldloc(ctx.RpcInvokeInfoVariable), Ret() } + : new[] { Ret() }; + + var ret = returnInstructions.First(); + + // check if runner's ok + if (rpc.IsStatic) { + il.AppendMacro(ctx.LoadRunner()); + var checkDone = Nop(); + il.Append(Brtrue_S(checkDone)); + il.Append(Ldstr(rpc.Parameters[0].Name)); + il.Append(Newobj(typeof(ArgumentNullException).GetConstructor(asm, 1))); + il.Append(Throw()); + il.Append(checkDone); + } else { + il.Append(Ldarg_0()); + il.Append(Call(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.ThrowIfBehaviourNotInitialized)))); + } + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(!invokeLocal, RpcLocalInvokeResult.NotInvokableLocally)); + + // if we shouldn't invoke during resim + if (invokeResim == false) { + var checkDone = Nop(); + + il.AppendMacro(ctx.LoadRunner()); + + il.Append(Call(asm.NetworkRunner.GetProperty("Stage"))); + il.Append(Ldc_I4((int)SimulationStages.Resimulate)); + il.Append(Bne_Un_S(checkDone)); + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(invokeLocal, RpcLocalInvokeResult.NotInvokableDuringResim)); + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.NotInvokableDuringResim)); + il.Append(Br(ret)); + + il.Append(checkDone); + } + + if (!rpc.IsStatic) { + localAuthorityMask = new VariableDefinition(asm.Import(typeof(int))); + rpc.Body.Variables.Add(localAuthorityMask); + il.Append(Ldarg_0()); + il.Append(Ldfld(asm.NetworkedBehaviour.GetField(OBJECT_FIELD_NAME))); + il.Append(Call(asm.NetworkedObject.GetMethod(nameof(NetworkObject.GetLocalAuthorityMask)))); + il.Append(Stloc(localAuthorityMask)); + } + + // check if target is reachable or not + if (rpcTargetParameter != null) { + il.AppendMacro(ctx.LoadRunner()); + + il.Append(Ldarg(rpcTargetParameter)); + il.Append(Call(asm.NetworkRunner.GetMethod(nameof(NetworkRunner.GetRpcTargetStatus)))); + il.Append(Dup()); + + // check for being unreachable + { + var done = Nop(); + il.Append(Ldc_I4((int)RpcTargetStatus.Unreachable)); + il.Append(Bne_Un_S(done)); + il.Append(Ldarg(rpcTargetParameter)); + il.Append(Ldstr(rpc.ToString())); + il.Append(Call(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.NotifyRpcTargetUnreachable)))); + il.Append(Pop()); // pop the GetRpcTargetStatus + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(invokeLocal, RpcLocalInvokeResult.TagetPlayerIsNotLocal)); + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.TargetPlayerUnreachable)); + il.Append(Br(ret)); + + il.Append(done); + } + + // check for self + { + il.Append(Ldc_I4((int)RpcTargetStatus.Self)); + if (invokeLocal) { + // straight to the invoke; this will prohibit any sending + Log.Assert(targetedInvokeLocal == null); + targetedInvokeLocal = Nop(); + il.Append(Beq(targetedInvokeLocal)); + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(true, RpcLocalInvokeResult.TagetPlayerIsNotLocal)); + } else { + // will never get called + var checkDone = Nop(); + il.Append(Bne_Un_S(checkDone)); + + if (NetworkRunner.BuildType == NetworkRunner.BuildTypes.Debug) { + il.Append(Ldarg(rpcTargetParameter)); + il.Append(Ldstr(rpc.ToString())); + il.Append(Call(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.NotifyLocalTargetedRpcCulled)))); + } + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.TargetPlayerIsLocalButRpcIsNotInvokableLocally)); + il.Append(Br(ret)); + + il.Append(checkDone); + } + } + } + + // check if sender flags make sense + if (!rpc.IsStatic) { + var checkDone = Nop(); + + il.Append(Ldloc(localAuthorityMask)); + il.Append(Ldc_I4(sources)); + il.Append(And()); + il.Append(Brtrue_S(checkDone)); + + // source is not valid, notify + il.Append(Ldstr(rpc.ToString())); + il.Append(Ldarg_0()); + il.Append(Ldfld(asm.NetworkedBehaviour.GetField(OBJECT_FIELD_NAME))); + il.Append(Ldc_I4(sources)); + il.Append(Call(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.NotifyLocalSimulationNotAllowedToSendRpc)))); + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(invokeLocal, RpcLocalInvokeResult.InsufficientSourceAuthority)); + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.InsufficientSourceAuthority)); + + il.Append(Br(ret)); + + il.Append(checkDone); + + if (invokeLocal) { + // how about the target? does it match only the local client? + if (targets != 0 && (targets & AuthorityMasks.PROXY) == 0) { + il.Append(Ldloc(localAuthorityMask)); + il.Append(Ldc_I4(targets)); + il.Append(And()); + il.Append(Ldc_I4(targets)); + il.Append(Beq(prepareInv)); + } + } + } + + // check if sending makes sense at all + var afterSend = Nop(); + + // if not targeted (already handled earlier) check if it can be sent at all + if (rpcTargetParameter == null) { + var checkDone = Nop(); + il.AppendMacro(ctx.LoadRunner()); + il.Append(Call(asm.NetworkRunner.GetMethod(nameof(NetworkRunner.HasAnyActiveConnections)))); + il.Append(Brtrue(checkDone)); + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.NoActiveConnections)); + il.Append(Br(afterSend)); + il.Append(checkDone); + } + + // create simulation message + il.AppendMacro(ctx.LoadRunner()); + il.Append(Call(asm.NetworkRunner.GetProperty(nameof(NetworkRunner.Simulation)))); + il.Append(Ldc_I4(RpcHeader.SIZE)); + + for (int i = 0; i < rpc.Parameters.Count; ++i) { + var para = rpc.Parameters[i]; + + if (rpc.IsStatic && i == 0) { + Log.Assert(para.ParameterType.IsSame()); + continue; + } + + if (IsInvokeOnlyParameter(para)) { + continue; + } + if (para == rpcTargetParameter) { + continue; + } + + if (para.ParameterType.IsString()) { + il.Append(Ldarg(para)); + il.Append(Call(asm.Native.GetMethod(nameof(Native.GetLengthPrefixedUTF8ByteCount)))); + il.AppendMacro(AlignToWordSize()); + } else if (para.ParameterType.IsArray) { + WeaveArrayByteSize(asm, il, para); + } else { + var size = GetByteCount(asm, para.ParameterType); + il.Append(Ldc_I4(size)); + } + il.Append(Add()); + } + + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.Allocate), 2))); + il.Append(Stloc(message)); + + // get data for messages + il.Append(Ldloc(message)); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.GetData), 1))); + il.Append(Stloc(ctx.DataVariable)); + + // create RpcHeader + // il.Append(Ldloc(data)); + + if (rpc.IsStatic) { + il.Append(Ldstr(rpc.ToString())); + il.Append(Call(asm.Import(typeof(NetworkBehaviourUtils).GetMethod(nameof(NetworkBehaviourUtils.GetRpcStaticIndexOrThrow))))); + il.Append(Call(asm.RpcHeader.GetMethod(nameof(RpcHeader.Create), 1))); + } else { + il.Append(Ldarg_0()); + il.Append(Ldfld(asm.NetworkedBehaviour.GetField(OBJECT_FIELD_NAME))); + il.Append(Ldfld(asm.NetworkedObject.GetField(nameof(NetworkObject.Id)))); + + il.Append(Ldarg_0()); + il.Append(Ldfld(asm.NetworkedBehaviour.GetField(nameof(NetworkBehaviour.ObjectIndex)))); + + instanceRpcKey = ++instanceRpcKeys; + il.Append(Ldc_I4(instanceRpcKey)); + il.Append(Call(asm.RpcHeader.GetMethod(nameof(RpcHeader.Create), 3))); + } + + il.Append(Ldloc(ctx.DataVariable)); + il.Append(Call(asm.RpcHeader.GetMethod(nameof(RpcHeader.Write)))); + il.AppendMacro(AlignToWordSize()); + //il.Append(Stobj(asm.RpcHeader.Reference)); + + //il.Append(Ldc_I4(RpcHeader.SIZE)); + il.Append(Stloc(ctx.OffsetVariable)); + + // write parameters + for (int i = 0; i < rpc.Parameters.Count; ++i) { + var para = rpc.Parameters[i]; + + if (rpc.IsStatic && i == 0) { + continue; + } + if (IsInvokeOnlyParameter(para)) { + continue; + } + if (para == rpcTargetParameter) { + continue; + } + if (para.ParameterType.IsString()) { + using (ctx.AddOffsetScope(il)) { + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldarg(para)); + il.Append(Call(asm.Native.GetMethod(nameof(Native.WriteLengthPrefixedUTF8)))); + il.AppendMacro(AlignToWordSize()); + }; + } else if (para.ParameterType.IsArray) { + WeaveRpcArrayInput(asm, ctx, il, para); + } else { + if (!para.ParameterType.IsPrimitive && TryGetNetworkWrapperType(para.ParameterType, out var wrapInfo)) { + WeaveNetworkWrap(asm, il, ctx, il => il.Append(Ldarg(para)), wrapInfo); + } else { + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldarg(para)); + il.Append(Stind_or_Stobj(para.ParameterType)); + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, para.ParameterType))); + } + } + } + + // update message offset + il.Append(Ldloc(message)); + il.Append(Ldflda(asm.SimulationMessage.GetField(nameof(SimulationMessage.Offset)))); + il.Append(Ldloc(ctx.OffsetVariable)); + il.Append(Ldc_I4(8)); + il.Append(Mul()); + il.Append(Stind_I4()); + + // send message + + il.AppendMacro(ctx.LoadRunner()); + + if (rpcTargetParameter != null) { + il.Append(Ldloc(message)); + il.Append(Ldarg(rpcTargetParameter)); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.SetTarget)))); + } + + if (channel == RpcChannel.Unreliable) { + il.Append(Ldloc(message)); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.SetUnreliable)))); + } + + if (!tickAligned) { + il.Append(Ldloc(message)); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.SetNotTickAligned)))); + } + + if (rpc.IsStatic) { + il.Append(Ldloc(message)); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.SetStatic)))); + } + + // send the rpc + il.Append(Ldloc(message)); + + if (ctx.RpcInvokeInfoVariable != null) { + il.Append(Ldloca(ctx.RpcInvokeInfoVariable)); + il.Append(Ldflda(asm.RpcInvokeInfo.GetField(nameof(RpcInvokeInfo.SendResult)))); + il.Append(Call(asm.NetworkRunner.GetMethod(nameof(NetworkRunner.SendRpc), 2))); + } else { + il.Append(Call(asm.NetworkRunner.GetMethod(nameof(NetworkRunner.SendRpc), 1))); + } + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(RpcSendCullResult.NotCulled)); + + il.Append(afterSend); + + // .. hmm + if (invokeLocal) { + + if (targetedInvokeLocal != null) { + il.Append(Br(ret)); + il.Append(targetedInvokeLocal); + } + + if (!rpc.IsStatic) { + var checkDone = Nop(); + il.Append(Ldloc(localAuthorityMask)); + il.Append(Ldc_I4(targets)); + il.Append(And()); + il.Append(Brtrue_S(checkDone)); + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(true, RpcLocalInvokeResult.InsufficientTargetAuthority)); + + il.Append(Br(ret)); + + il.Append(checkDone); + } + + il.Append(prepareInv); + + foreach (var param in rpc.Parameters) { + if (param.ParameterType.IsSame()) { + // need to fill it now + il.AppendMacro(ctx.LoadRunner()); + il.Append(Ldc_I4((int)channel)); + il.Append(Ldc_I4((int)hostMode)); + il.Append(Call(asm.RpcInfo.GetMethod(nameof(RpcInfo.FromLocal)))); + il.Append(Starg_S(param)); + } + } + + il.AppendMacro(ctx.SetRpcInvokeInfoStatus(true, RpcLocalInvokeResult.Invoked)); + + // invoke + il.Append(Br(inv)); + } + + foreach (var instruction in returnInstructions) { + il.Append(instruction); + } + } + + var invoker = new MethodDefinition(InvokerMethodName(rpc.Name, invokerNameCounter), MethodAttributes.Family | MethodAttributes.Static, asm.Import(typeof(void))); + using (var ctx = new RpcMethodContext(asm, invoker, rpc.IsStatic)) { + + // create invoker delegate + if (rpc.IsStatic) { + var runner = new ParameterDefinition("runner", ParameterAttributes.None, asm.NetworkRunner.Reference); + invoker.Parameters.Add(runner); + } else { + var behaviour = new ParameterDefinition("behaviour", ParameterAttributes.None, asm.NetworkedBehaviour.Reference); + invoker.Parameters.Add(behaviour); + } + var message = new ParameterDefinition("message", ParameterAttributes.None, asm.SimulationMessage.Reference.MakePointerType()); + invoker.Parameters.Add(message); + + // add attribute + if (rpc.IsStatic) { + Log.Assert(instanceRpcKey < 0); + invoker.AddAttribute(asm, rpc.ToString()); + } else { + Log.Assert(instanceRpcKey >= 0); + invoker.AddAttribute(asm, instanceRpcKey, sources, targets); + } + + // put on type + type.Methods.Add(invoker); + + // local variables + ctx.DataVariable = new VariableDefinition(asm.Import(typeof(byte)).MakePointerType()); + ctx.OffsetVariable = new VariableDefinition(asm.Import(typeof(int))); + var parameters = new VariableDefinition[rpc.Parameters.Count]; + + for (int i = 0; i < parameters.Length; ++i) { + invoker.Body.Variables.Add(parameters[i] = new VariableDefinition(rpc.Parameters[i].ParameterType)); + } + + invoker.Body.Variables.Add(ctx.DataVariable); + invoker.Body.Variables.Add(ctx.OffsetVariable); + invoker.Body.InitLocals = true; + + var il = invoker.Body.GetILProcessor(); + + // grab data from message and store in local + il.Append(Ldarg_1()); + il.Append(Call(asm.SimulationMessage.GetMethod(nameof(SimulationMessage.GetData), 1))); + il.Append(Stloc(ctx.DataVariable)); + + il.Append(Ldloc(ctx.DataVariable)); + il.Append(Call(asm.RpcHeader.GetMethod(nameof(RpcHeader.ReadSize)))); + il.AppendMacro(AlignToWordSize()); + il.Append(Stloc(ctx.OffsetVariable)); + + for (int i = 0; i < parameters.Length; ++i) { + var para = parameters[i]; + + if (rpc.IsStatic && i == 0) { + il.Append(Ldarg_0()); + il.Append(Stloc(para)); + continue; + } + + if (rpcTargetParameter == rpc.Parameters[i]) { + il.Append(Ldarg_1()); + il.Append(Ldfld(asm.SimulationMessage.GetField(nameof(SimulationMessage.Target)))); + il.Append(Stloc(para)); + } else if (para.VariableType.IsString()) { + + using (ctx.AddOffsetScope(il)) { + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldloca(para)); + il.Append(Call(asm.Native.GetMethod(nameof(Native.ReadLengthPrefixedUTF8)))); + il.AppendMacro(AlignToWordSize()); + }; + + } else if (para.VariableType.IsSame()) { + il.AppendMacro(ctx.LoadRunner()); + il.Append(Ldarg_1()); + il.Append(Ldc_I4((int)hostMode)); + il.Append(Call(asm.RpcInfo.GetMethod(nameof(RpcInfo.FromMessage)))); + il.Append(Stloc(para)); + + } else if (para.VariableType.IsArray) { + WeaveRpcArrayInvoke(asm, ctx, il, para); + + } else { + if (!para.VariableType.IsPrimitive && TryGetNetworkWrapperType(para.VariableType, out var wrapInfo)) { + WeaveNetworkUnwrap(asm, il, ctx, wrapInfo, para.VariableType, storeResult: il => il.Append(Stloc(para))); + } else { + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldind_or_Ldobj(para.VariableType)); + il.Append(Stloc(para)); + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, para.VariableType))); + } + } + } + + if (rpc.IsStatic) { + il.Append(Ldc_I4(1)); + il.Append(Stsfld(asm.NetworkBehaviourUtils.GetField(nameof(NetworkBehaviour.InvokeRpc)))); + } else { + il.Append(Ldarg_0()); + il.Append(Ldc_I4(1)); + il.Append(Stfld(asm.NetworkedBehaviour.GetField(nameof(NetworkBehaviour.InvokeRpc)))); + } + + if (!rpc.IsStatic) { + il.Append(Ldarg_0()); + il.Append(Instruction.Create(OpCodes.Castclass, rpc.DeclaringType)); + } + + for (int i = 0; i < parameters.Length; ++i) { + il.Append(Ldloc(parameters[i])); + } + il.Append(Call(rpc)); + if (returnsRpcInvokeInfo) { + il.Append(Pop()); + } + il.Append(Ret()); + } + } + + { + Log.Assert(_rpcCount.TryGetValue(type.FullName, out int count) == false || count == instanceRpcKeys); + _rpcCount[type.FullName] = instanceRpcKeys; + } + } + + private int GetInstanceRpcCount(TypeReference type) { + if (_rpcCount.TryGetValue(type.FullName, out int result)) { + return result; + } + + result = 0; + + var typeDef = type.TryResolve(); + if (typeDef != null) { + + if (typeDef.BaseType != null) { + result += GetInstanceRpcCount(typeDef.BaseType); + } + + result += typeDef.GetMethods() + .Where(x => !x.IsStatic) + .Where(x => x.HasAttribute()) + .Count(); + } + + _rpcCount.Add(type.FullName, result); + return result; + } + + private bool IsInvokeOnlyParameter(ParameterDefinition para) { + if (para.ParameterType.IsSame()) { + return true; + } + return false; + } + + + + void WeaveArrayByteSize(ILWeaverAssembly asm, ILProcessor il, ParameterDefinition para) { + + int size; + + var elementType = para.ParameterType.GetElementType(); + if (elementType.IsPrimitive) { + size = elementType.GetPrimitiveSize(); + } else if (TryGetNetworkWrapperType(elementType, out var wrapInfo)) { + size = GetByteCount(asm, wrapInfo); + } else { + size = GetByteCount(asm, elementType); + } + + // array length + il.Append(Ldarg(para)); + il.Append(Ldlen()); + il.Append(Conv_I4()); + + il.Append(Ldc_I4(size)); + il.Append(Mul()); + + if (elementType.IsPrimitive && (size % Allocator.REPLICATE_WORD_SIZE) != 0) { + // need to align to word count boundaries + il.AppendMacro(AlignToWordSize()); + } + + // store the length + il.Append(Ldc_I4(sizeof(Int32))); + il.Append(Add()); + } + + void WeaveRpcArrayInvoke(ILWeaverAssembly asm, RpcMethodContext ctx, ILProcessor il, VariableDefinition para) { + var elementType = para.VariableType.GetElementType(); + + var intType = asm.Import(typeof(Int32)); + + + // alloc result array + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldind(intType)); + il.Append(Instruction.Create(OpCodes.Newarr, elementType)); + il.Append(Stloc(para)); + + il.AppendMacro(ctx.AddOffset(sizeof(Int32))); + + if (elementType.IsPrimitive) { + using (ctx.AddOffsetScope(il)) { + // dst + il.Append(Ldloc(para)); + + // src + il.AppendMacro(ctx.LoadAddress()); + + var memCpy = new GenericInstanceMethod(asm.Native.GetMethod(nameof(Native.CopyToArray), 2)); + memCpy.GenericArguments.Add(elementType); + il.Append(Call(memCpy)); + + if (elementType.IsPrimitive && (elementType.GetPrimitiveSize() % Allocator.REPLICATE_WORD_SIZE) != 0) { + // need to align to word count boundaries + il.AppendMacro(AlignToWordSize()); + } + }; + } else { + + var arrayIndex = ctx.GetOrCreateVariable("ArrayIndex", asm.Import(typeof(int))); + var exitCondition = Ldloc(arrayIndex); + + il.Append(Ldc_I4(0)); + il.Append(Stloc(arrayIndex)); + il.Append(Br_S(exitCondition)); + + // prepare store + var loopBody = il.AppendReturn(Nop()); + + if (TryGetNetworkWrapperType(elementType, out var wrapInfo)) { + + WeaveNetworkUnwrap(asm, il, ctx, wrapInfo, elementType, + prepareStore: il => { + il.Append(Ldloc(para)); + il.Append(Ldloc(arrayIndex)); + }, + storeResult: il => { + il.Append(Stelem(elementType)); + }); + + } else { + + il.Append(Ldloc(para)); + il.Append(Ldloc(arrayIndex)); + + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldind_or_Ldobj(elementType)); + il.Append(Stelem(elementType)); + + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, elementType))); + } + + // increase index + il.Append(Ldloc(arrayIndex)); + il.Append(Ldc_I4(1)); + il.Append(Add()); + il.Append(Stloc(arrayIndex)); + + // exit condition + il.Append(exitCondition); + il.Append(Ldloc(para)); + il.Append(Ldlen()); + il.Append(Conv_I4()); + il.Append(Blt_S(loopBody)); + } + } + + void WeaveRpcArrayInput(ILWeaverAssembly asm, RpcMethodContext ctx, ILProcessor il, ParameterDefinition para) { + var elementType = para.ParameterType.GetElementType(); + + // store the array size + il.AppendMacro(ctx.LoadAddress()); + + // array length + il.Append(Ldarg(para)); + il.Append(Ldlen()); + il.Append(Conv_I4()); + + il.Append(Stind_I4()); + + il.AppendMacro(ctx.AddOffset(sizeof(Int32))); + + if (elementType.IsPrimitive) { + using (ctx.AddOffsetScope(il)) { + // dst + il.AppendMacro(ctx.LoadAddress()); + + // src + il.Append(Ldarg(para)); + var memCpy = new GenericInstanceMethod(asm.Native.GetMethod(nameof(Native.CopyFromArray), 2)); + memCpy.GenericArguments.Add(elementType); + il.Append(Call(memCpy)); + + if (elementType.IsPrimitive && (elementType.GetPrimitiveSize() % Allocator.REPLICATE_WORD_SIZE) != 0) { + il.AppendMacro(AlignToWordSize()); + } + }; + } else { + + var arrayIndex = ctx.GetOrCreateVariable("arrayIndex", asm.Import(typeof(int))); + + var exitCondition = Ldloc(arrayIndex); + + il.Append(Ldc_I4(0)); + il.Append(Stloc(arrayIndex)); + il.Append(Br_S(exitCondition)); + + var loopBody = il.AppendReturn(Nop()); + + + if (TryGetNetworkWrapperType(elementType, out var wrapInfo)) { + + WeaveNetworkWrap(asm, il, ctx, il => { + il.Append(Ldarg(para)); + il.Append(Ldloc(arrayIndex)); + il.Append(Ldelem(elementType)); + }, wrapInfo); + + } else { + // get array elem + il.AppendMacro(ctx.LoadAddress()); + + il.Append(Ldarg(para)); + il.Append(Ldloc(arrayIndex)); + il.Append(Ldelem(elementType)); + il.Append(Stind_or_Stobj(elementType)); + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, elementType))); + } + + // increase index + il.Append(Ldloc(arrayIndex)); + il.Append(Ldc_I4(1)); + il.Append(Add()); + il.Append(Stloc(arrayIndex)); + + // exit condition + il.Append(exitCondition); + il.Append(Ldarg(para)); + il.Append(Ldlen()); + il.Append(Conv_I4()); + il.Append(Blt_S(loopBody)); + + } + } + + void WeaveNetworkWrap(ILWeaverAssembly asm, ILProcessor il, MethodContext ctx, Action emitGetValue, WrapInfo wrapInfo) { + + if (!wrapInfo.IsRaw) { + il.AppendMacro(ctx.LoadAddress()); + } + + il.AppendMacro(ctx.LoadRunner()); + emitGetValue(il); + + if (wrapInfo.IsRaw) { + il.AppendMacro(ctx.LoadAddress()); + } + + il.Append(Call(ctx.ImportReference(wrapInfo.WrapMethod))); + + if (wrapInfo.IsRaw) { + // read bytes are on top of the stack + il.Append(Ldc_I4(wrapInfo.MaxRawByteCount)); + var validateMethod = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.VerifyRawNetworkWrap))); + validateMethod.GenericArguments.Add(ctx.ImportReference(wrapInfo.Type)); + il.Append(Call(validateMethod)); + + if (ctx.HasOffset) { + il.AppendMacro(AlignToWordSize()); + il.AppendMacro(ctx.AddOffset()); + } else { + il.Append(Pop()); + } + + } else { + var type = ctx.ImportReference(wrapInfo.WrapperType.GetElementType()); + il.Append(Stind_or_Stobj(type)); + + if (ctx.HasOffset) { + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, type))); + } + } + } + + void WeaveNetworkUnwrap(ILWeaverAssembly asm, ILProcessor il, MethodContext ctx, WrapInfo wrapInfo, TypeReference resultType, FieldDefinition previousValue = null, Action prepareStore = null, Action storeResult = null) { + if (resultType == null) { + throw new ArgumentNullException(nameof(resultType)); + } + + var wrapperType = wrapInfo.UnwrapMethod.Parameters[1].ParameterType; + Log.Assert(!wrapperType.IsByReference); + + var typeToLoad = ctx.ImportReference(wrapperType); + + if (wrapInfo.IsRaw || wrapInfo.UnwrapByRef) { + il.AppendMacro(ctx.LoadRunner()); + il.AppendMacro(ctx.LoadAddress()); + if (!wrapInfo.IsRaw) { + il.Append(Ldind_or_Ldobj(typeToLoad)); + } + + VariableDefinition byRefTempVariable = null; + if (previousValue == null) { + byRefTempVariable = ctx.GetOrCreateVariable("RawUnwrap_tmp", ctx.ImportReference(wrapInfo.Type)); + il.Append(Ldloca_S(byRefTempVariable)); + } else { + il.Append(Ldarg_0()); + il.Append(Ldflda(previousValue)); + } + + il.Append(Call(ctx.ImportReference(wrapInfo.UnwrapMethod))); + + if (wrapInfo.IsRaw) { + // check if number of bytes checks out + GenericInstanceMethod validateMethod; + validateMethod = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.VerifyRawNetworkUnwrap), genericArgsCount: 1)); + validateMethod.GenericArguments.Add(resultType); + il.Append(Ldc_I4(wrapInfo.MaxRawByteCount)); + il.Append(Call(validateMethod)); + + if (ctx.HasOffset) { + il.AppendMacro(AlignToWordSize()); + il.AppendMacro(ctx.AddOffset()); + } else { + il.Append(Pop()); + } + + } else { + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, resultType))); + } + + prepareStore?.Invoke(il); + + if (previousValue == null) { + il.Append(Ldloc(byRefTempVariable)); + } else { + il.Append(Ldarg_0()); + il.Append(Ldfld(previousValue)); + } + + if (!wrapInfo.UnwrapMethod.ReturnType.IsSame(resultType)) { + il.Append(Cast(resultType)); + } + + storeResult?.Invoke(il); + + } else { + + // if not by ref, we don't handle the previous value field + Log.Assert(previousValue == null); + + prepareStore?.Invoke(il); + + il.AppendMacro(ctx.LoadRunner()); + il.AppendMacro(ctx.LoadAddress()); + il.Append(Ldind_or_Ldobj(typeToLoad)); + + il.Append(Call(ctx.ImportReference(wrapInfo.UnwrapMethod))); + + if (!wrapInfo.UnwrapMethod.ReturnType.IsSame(resultType)) { + il.Append(Cast(resultType)); + } + + storeResult?.Invoke(il); + + il.AppendMacro(ctx.AddOffset(GetByteCount(asm, resultType))); + } + } + + void WeaveSimulation(ILWeaverAssembly asm, TypeDefinition type) { + + if (type.HasGenericParameters) { + return; + } + + WeaveRpcs(asm, type, allowInstanceRpcs: false); + } + + + private Instruction[] GetInlineFieldInit(MethodDefinition constructor, FieldDefinition field) { + var instructions = constructor.Body.Instructions; + int prevStfld = -1; + for (int i = 0; i < instructions.Count; ++i) { + var instruction = instructions[i]; + if (instruction.OpCode == OpCodes.Stfld) { + // found the store + if (instruction.Operand == field) { + int start = prevStfld + 1; + return instructions.Skip(start).Take(i - start + 1).ToArray(); + } else { + prevStfld = i; + } + } else if (instruction.IsBaseConstructorCall(constructor.DeclaringType)) { + // base constructor init + break; + } + } + return Array.Empty(); + } + + private Instruction[] RemoveInlineFieldInit(TypeDefinition type, FieldDefinition field) { + var constructors = type.GetConstructors().Where(x => !x.IsStatic); + if (!constructors.Any()) { + return Array.Empty(); + } + + var firstConstructor = constructors.First(); + var firstInlineInit = GetInlineFieldInit(firstConstructor, field).ToArray(); + if (firstInlineInit.Length != 0) { + Log.Debug($"Found {field} inline init: {(string.Join("; ", firstInlineInit.Cast()))}"); + } + + foreach (var constructor in constructors.Skip(1)) { + var otherInlineInit = GetInlineFieldInit(constructor, field); + if (!firstInlineInit.SequenceEqual(otherInlineInit, new InstructionEqualityComparer())) { + throw new ILWeaverException($"Expect inline init of {field} to be the same in all constructors," + + $" but there's a difference between {firstConstructor} and {constructor}"); + } + } + + foreach (var constructor in constructors) { + Log.Debug($"Removing inline init of {field} from {constructor}"); + var il = constructor.Body.GetILProcessor(); + var otherInlineInit = GetInlineFieldInit(constructor, field); + foreach (var instruction in otherInlineInit.Reverse()) { + Log.Debug($"Removing {instruction}"); + il.Remove(instruction); + } + } + + return firstInlineInit; + } + + private static bool IsMakeInitializerCall(Instruction instruction) { + if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference method) { + if (method.DeclaringType.IsSame() && method.Name == nameof(NetworkBehaviour.MakeInitializer)) { + return true; + } + } + return false; + } + + private static bool IsMakeRefOrMakePtrCall(Instruction instruction) { + if (instruction.OpCode == OpCodes.Call && instruction.Operand is MethodReference method) { + if (method.DeclaringType.IsSame() && ( + method.Name == nameof(NetworkBehaviour.MakeRef) || method.Name == nameof(NetworkBehaviour.MakePtr) + )) { + return true; + } + } + return false; + } + + private void CheckIfMakeInitializerImplicitCast(Instruction instruction) { + if (instruction.OpCode == OpCodes.Call && (instruction.Operand as MethodReference)?.Name == "op_Implicit") { + // all good + } else { + throw new ILWeaverException($"Expected an implicit cast, got {instruction}"); + } + } + + private void ReplaceBackingFieldInInlineInit(ILWeaverAssembly asm, FieldDefinition backingField, FieldDefinition field, ILProcessor il, Instruction[] instructions) { + bool nextImplicitCast = false; + foreach (var instruction in instructions) { + if (nextImplicitCast) { + CheckIfMakeInitializerImplicitCast(instruction); + nextImplicitCast = false; + il.Remove(instruction); + } else if (instruction.OpCode == OpCodes.Stfld && instruction.Operand == backingField) { + instruction.Operand = field; + } else if (IsMakeInitializerCall(instruction)) { + // dictionaries need one extra step, if using SerializableDictionary :( + if (ILWeaverSettings.UseSerializableDictionaryForNetworkDictionaryProperties() && backingField.FieldType.IsNetworkDictionary(out var keyType, out var valueType)) { + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.MakeSerializableDictionary))); + m.GenericArguments.Add(keyType); + m.GenericArguments.Add(valueType); + Log.Debug($"Inline init for {field}, replacing {instruction.Operand} with {m}"); + instruction.Operand = m; + } else { + // remove the op, it will be fine + Log.Debug($"Inline init for {field}, removing {instruction}"); + il.Remove(instruction); + } + nextImplicitCast = true; + } + } + } + + static IEnumerable AllTypeDefs(TypeDefinition definitions) { + yield return definitions; + + if (definitions.HasNestedTypes) { + foreach (var nested in definitions.NestedTypes.SelectMany(AllTypeDefs)) { + yield return nested; + } + } + } + + static IEnumerable AllTypeDefs(Collection definitions) { + return definitions.SelectMany(AllTypeDefs); + } + + public bool Weave(ILWeaverAssembly asm) { + // if we don't have the weaved assembly attribute, we need to do weaving and insert the attribute + if (asm.CecilAssembly.HasAttribute() != false) { + return false; + } + + using (Log.ScopeAssembly(asm.CecilAssembly)) { + // grab main module .. this contains all the types we need + var module = asm.CecilAssembly.MainModule; + var moduleAllTypes = AllTypeDefs(module.Types).ToArray(); + + // go through all types and check for network behaviours + foreach (var t in moduleAllTypes) { + if (t.IsValueType && t.Is()) { + try { + WeaveStruct(asm, t, null); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave struct {t}", ex); + } + } + } + + foreach (var t in moduleAllTypes) { + if (t.IsValueType && t.Is()) { + try { + WeaveInput(asm, t); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave input {t}", ex); + } + } + } + + foreach (var t in moduleAllTypes) { + if (t.IsSubclassOf()) { + try { + WeaveBehaviour(asm, t); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave behaviour {t}", ex); + } + } else if (t.IsSubclassOf()) { + try { + WeaveSimulation(asm, t); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave behaviour {t}", ex); + } + } + } + + // only if it was modified + if (asm.Modified) { + // add weaved assembly attribute to this assembly + asm.CecilAssembly.CustomAttributes.Add(new CustomAttribute(typeof(NetworkAssemblyWeavedAttribute).GetConstructor(asm))); + } + + return asm.Modified; + } + } + } +} +#endif + + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaver.INetworkedStruct.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.Compilation; + using UnityEngine; + using System.Runtime.CompilerServices; + using static Fusion.CodeGen.ILWeaverOpCodes; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + using Mono.Collections.Generic; + using CompilerAssembly = UnityEditor.Compilation.Assembly; + using FieldAttributes = Mono.Cecil.FieldAttributes; + using MethodAttributes = Mono.Cecil.MethodAttributes; + using ParameterAttributes = Mono.Cecil.ParameterAttributes; + + unsafe partial class ILWeaver { + + const int WordSize = Allocator.REPLICATE_WORD_SIZE; + + private bool IsTypeBlittable(ILWeaverAssembly asm, TypeReference type) { + if (type.IsPrimitive) { + return type.IsIntegral(); + } else if (!type.IsValueType) { + return false; + } else if (type.IsVector2() || type.IsVector3() || type.IsQuaternion()) { + return false; + } else if (type.IsNetworkArray() || type.IsNetworkDictionary() || type.IsNetworkList()) { + return false; + } else { + return true; + } + } + + public void WeaveStruct(ILWeaverAssembly asm, TypeDefinition type, TypeReference typeRef) { + ILWeaverException.DebugThrowIf(!type.Is(), $"Not a {nameof(INetworkStruct)}"); + + string typeKey; + if (type.HasGenericParameters) { + Log.Assert(typeRef?.IsGenericInstance == true); + typeKey = typeRef.FullName; + } else { + typeKey = type.FullName; + } + + if (type.TryGetAttribute(out var attribute)) { + + if (_typeData.ContainsKey(typeKey) == false) { + var wordCount = attribute.GetAttributeArgument(0); + if (attribute.TryGetAttributeArgument(1, out bool value, false) && value) { + Log.Assert(typeRef?.IsGenericInstance == true); + foreach (var gen in ((GenericInstanceType)typeRef).GenericArguments) { + if (gen.IsValueType && gen.Is()) { + wordCount += GetTypeWordCount(asm, gen); + } + } + } + + _typeData.Add(typeKey, new TypeMetaData { + WordCount = wordCount, + Definition = type, + Reference = type + }); + } + + return; + } + + using (Log.ScopeStruct(type)) { + + int wordCount = WeaveStructInner(asm, type); + + // track type data + _typeData.Add(typeKey, new TypeMetaData { + WordCount = wordCount, + Definition = type, + Reference = type + }); + + // add new attribute + type.AddAttribute(asm, wordCount); + } + } + + int WeaveStructInner(ILWeaverAssembly asm, TypeDefinition type) { + // flag asm as modified + asm.Modified = true; + + // set as explicit layout + type.IsExplicitLayout = true; + + // clear all backing fields + foreach (var property in type.Properties) { + if (!IsWeavableProperty(property, out var propertyInfo)) { + continue; + } + + property.ThrowIfStatic(); + + if (IsTypeBlittable(asm, property.PropertyType)) { + Log.Warn($"Networked property {property} should be replaced with a regular field. For structs, " + + $"[Networked] attribute should to be applied only on collections, booleans, floats and vectors."); + } + + int fieldIndex = type.Fields.Count; + + if (propertyInfo.BackingField != null) { + if (!propertyInfo.BackingField.FieldType.IsValueType) { + Log.Warn($"Networked property {property} has a backing field that is not a value type. To keep unmanaged status," + + $" the accessor should follow \"{{ get => default; set {{}} }}\" pattern"); + } + + fieldIndex = type.Fields.IndexOf(propertyInfo.BackingField); + if (fieldIndex >= 0) { + type.Fields.RemoveAt(fieldIndex); + } + } + + try { + var propertyWordCount = GetPropertyWordCount(asm, property); + + property.GetMethod?.RemoveAttribute(asm); + property.SetMethod?.RemoveAttribute(asm); + + var getIL = property.GetMethod.Body.GetILProcessor(); + getIL.Clear(); + getIL.Body.Variables.Clear(); + + var setIL = property.SetMethod?.Body.GetILProcessor(); + if (setIL != null) { + setIL.Clear(); + setIL.Body.Variables.Clear(); + } + + var backingFieldName = $"_{property.Name}"; + var fixedBufferInfo = CacheGetFixedBuffer(asm, propertyWordCount); + var surrogateType = CacheGetUnitySurrogate(asm, property); + var storageField = new FieldDefinition($"_{property.Name}", FieldAttributes.Private, fixedBufferInfo.Type); + + int capacity; + if (property.PropertyType.IsNetworkDictionary()) { + capacity = GetStaticDictionaryCapacity(property); + } else { + capacity = GetCapacity(property, 1); + } + storageField.AddAttribute(asm); + storageField.AddAttribute(asm, property.PropertyType, surrogateType, capacity); + + type.Fields.Insert(fieldIndex, storageField); + + // move field attributes, if any + if (propertyInfo.BackingField != null) { + MoveBackingFieldAttributes(asm, propertyInfo.BackingField, storageField); + } + MovePropertyAttributesToBackingField(asm, property, storageField); + + InjectValueAccessor(asm, getIL, setIL, property, property.PropertyType, OpCodes.Ldarg_1, (il, offset) => { + + var m = new GenericInstanceMethod(asm.Native.GetMethod(nameof(Native.ReferenceToPointer))); + m.GenericArguments.Add(storageField.FieldType); + + il.Append(Ldarg_0()); + il.Append(Ldflda(storageField)); + + il.Append(Call(m)); + + }, false); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave property {property}", ex); + } + } + + // figure out word counts for everything + var wordCount = 0; + + foreach (var field in type.Fields) { + + // skip statics + if (field.IsStatic) { + continue; + } + + // set offset + field.Offset = wordCount * Allocator.REPLICATE_WORD_SIZE; + + try { + // increase block count + wordCount += GetTypeWordCount(asm, field.FieldType); + } catch (Exception ex) { + throw new ILWeaverException($"Failed to get word count of field {field}", ex); + } + } + + return wordCount; + } + } +} +#endif + + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaver.NetworkBehaviour.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using UnityEditor; + using UnityEditor.Compilation; + using UnityEngine; + using System.Runtime.CompilerServices; + using static Fusion.CodeGen.ILWeaverOpCodes; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + using Mono.Collections.Generic; + using CompilerAssembly = UnityEditor.Compilation.Assembly; + using FieldAttributes = Mono.Cecil.FieldAttributes; + using MethodAttributes = Mono.Cecil.MethodAttributes; + using ParameterAttributes = Mono.Cecil.ParameterAttributes; + using UnityEngine.Scripting; + + unsafe partial class ILWeaver { + + FieldDefinition AddNetworkBehaviourBackingField(ILWeaverAssembly asm, PropertyDefinition property) { + + TypeReference fieldType = property.PropertyType; + if (fieldType.IsPointer || fieldType.IsByReference) { + fieldType = fieldType.GetElementType(); + } else if (fieldType.IsNetworkArray(out var elementType) || fieldType.IsNetworkList(out elementType)) { + fieldType = TypeReferenceRocks.MakeArrayType(elementType); + } else if (fieldType.IsNetworkDictionary(out var keyType, out var valueType)) { + if (ILWeaverSettings.UseSerializableDictionaryForNetworkDictionaryProperties()) { + fieldType = TypeReferenceRocks.MakeGenericInstanceType(asm.Import(typeof(SerializableDictionary<,>)), keyType, valueType); + } else { + fieldType = TypeReferenceRocks.MakeGenericInstanceType(asm.Import(typeof(Dictionary<,>)), keyType, valueType); + } + } + + var field = new FieldDefinition(GetInspectorFieldName(property.Name), FieldAttributes.Private, fieldType); + return field; + } + + private void MoveBackingFieldAttributes(ILWeaverAssembly asm, FieldDefinition backingField, FieldDefinition storageField) { + if (backingField.IsNotSerialized) { + storageField.IsNotSerialized = true; + } + + foreach (var attrib in backingField.CustomAttributes) { + if (attrib.AttributeType.Is() || + attrib.AttributeType.Is()) { + continue; + } + storageField.CustomAttributes.Add(attrib); + } + } + + private void MovePropertyAttributesToBackingField(ILWeaverAssembly asm, PropertyDefinition property, FieldDefinition field) { + bool hasNonSerialized = false; + + foreach (var attribute in property.CustomAttributes) { + if (attribute.AttributeType.IsSame() || + attribute.AttributeType.IsSame() || + attribute.AttributeType.IsSame() || + attribute.AttributeType.IsSame()) { + continue; + } + + var attribDef = attribute.AttributeType.TryResolve(); + if (attribDef == null) { + Log.Warn($"Failed to resolve {attribute.AttributeType}, not going to try to apply on {field}"); + continue; + } + + + if (attribDef.TryGetAttribute(out var proxy)) { + Log.Debug($"Found proxy attribute {attribute.AttributeType}, applying to {field}"); + var attribTypeRef = proxy.GetAttributeArgument(0); + var attribTypeDef = attribTypeRef.Resolve(); + + if (attribTypeDef.TryGetMatchingConstructor(attribute.Constructor.Resolve(), out var constructor)) { + field.CustomAttributes.Add(new CustomAttribute(property.Module.ImportReference(constructor), attribute.GetBlob())); + + if (attribute.AttributeType.IsSame()) { + Log.Debug($"{field} marked as NonSerialized, SerializeField will not be applied"); + hasNonSerialized = true; + } + } else { + Log.Warn($"Failed to find matching constructor of {attribTypeDef} for {attribute.Constructor} (field {field})"); + } + + continue; + } + + if (attribDef.TryGetAttribute(out var usage)) { + var targets = usage.GetAttributeArgument(0); + if ((targets & AttributeTargets.Field) != AttributeTargets.Field) { + Log.Debug($"Attribute {attribute.AttributeType} can't be applied on a field ({field}), skipping."); + continue; + } + } + + Log.Debug($"Copying {attribute.AttributeType} to {field}"); + field.CustomAttributes.Add(new CustomAttribute(attribute.Constructor, attribute.GetBlob())); + } + + if (!hasNonSerialized && !property.GetMethod.IsPrivate) { + if (field.IsNotSerialized) { + // prohibited + } else if (field.HasAttribute()) { + // already added + } else { + field.AddAttribute(asm); + } + } + } + + public void WeaveBehaviour(ILWeaverAssembly asm, TypeDefinition type) { + if (type.HasGenericParameters) { + return; + } + + ILWeaverException.DebugThrowIf(!type.IsSubclassOf(), $"Not a {nameof(NetworkBehaviour)}"); + + if (type.TryGetAttribute(out var weavedAttribute)) { + int weavedSize = weavedAttribute.GetAttributeArgument(0); + + if (_networkedBehaviourTypeData.TryGetValue(type.FullName, out var metaData)) { + Debug.Assert(weavedSize < 0 || weavedSize == metaData.BlockCount); + } else { + _networkedBehaviourTypeData.Add(type.FullName, new BehaviourMetaData { + Definition = type, + BlockCount = weavedSize >= 0 ? weavedSize : GetNetworkBehaviourWordCount(asm, type) + }); + } + + return; + } + + // flag as modified + asm.Modified = true; + + using (Log.ScopeBehaviour(type)) { + + var changed = asm.Import(typeof(Changed<>)).MakeGenericInstanceType(type); + var changedDelegate = asm.Import(typeof(ChangedDelegate<>)).MakeGenericInstanceType(type); + var networkBehaviourCallbacks = asm.Import(typeof(NetworkBehaviourCallbacks<>)).MakeGenericInstanceType(type); + type.Fields.Add(new FieldDefinition("$IL2CPP_CHANGED", FieldAttributes.Static, changed)); + type.Fields.Add(new FieldDefinition("$IL2CPP_CHANGED_DELEGATE", FieldAttributes.Static, changedDelegate)); + type.Fields.Add(new FieldDefinition("$IL2CPP_NETWORK_BEHAVIOUR_CALLBACKS", FieldAttributes.Static, networkBehaviourCallbacks)); + + // get block count of parent as starting point for ourselves + var wordCount = GetNetworkBehaviourWordCount(asm, type.BaseType.Resolve()); + + // this is the data field which holds this behaviours root pointer + var dataField = GetFieldFromNetworkedBehaviour(asm, type, PTR_FIELD_NAME); + + // find onspawned method + Func createOverride = (name) => { + var result = type.Methods.FirstOrDefault(x => x.Name == name); + if (result != null) { + // need to find the placeholder method + var placeholderMethodName = asm.NetworkedBehaviour.GetMethod(nameof(NetworkBehaviour.InvokeWeavedCode)).FullName; + var placeholders = result.Body.Instructions + .Where(x => x.OpCode == OpCodes.Call && x.Operand is MethodReference && ((MethodReference)x.Operand).FullName == placeholderMethodName) + .ToArray(); + + if (placeholders.Length != 1) { + throw new ILWeaverException($"When overriding {name} in a type with [Networked] properties, make sure to call {placeholderMethodName} exactly once somewhere."); + } + + var placeholder = placeholders[0]; + var il = result.Body.GetILProcessor(); + + var jumpTarget = Nop(); + var returnTarget = Nop(); + + // this is where to jump after weaved code's done + il.InsertAfter(placeholder, returnTarget); + il.InsertAfter(placeholder, Br(jumpTarget)); + + il.Append(jumpTarget); + return (result, Br(returnTarget)); + } + + result = new MethodDefinition(name, MethodAttributes.Public, asm.CecilAssembly.MainModule.ImportReference(typeof(void))) { + IsVirtual = true, + IsHideBySig = true, + IsReuseSlot = true + }; + + var baseMethod = FindMethodInParent(asm, type, name, nameof(SimulationBehaviour)); + + // call base method + if (baseMethod != null) { + if (baseMethod.DeclaringType.IsSame()) { + // don't call base method + foreach (var parameter in baseMethod.Parameters) { + result.Parameters.Add(new ParameterDefinition(parameter.ParameterType)); + } + } else { + var bodyIL = result.Body.GetILProcessor(); + + bodyIL.Append(Instruction.Create(OpCodes.Ldarg_0)); + + foreach (var parameter in baseMethod.Parameters) { + var p = new ParameterDefinition(parameter.ParameterType); + result.Parameters.Add(p); + bodyIL.Append(Ldarg(p)); + } + + bodyIL.Append(Instruction.Create(OpCodes.Call, baseMethod)); + } + } + + type.Methods.Add(result); + return (result, Ret()); + }; + + var setDefaults = new Lazy<(MethodDefinition, Instruction)>(() => createOverride(nameof(NetworkBehaviour.CopyBackingFieldsToState))); + var getDefaults = new Lazy<(MethodDefinition, Instruction)>(() => createOverride(nameof(NetworkBehaviour.CopyStateToBackingFields))); + + FieldDefinition lastAddedFieldWithKnownPosition = null; + List fieldsWithUncertainPosition = new List(); + + foreach (var property in type.Properties) { + if (IsWeavableProperty(property, out var propertyInfo) == false) { + continue; + } + + property.ThrowIfStatic(); + + if (!string.IsNullOrEmpty(propertyInfo.OnChanged)) { + WeaveChangedHandler(asm, property, propertyInfo.OnChanged); + } + + + if (property.PropertyType.IsPointer || property.PropertyType.IsByReference) { + var elementType = property.PropertyType.GetElementType(); + if (!IsTypeBlittable(asm, elementType)) { + Log.Warn($"Property {property} type ({elementType}) is not safe to in pointer/reference properties. " + + $"Consider wrapping it with a struct implementing INetworkStruct and a [Networked] property."); + } + } + + + try { + // try to maintain fields order + int backingFieldIndex = type.Fields.Count; + if (propertyInfo.BackingField != null) { + backingFieldIndex = type.Fields.IndexOf(propertyInfo.BackingField); + if (backingFieldIndex >= 0) { + type.Fields.RemoveAt(backingFieldIndex); + } else { + Log.Warn($"Unable to find backing field for {property}"); + backingFieldIndex = type.Fields.Count; + } + } + + var readOnlyInit = GetReadOnlyPropertyInitializer(property); + + // prepare getter/setter methods + + + var (getter, setter) = PreparePropertyForWeaving(property); + + // capture word count in case we re-use the lambda that is created later on ... + var wordOffset = wordCount; + + // perform injection + InjectValueAccessor(asm, getter.Body.GetILProcessor(), setter?.Body?.GetILProcessor(), property, property.PropertyType, OpCodes.Ldarg_1, + (il, offset) => LoadDataAddress(il, dataField, wordOffset + offset), true); + + var propertyWordCount = GetPropertyWordCount(asm, property); + + // step up wordcount + wordCount += propertyWordCount; + + // inject attribute to poll weaver data during runtime + property.AddAttribute(asm, wordOffset, propertyWordCount); + + + + if (property.HasAttribute() || propertyInfo.BackingField?.IsNotSerialized == true) { + // so the property is not serialized, so there will be no backing field. + + IEnumerable fieldInit = null; + VariableDefinition[] fieldInitLocalVariables = null; + + if (readOnlyInit != null) { + fieldInit = readOnlyInit.Value.Instructions; + fieldInitLocalVariables = readOnlyInit.Value.Variables; + } else { + fieldInit = RemoveInlineFieldInit(type, propertyInfo.BackingField); + fieldInitLocalVariables = Array.Empty(); + } + + if (fieldInit?.Any() == true) { + // need to patch defaults with this, but only during the initial set + var il = setDefaults.Value.Item1.Body.GetILProcessor(); + var postInit = Nop(); + il.Append(Ldarg_1()); + il.Append(Brfalse(postInit)); + + foreach (var loc in fieldInitLocalVariables) { + il.Body.Variables.Add(loc); + } + + if (property.PropertyType.IsPointer || + property.PropertyType.IsByReference) { + // load up address + il.Append(Ldarg_0()); + il.Append(Call(getter)); + } else if (property.PropertyType.IsNetworkArray() || + property.PropertyType.IsNetworkList() || + property.PropertyType.IsNetworkDictionary()) { + + var first = fieldInit.First(); + // needs some special init + Log.AssertMessage(first.OpCode == OpCodes.Ldarg_0, $"Expected Ldarg_0, got: {first}"); + il.Append(first); + il.Append(Call(getter)); + fieldInit = fieldInit.Skip(1); + } + + bool skipImplicitInitializerCast = false; + foreach (var instruction in fieldInit) { + if (instruction.IsLdlocWithIndex(out var index)) { + var repl = Ldloc(fieldInitLocalVariables[index], il.Body.Method); + il.Append(repl); + } else if (skipImplicitInitializerCast) { + skipImplicitInitializerCast = false; + CheckIfMakeInitializerImplicitCast(instruction); + } else if (instruction.OpCode == OpCodes.Stfld && instruction.Operand == propertyInfo.BackingField) { + // arrays and dictionaries don't have setters + if (property.PropertyType.IsPointer || property.PropertyType.IsByReference) { + il.Append(Stind_or_Stobj(property.PropertyType.GetElementType())); + } else if (property.PropertyType.IsNetworkArray(out var elementType)) { + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkArray))); + m.GenericArguments.Add(elementType); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + } else if (property.PropertyType.IsNetworkList(out elementType)) { + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkList))); + m.GenericArguments.Add(elementType); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + } else if (property.PropertyType.IsNetworkDictionary(out var keyType, out var valueType)) { + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkDictionary))); + m.GenericArguments.Add(TypeReferenceRocks.MakeGenericInstanceType(asm.Import(typeof(Dictionary<,>)), keyType, valueType)); + m.GenericArguments.Add(keyType); + m.GenericArguments.Add(valueType); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + } else { + il.Append(Call(setter)); + } + } else if (IsMakeInitializerCall(instruction)) { + skipImplicitInitializerCast = true; + } else { + il.Append(instruction); + } + } + + if (property.PropertyType.IsPointer || property.PropertyType.IsByReference) { + il.Append(Stind_or_Stobj(property.PropertyType.GetElementType())); + } + + il.Append(postInit); + } + + } else { + + FieldDefinition defaultField; + + if (string.IsNullOrEmpty(propertyInfo.DefaultFieldName)) { + defaultField = AddNetworkBehaviourBackingField(asm, property); + if (propertyInfo.BackingField != null) { + type.Fields.Insert(backingFieldIndex, defaultField); + MoveBackingFieldAttributes(asm, propertyInfo.BackingField, defaultField); + + if (lastAddedFieldWithKnownPosition == null) { + // fixup fields that have been added without knowing their index + foreach (var f in fieldsWithUncertainPosition) { + type.Fields.Remove(f); + } + + var index = type.Fields.IndexOf(defaultField); + fieldsWithUncertainPosition.Reverse(); + foreach (var f in fieldsWithUncertainPosition) { + type.Fields.Insert(index, f); + } + } + + lastAddedFieldWithKnownPosition = defaultField; + + } else { + if (lastAddedFieldWithKnownPosition == null) { + // not sure where to put this... append + type.Fields.Add(defaultField); + fieldsWithUncertainPosition.Add(defaultField); + } else { + // add after the previous field + var index = type.Fields.IndexOf(lastAddedFieldWithKnownPosition); + Log.Assert(index >= 0); + + type.Fields.Insert(index+1, defaultField); + lastAddedFieldWithKnownPosition = defaultField; + } + } + MovePropertyAttributesToBackingField(asm, property, defaultField); + } else { + defaultField = property.DeclaringType.GetFieldOrThrow(propertyInfo.DefaultFieldName); + } + + // in each constructor, replace inline init, if present + foreach (var constructor in type.GetConstructors()) { + + if (readOnlyInit != null) { + var il = constructor.Body.GetILProcessor(); + + Instruction before = il.Body.Instructions[0]; + { + // find where to plug in; after last stfld, but before base constructor call + for (int i = 0; i < il.Body.Instructions.Count; ++i) { + var instruction = il.Body.Instructions[i]; + if (instruction.IsBaseConstructorCall(type)) { + break; + } else if (instruction.OpCode == OpCodes.Stfld) { + before = il.Body.Instructions[i + 1]; + } + } + } + + // clone variables + var (instructions, variables) = CloneInstructions(readOnlyInit.Value.Instructions, readOnlyInit.Value.Variables); + + foreach (var variable in variables) { + il.Body.Variables.Add(variable); + } + + il.InsertBefore(before, Ldarg_0()); + foreach (var instruction in instructions) { + il.InsertBefore(before, instruction); + } + il.InsertBefore(before, Stfld(defaultField)); + } else { + // remove the inline init, if present + var init = GetInlineFieldInit(constructor, propertyInfo.BackingField); + if (init.Length > 0) { + ReplaceBackingFieldInInlineInit(asm, propertyInfo.BackingField, defaultField, constructor.Body.GetILProcessor(), init); + } + } + } + + defaultField.AddAttribute(asm, property.Name, wordOffset, propertyWordCount); + + { + var il = setDefaults.Value.Item1.Body.GetILProcessor(); + + if (property.PropertyType.IsByReference || property.PropertyType.IsPointer) { + il.Append(Ldarg_0()); + il.Append(Call(getter)); + + il.Append(Ldarg_0()); + il.Append(Ldfld(defaultField)); + + il.Append(Stind_or_Stobj(property.PropertyType.GetElementType())); + + } else if (property.PropertyType.IsNetworkDictionary(out var keyType, out var valueType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkDictionary))); + m.GenericArguments.Add(defaultField.FieldType); + m.GenericArguments.Add(keyType); + m.GenericArguments.Add(valueType); + + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldfld(defaultField)); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + + } else if (property.PropertyType.IsNetworkArray(out var elementType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkArray))); + m.GenericArguments.Add(elementType); + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldfld(defaultField)); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + + } else if (property.PropertyType.IsNetworkList(out elementType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.InitializeNetworkList))); + m.GenericArguments.Add(elementType); + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldfld(defaultField)); + il.Append(Ldstr(property.Name)); + il.Append(Call(m)); + + + } else { + Log.AssertMessage(setter != null, $"{property} expected to have a setter"); + il.Append(Ldarg_0()); + il.Append(Ldarg_0()); + il.Append(Ldfld(defaultField)); + il.Append(Call(setter)); + } + } + + { + var il = getDefaults.Value.Item1.Body.GetILProcessor(); + + if (property.PropertyType.IsByReference || property.PropertyType.IsPointer) { + il.Append(Ldarg_0()); + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldind_or_Ldobj(property.PropertyType.GetElementType())); + il.Append(Stfld(defaultField)); + + } else if (property.PropertyType.IsNetworkDictionary(out var keyType, out var valueType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.CopyFromNetworkDictionary))); + m.GenericArguments.Add(defaultField.FieldType); + m.GenericArguments.Add(keyType); + m.GenericArguments.Add(valueType); + + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldflda(defaultField)); + il.Append(Call(m)); + + } else if (property.PropertyType.IsNetworkArray(out var elementType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.CopyFromNetworkArray))); + m.GenericArguments.Add(elementType); + + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldflda(defaultField)); + il.Append(Call(m)); + + } else if (property.PropertyType.IsNetworkList(out elementType)) { + + var m = new GenericInstanceMethod(asm.NetworkBehaviourUtils.GetMethod(nameof(NetworkBehaviourUtils.CopyFromNetworkList))); + m.GenericArguments.Add(elementType); + + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Ldarg_0()); + il.Append(Ldflda(defaultField)); + il.Append(Call(m)); + + } else { + Log.AssertMessage(getter != null, $"{property} expected to have a getter"); + il.Append(Ldarg_0()); + il.Append(Ldarg_0()); + il.Append(Call(getter)); + il.Append(Stfld(defaultField)); + } + } + } + } catch (Exception ex) { + throw new ILWeaverException($"Failed to weave property {property}", ex); + } + } + + if (setDefaults.IsValueCreated) { + var (method, instruction) = setDefaults.Value; + method.Body.GetILProcessor().Append(instruction); + } + if (getDefaults.IsValueCreated) { + var (method, instruction) = getDefaults.Value; + method.Body.GetILProcessor().Append(instruction); + } + + // add meta attribute + var metaAttribute = GetMetaAttributeConstructor(asm); + var metaAttributeCtor = new CustomAttribute(metaAttribute); + metaAttributeCtor.ConstructorArguments.Add(new CustomAttributeArgument(ImportType(asm), wordCount)); + type.CustomAttributes.Add(metaAttributeCtor); + + // add to type data lookup + _networkedBehaviourTypeData.Add(type.FullName, new BehaviourMetaData { + Definition = type, + BlockCount = wordCount + }); + + WeaveRpcs(asm, type); + } + } + + private void WeaveChangedHandler(ILWeaverAssembly asm, PropertyDefinition property, string handlerName) { + + // find the handler + { + foreach (var declaringType in property.DeclaringType.GetHierarchy()) { + var candidates = declaringType.GetMethods() + .Where(x => x.IsStatic) + .Where(x => x.Name == handlerName) + .Where(x => x.HasParameters && x.Parameters.Count == 1) + .Where(x => { + var parameterType = x.Parameters[0].ParameterType; + if (!parameterType.IsGenericInstance) { + return false; + } + var openGenericType = parameterType.GetElementType(); + if (!openGenericType.IsSame(typeof(Changed<>))) { + return false; + } + var behaviourType = ((GenericInstanceType)parameterType).GenericArguments[0]; + if (!property.DeclaringType.Is(behaviourType)) { + return false; + } + return true; + }) + .ToList(); + + if (candidates.Count > 1) { + throw new ILWeaverException($"Ambiguous match for OnChanged handler for {property}: {string.Join("; ", candidates)}"); + } else if (candidates.Count == 1) { + + var handler = candidates[0]; + + Log.Debug($"OnChanged handler for {property}: {handler}"); + // add preserve attribute, if not added already + if (!handler.TryGetAttribute(out _)) { + handler.AddAttribute(asm); + Log.Debug($"Added {nameof(PreserveAttribute)} to {handler}"); + } + return; + } + } + } + + throw new ILWeaverException($"No match found for OnChanged handler for {property}"); + } + + struct ReadOnlyInitializer { + public Instruction[] Instructions; + public VariableDefinition[] Variables; + } + + private ReadOnlyInitializer? GetReadOnlyPropertyInitializer(PropertyDefinition property) { + if (property.PropertyType.IsPointer || property.PropertyType.IsByReference) { + // need to check if there's MakeRef/Ptr before getter gets obliterated + var instructions = property.GetMethod.Body.Instructions; + + for (int i = 0; i < instructions.Count; ++i) { + var instr = instructions[i]; + if (IsMakeRefOrMakePtrCall(instr)) { + Log.Debug($"Property {property} has MakePtr/MakeRef init"); + // found it! + if (i == 0) { + // seems we're dealing with an empty MakeRef/MakePtr + return null; + } else { + return new ReadOnlyInitializer() { + Instructions = instructions.Take(i).ToArray(), + Variables = property.GetMethod.Body.Variables.ToArray() + }; + } + } + } + } + return null; + } + + private (Instruction[], VariableDefinition[]) CloneInstructions(Instruction[] source, VariableDefinition[] sourceVariables) { + + var constructor = typeof(Instruction).GetConstructor( + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, + new[] { typeof(OpCode), typeof(object) }, null); + + // shallow copy + var result = source.Select(x => (Instruction)constructor.Invoke(new[] { x.OpCode, x.Operand })) + .ToArray(); + + var variableMapping = new Dictionary(); + + // now need to resolve local variables and jump targets + foreach (var instruction in result) { + + if (instruction.IsLdlocWithIndex(out var locIndex) || instruction.IsStlocWithIndex(out locIndex)) { + var variable = sourceVariables[locIndex]; + if (!variableMapping.TryGetValue(variable, out var replacement)) { + replacement = new VariableDefinition(variable.VariableType); + variableMapping.Add(variable, replacement); + } + if (instruction.IsLdlocWithIndex(out _)) { + instruction.OpCode = OpCodes.Ldloc; + } else { + instruction.OpCode = OpCodes.Stloc; + } + instruction.Operand = replacement; + } else if (instruction.Operand is VariableDefinition variable) { + if (!variableMapping.TryGetValue(variable, out var replacement)) { + replacement = new VariableDefinition(variable.VariableType); + variableMapping.Add(variable, replacement); + } + instruction.Operand = replacement; + } else if (instruction.Operand is Instruction target) { + var targetIndex = Array.IndexOf(source, target); + Log.Assert(targetIndex >= 0); + instruction.Operand = result[targetIndex]; + } else if (instruction.Operand is Instruction[] targets) { + instruction.Operand = targets.Select(x => { + var targetIndex = Array.IndexOf(source, x); + Log.Assert(targetIndex >= 0); + return result[targetIndex]; + }); + } else if (instruction.Operand is ParameterDefinition) { + throw new NotSupportedException(); + } + } + + return (result, variableMapping.Values.ToArray()); + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverAssembly.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using Assembly = UnityEditor.Compilation.Assembly; + + using Mono.Cecil; + + public class ILWeaverImportedType { + public Type ClrType; + public ILWeaverAssembly Assembly; + public List BaseDefinitions; + public TypeReference Reference; + + Dictionary _fields = new Dictionary(); + Dictionary<(string, int?), MethodReference> _methods = new Dictionary<(string, int?), MethodReference>(); + Dictionary _propertiesGet = new Dictionary(); + Dictionary _propertiesSet = new Dictionary(); + + public static implicit operator TypeReference(ILWeaverImportedType type) => type.Reference; + + public ILWeaverImportedType(ILWeaverAssembly asm, Type type) { + ClrType = type; + Assembly = asm; + + Type baseType = type; + BaseDefinitions = new List(); + // Store the type, and each of its base types - so we can later find fields/properties/methods in the base class. + while (baseType != null) { + BaseDefinitions.Add(asm.CecilAssembly.MainModule.ImportReference(baseType).Resolve()); + baseType = baseType.BaseType; + } + + Reference = asm.CecilAssembly.MainModule.ImportReference(BaseDefinitions[0]); + } + + public FieldReference GetField(string name) { + bool found = _fields.TryGetValue(name, out var fieldRef); + if (found == false) { + for (int i = 0; i < BaseDefinitions.Count; ++i) { + FieldDefinition typeDef = BaseDefinitions[i].Fields.FirstOrDefault(x => x.Name == name); + if (typeDef != null) { + fieldRef = Assembly.CecilAssembly.MainModule.ImportReference(typeDef); + _fields.Add(name, fieldRef); + return fieldRef; + } + } + } + return fieldRef; + } + + public MethodReference GetProperty(string name) { + bool found = _propertiesGet.TryGetValue(name, out var methRef); + if (found == false) { + for (int i = 0; i < BaseDefinitions.Count; ++i) { + PropertyDefinition typeDef = BaseDefinitions[i].Properties.FirstOrDefault(x => x.Name == name); + if (typeDef != null) { + methRef = Assembly.CecilAssembly.MainModule.ImportReference(typeDef.GetMethod); + _propertiesGet.Add(name, methRef); + return methRef; + } + } + } + return methRef; + } + + public MethodReference SetProperty(string name) { + bool found = _propertiesSet.TryGetValue(name, out var methRef); + if (found == false) { + for (int i = 0; i < BaseDefinitions.Count; ++i) { + PropertyDefinition def = BaseDefinitions[i].Properties.FirstOrDefault(x => x.Name == name); + if (def != null) { + methRef = Assembly.CecilAssembly.MainModule.ImportReference(def.SetMethod); + _propertiesSet.Add(name, methRef); + return methRef; + } + } + } + return methRef; + } + + public MethodReference GetMethod(string name, int? argsCount = null, int? genericArgsCount = null) { + bool found = _methods.TryGetValue((name, argsCount), out var methRef); + if (found == false) { + for (int i = 0; i < BaseDefinitions.Count; ++i) { + + var typeDef = BaseDefinitions[i].Methods.FirstOrDefault( + x => x.Name == name && + (argsCount.HasValue == false || x.Parameters.Count == argsCount.Value) && + (genericArgsCount == null || x.GenericParameters.Count == genericArgsCount.Value)); + + if (typeDef != null) { + methRef = Assembly.CecilAssembly.MainModule.ImportReference(typeDef); + _methods.Add((name, argsCount), methRef); + return methRef; + } + } + } + if (methRef == null) { + throw new InvalidOperationException($"Not found: {name}"); + } + return methRef; + } + + + public GenericInstanceMethod GetGenericMethod(string name, int? argsCount = null, params TypeReference[] types) { + var method = GetMethod(name, argsCount); + var generic = new GenericInstanceMethod(method); + + foreach (var t in types) { + generic.GenericArguments.Add(t); + } + + return generic; + } + } + + public class ILWeaverAssembly { + public bool Modified; + public List Errors = new List(); + + public AssemblyDefinition CecilAssembly; + + ILWeaverImportedType _networkRunner; + ILWeaverImportedType _readWriteUtils; + ILWeaverImportedType _nativeUtils; + ILWeaverImportedType _rpcInfo; + ILWeaverImportedType _rpcInvokeInfo; + ILWeaverImportedType _rpcHeader; + ILWeaverImportedType _networkBehaviourUtils; + + ILWeaverImportedType _simulation; + ILWeaverImportedType _networkedObject; + ILWeaverImportedType _networkedObjectId; + ILWeaverImportedType _networkedBehaviour; + ILWeaverImportedType _networkedBehaviourId; + ILWeaverImportedType _simulationBehaviour; + ILWeaverImportedType _simulationMessage; + + ILWeaverImportedType _object; + ILWeaverImportedType _valueType; + ILWeaverImportedType _void; + ILWeaverImportedType _int; + ILWeaverImportedType _float; + + Dictionary _types = new Dictionary(); + + private ILWeaverImportedType MakeImportedType(ref ILWeaverImportedType field) { + return MakeImportedType(ref field, typeof(T)); + } + + private ILWeaverImportedType MakeImportedType(ref ILWeaverImportedType field, Type type) { + if (field == null) { + field = new ILWeaverImportedType(this, type); + } + return field; + } + + public ILWeaverImportedType WordSizedPrimitive => MakeImportedType(ref _int); + + public ILWeaverImportedType Void => MakeImportedType(ref _void, typeof(void)); + + public ILWeaverImportedType Object => MakeImportedType(ref _object); + + public ILWeaverImportedType ValueType => MakeImportedType(ref _valueType); + + public ILWeaverImportedType Float => MakeImportedType(ref _float); + + public ILWeaverImportedType NetworkedObject => MakeImportedType(ref _networkedObject); + + public ILWeaverImportedType Simulation => MakeImportedType(ref _simulation); + + public ILWeaverImportedType SimulationMessage => MakeImportedType(ref _simulationMessage); + + public ILWeaverImportedType NetworkedBehaviour => MakeImportedType(ref _networkedBehaviour); + + public ILWeaverImportedType SimulationBehaviour => MakeImportedType(ref _simulationBehaviour); + + public ILWeaverImportedType NetworkId => MakeImportedType(ref _networkedObjectId); + + public ILWeaverImportedType NetworkedBehaviourId => MakeImportedType(ref _networkedBehaviourId); + + public ILWeaverImportedType NetworkRunner => MakeImportedType(ref _networkRunner); + + public ILWeaverImportedType ReadWriteUtils => MakeImportedType(ref _readWriteUtils, typeof(ReadWriteUtilsForWeaver)); + + public ILWeaverImportedType Native => MakeImportedType(ref _nativeUtils, typeof(Native)); + + public ILWeaverImportedType NetworkBehaviourUtils => MakeImportedType(ref _networkBehaviourUtils, typeof(NetworkBehaviourUtils)); + + public ILWeaverImportedType RpcHeader => MakeImportedType(ref _rpcHeader); + + public ILWeaverImportedType RpcInfo => MakeImportedType(ref _rpcInfo); + + public ILWeaverImportedType RpcInvokeInfo => MakeImportedType(ref _rpcInvokeInfo); + + public TypeReference Import(TypeReference type) { + return CecilAssembly.MainModule.ImportReference(type); + } + + public MethodReference Import(MethodInfo method) { + return CecilAssembly.MainModule.ImportReference(method); + } + + public MethodReference Import(MethodReference method) { + return CecilAssembly.MainModule.ImportReference(method); + } + + public MethodReference Import(ConstructorInfo method) { + return CecilAssembly.MainModule.ImportReference(method); + } + + public TypeReference Import(Type type) { + if (_types.TryGetValue(type, out var reference) == false) { + _types.Add(type, reference = CecilAssembly.MainModule.ImportReference(type)); + } + + return reference; + } + + public void Dispose() { + CecilAssembly?.Dispose(); + + Modified = false; + Errors.Clear(); + CecilAssembly = null; + } + + public TypeReference Import() { + return Import(typeof(T)); + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverAssemblyResolver.ILPostProcessor.cs + +#if FUSION_WEAVER && FUSION_WEAVER_ILPOSTPROCESSOR && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Mono.Cecil; + + internal class ILWeaverAssemblyResolver : IAssemblyResolver { + private List _lookInDirectories; + private Dictionary _assemblyNameToPath; + private Dictionary _resolvedAssemblies = new Dictionary(); + private string _compiledAssemblyName; + private ILWeaverLog _log; + + public AssemblyDefinition WeavedAssembly; + + public ILWeaverAssemblyResolver(ILWeaverLog log, string compiledAssemblyName, string[] references) { + _log = log; + _compiledAssemblyName = compiledAssemblyName; + _assemblyNameToPath = new Dictionary(); + + foreach (var referencePath in references) { + var assemblyName = Path.GetFileNameWithoutExtension(referencePath); + if (_assemblyNameToPath.TryGetValue(assemblyName, out var existingPath)) { + _log.Warn($"Assembly {assemblyName} (full path: {referencePath}) already referenced by {compiledAssemblyName} at {existingPath}"); + } else { + _assemblyNameToPath.Add(assemblyName, referencePath); + } + } + + _lookInDirectories = references.Select(x => Path.GetDirectoryName(x)).Distinct().ToList(); + } + + public void Dispose() { + } + + public AssemblyDefinition Resolve(AssemblyNameReference name) { + return Resolve(name, new ReaderParameters(ReadingMode.Deferred)); + } + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { + { + if (name.Name == _compiledAssemblyName) + return WeavedAssembly; + + var path = GetAssemblyPath(name); + if (string.IsNullOrEmpty(path)) + return null; + + if (_resolvedAssemblies.TryGetValue(path, out var result)) + return result; + + parameters.AssemblyResolver = this; + + var pdb = path + ".pdb"; + if (File.Exists(pdb)) { + parameters.SymbolStream = CreateAssemblyStream(pdb); + } + + var assemblyDefinition = AssemblyDefinition.ReadAssembly(CreateAssemblyStream(path), parameters); + _resolvedAssemblies.Add(path, assemblyDefinition); + return assemblyDefinition; + } + } + + private string GetAssemblyPath(AssemblyNameReference name) { + if (_assemblyNameToPath.TryGetValue(name.Name, out var path)) { + return path; + } + + // fallback for second-order references + foreach (var parentDir in _lookInDirectories) { + var fullPath = Path.Combine(parentDir, name.Name + ".dll"); + if (File.Exists(fullPath)) { + _assemblyNameToPath.Add(name.Name, fullPath); + return fullPath; + } + } + + return null; + } + + private static MemoryStream CreateAssemblyStream(string fileName) { + var bytes = File.ReadAllBytes(fileName); + return new MemoryStream(bytes); + } + } +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverAssemblyResolver.UnityEditor.cs + +#if FUSION_WEAVER && !FUSION_WEAVER_ILPOSTPROCESSOR && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using Mono.Cecil; + using CompilerAssembly = UnityEditor.Compilation.Assembly; + + class ILWeaverAssemblyResolver : BaseAssemblyResolver { + Dictionary _assemblies; + Dictionary _assembliesByPath; + + public IEnumerable Assemblies => _assemblies.Values; + + public ILWeaverAssemblyResolver() { + _assemblies = new Dictionary(StringComparer.Ordinal); + _assembliesByPath = new Dictionary(); + } + + public sealed override AssemblyDefinition Resolve(AssemblyNameReference name) { + if (_assemblies.TryGetValue(name.FullName, out var asm) == false) { + asm = new ILWeaverAssembly(); + asm.CecilAssembly = base.Resolve(name, ReaderParameters(false, false)); + + _assemblies.Add(name.FullName, asm); + } + + return asm.CecilAssembly; + } + + public void Clear() { + _assemblies.Clear(); + } + + public bool Contains(CompilerAssembly compilerAssembly) { + return _assembliesByPath.ContainsKey(compilerAssembly.outputPath); + } + + public ILWeaverAssembly AddAssembly(string path, bool readWrite = true, bool readSymbols = true) { + return AddAssembly(AssemblyDefinition.ReadAssembly(path, ReaderParameters(readWrite, readSymbols)), null); + } + + public ILWeaverAssembly AddAssembly(CompilerAssembly compilerAssembly, bool readWrite = true, bool readSymbols = true) { + return AddAssembly(AssemblyDefinition.ReadAssembly(compilerAssembly.outputPath, ReaderParameters(readWrite, readSymbols)), compilerAssembly); + } + + public ILWeaverAssembly AddAssembly(AssemblyDefinition assembly, CompilerAssembly compilerAssembly) { + if (assembly == null) { + throw new ArgumentNullException(nameof(assembly)); + } + + if (_assemblies.TryGetValue(assembly.Name.FullName, out var asm) == false) { + asm = new ILWeaverAssembly(); + asm.CecilAssembly = assembly; + + _assemblies.Add(assembly.Name.FullName, asm); + + if (compilerAssembly != null) { + Assert.Always(_assembliesByPath.ContainsKey(compilerAssembly.outputPath) == false); + _assembliesByPath.Add(compilerAssembly.outputPath, asm); + } + } + + return asm; + } + + protected override void Dispose(bool disposing) { + foreach (var asm in _assemblies.Values) { + asm.CecilAssembly?.Dispose(); + } + + _assemblies.Clear(); + + base.Dispose(disposing); + } + + ReaderParameters ReaderParameters(bool readWrite, bool readSymbols) { + ReaderParameters p; + p = new ReaderParameters(ReadingMode.Immediate); + p.ReadWrite = readWrite; + p.ReadSymbols = readSymbols; + p.AssemblyResolver = this; + return p; + } + } +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverBindings.ILPostProcessor.cs + +#if FUSION_WEAVER_ILPOSTPROCESSOR +namespace Fusion.CodeGen { + using System; + using Unity.CompilationPipeline.Common.ILPostProcessing; + +#if FUSION_WEAVER + using System.Collections.Generic; + using Unity.CompilationPipeline.Common.Diagnostics; +#if FUSION_HAS_MONO_CECIL + using System.IO; + using System.Linq; + using Mono.Cecil; + using Mono.Cecil.Cil; + using System.Reflection; + + class ILWeaverBindings : ILPostProcessor { + + public override ILPostProcessor GetInstance() { + return this; + } + + static ILWeaverAssembly CreateWeaverAssembly(ILWeaverLog log, ICompiledAssembly compiledAssembly) { + var resolver = new ILWeaverAssemblyResolver(log, compiledAssembly.Name, compiledAssembly.References); + + var readerParameters = new ReaderParameters { + SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData.ToArray()), + SymbolReaderProvider = new PortablePdbReaderProvider(), + AssemblyResolver = resolver, + ReadingMode = ReadingMode.Immediate, + ReadWrite = true, + ReadSymbols = true, + ReflectionImporterProvider = new ReflectionImporterProvider(log) + }; + + var peStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PeData.ToArray()); + var assemblyDefinition = AssemblyDefinition.ReadAssembly(peStream, readerParameters); + + resolver.WeavedAssembly = assemblyDefinition; + + return new ILWeaverAssembly() { + CecilAssembly = assemblyDefinition, + }; + } + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) { + + ILPostProcessResult result; + var log = new ILWeaverLog(); + + + if (!ILWeaverSettings.ValidateConfig(out var status, out var readException)) { + string message; + DiagnosticType messageType; + + switch (status) { + case ILWeaverSettings.ConfigStatus.NotFound: { + + var candidates = Directory.GetFiles("Assets", "*.fusion", SearchOption.AllDirectories); + + message = $"Fusion ILWeaver config error: {nameof(NetworkProjectConfig)} not found at {ILWeaverSettings.NetworkProjectConfigPath}. " + + $"Implement {nameof(ILWeaverSettings)}.OverrideNetworkProjectConfigPath in Fusion.CodeGen.User.cs to change the config's location."; + + if (candidates.Any()) { + message += $" Possible candidates are: {(string.Join(", ", candidates))}."; + } + + messageType = DiagnosticType.Warning; + } + break; + + case ILWeaverSettings.ConfigStatus.ReadException: + message = $"Fusion ILWeaver config error: reading file {ILWeaverSettings.NetworkProjectConfigPath} failed: {readException.Message}"; + messageType = DiagnosticType.Error; + break; + + default: + throw new NotSupportedException(status.ToString()); + } + + return new ILPostProcessResult(null, new List() { + new DiagnosticMessage() { + DiagnosticType = messageType, + MessageData = message, + } + } + ); + } + + using (log.Scope($"Process {compiledAssembly.Name}")) { + InMemoryAssembly resultAssembly = null; + + try { + ILWeaverAssembly asm; + ILWeaver weaver; + + using (log.Scope("Resolving")) { + asm = CreateWeaverAssembly(log, compiledAssembly); + } + + using (log.Scope("Init")) { + weaver = new ILWeaver(log); + } + + weaver.Weave(asm); + + if (asm.Modified) { + var pe = new MemoryStream(); + var pdb = new MemoryStream(); + var writerParameters = new WriterParameters { + SymbolWriterProvider = new PortablePdbWriterProvider(), + SymbolStream = pdb, + WriteSymbols = true + }; + + using (log.Scope("Writing")) { + asm.CecilAssembly.Write(pe, writerParameters); + resultAssembly = new InMemoryAssembly(pe.ToArray(), pdb.ToArray()); + } + } + } catch (Exception ex) { + log.Error($"Exception thrown when weaving {compiledAssembly.Name}"); + log.Exception(ex); + } finally { + log.FixNewLinesInMessages(); + result = new ILPostProcessResult(resultAssembly, log.Messages); + } + } + + return result; + } + + + public override bool WillProcess(ICompiledAssembly compiledAssembly) { + + if (!ILWeaverSettings.ValidateConfig(out _, out _)) { + // need to go to the next stage for some assembly, main is good enough + return compiledAssembly.Name == "Assembly-CSharp"; + } + + if (!ILWeaverSettings.IsAssemblyWeavable(compiledAssembly.Name)) { + return false; + } + + if (!ILWeaverSettings.ContainsRequiredReferences(compiledAssembly.References)) { + return false; + } + + return true; + } + + class ReflectionImporterProvider : IReflectionImporterProvider { + private ILWeaverLog _log; + + public ReflectionImporterProvider(ILWeaverLog log) { + _log = log; + } + + public IReflectionImporter GetReflectionImporter(ModuleDefinition module) { + return new ReflectionImporter(_log, module); + } + } + + class ReflectionImporter : DefaultReflectionImporter { + private ILWeaverLog _log; + + public ReflectionImporter(ILWeaverLog log, ModuleDefinition module) : base(module) { + _log = log; + } + + public override AssemblyNameReference ImportReference(AssemblyName name) { + if (name.Name == "System.Private.CoreLib") { + // seems weaver is run with .net core, but we need to stick to .net framework + var candidates = module.AssemblyReferences + .Where(x => x.Name == "mscorlib" || x.Name == "netstandard") + .OrderBy(x => x.Name) + .ThenByDescending(x => x.Version) + .ToList(); + + // in Unity 2020.1 and .NET 4.x mode when building with IL2CPP apparently both mscrolib and netstandard can be loaded + if (candidates.Count > 0) { + return candidates[0]; + } + + throw new ILWeaverException("Could not locate mscrolib or netstandard assemblies"); + } + + return base.ImportReference(name); + } + } + } +#else + class ILWeaverBindings : ILPostProcessor { + public override ILPostProcessor GetInstance() { + return this; + } + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) { + return new ILPostProcessResult(null, new List() { + new DiagnosticMessage() { + DiagnosticType = DiagnosticType.Warning, + MessageData = "Mono.Cecil not found, Fusion IL weaving is disabled. Make sure package com.unity.nuget.mono-cecil is installed." + } + }); + } + + public override bool WillProcess(ICompiledAssembly compiledAssembly) { + return compiledAssembly.Name == "Assembly-CSharp"; + } + } +#endif +#else + class ILWeaverBindings : ILPostProcessor { + public override ILPostProcessor GetInstance() { + return this; + } + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) { + throw new NotImplementedException(); + } + + public override bool WillProcess(ICompiledAssembly compiledAssembly) { + return false; + } + } +#endif +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverBindings.UnityEditor.cs + +#if FUSION_WEAVER && !FUSION_WEAVER_ILPOSTPROCESSOR + +namespace Fusion.CodeGen { +#if FUSION_HAS_MONO_CECIL + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using Mono.Cecil; + using UnityEditor; + using UnityEditor.Build; + using UnityEditor.Build.Reporting; + using UnityEditor.Compilation; + using CompilerAssembly = UnityEditor.Compilation.Assembly; + + class ILWeaverBindings { + + static ILWeaver _weaver; + + + [UnityEditor.InitializeOnLoadMethod] + public static void InitializeOnLoad() { + CompilationPipeline.assemblyCompilationFinished += OnCompilationFinished; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + } + + static void OnPlayModeStateChanged(PlayModeStateChange state) { + var projectConfig = NetworkProjectConfig.Global; + + // exit edit mode means play mode is about to start ... + if (state == PlayModeStateChange.ExitingEditMode) { + foreach (var assembly in CompilationPipeline.GetAssemblies()) { + var name = Path.GetFileNameWithoutExtension(assembly.outputPath); + if (ILWeaverSettings.IsAssemblyWeavable(name)) { + OnCompilationFinished(assembly.outputPath, new CompilerMessage[0]); + } + } + } + } + + static void OnCompilationFinished(string path, CompilerMessage[] messages) { +#if FUSION_DEV + Stopwatch sw = Stopwatch.StartNew(); + Log.Debug($"OnCompilationFinished({path})"); +#endif + + // never modify editor assemblies + if (ILWeaver.IsEditorAssemblyPath(path)) { + return; + } + + var projectConfig = NetworkProjectConfig.Global; + if (projectConfig != null) { + // name of assembly on disk + var name = Path.GetFileNameWithoutExtension(path); + if (!ILWeaverSettings.IsAssemblyWeavable(name)) { + return; + } + } + + // errors means we should exit + if (messages.Any(x => x.type == CompilerMessageType.Error)) { +#if FUSION_DEV + Log.Error($"Can't execute ILWeaver on {path}, compilation errors exist."); +#endif + return; + } + + // grab compiler pipe assembly + var asm = CompilationPipeline.GetAssemblies().First(x => x.outputPath == path); + + // needs to reference phoenix runtime + if (ILWeaverSettings.ContainsRequiredReferences(asm.allReferences) == false) { + return; + } + + // perform weaving + try { + _weaver = _weaver ?? new ILWeaver(new ILWeaverLog()); + Weave(_weaver, asm); + } catch (Exception ex) { + UnityEngine.Debug.LogError(ex); + } + +#if FUSION_DEV + UnityEngine.Debug.Log($"OnCompilationFinished took: {sw.Elapsed}"); +#endif + } + + + static void Weave(ILWeaver weaver, Assembly compilerAssembly) { + using (weaver.Log.Scope("Processing")) { + + using (var resolver = new ILWeaverAssemblyResolver()) { + // if we're already weaving this don't do anything + if (resolver.Contains(compilerAssembly)) { + return; + } + + // make sure we can load all dlls + foreach (string path in compilerAssembly.allReferences) { + resolver.AddSearchDirectory(Path.GetDirectoryName(path)); + } + + // make sure we have the runtime dll loaded + if (!ILWeaverSettings.ContainsRequiredReferences(compilerAssembly.allReferences)) { + throw new InvalidOperationException($"Weaving: Could not find required assembly references"); + } + + ILWeaverAssembly asm; + + using (weaver.Log.Scope("Resolving")) { + asm = resolver.AddAssembly(compilerAssembly); + } + + if (weaver.Weave(asm)) { + + using (weaver.Log.Scope("Writing")) { + // write asm to disk + asm.CecilAssembly.Write(new WriterParameters { + WriteSymbols = true + }); + } + } + } + } + } + } +#else + class ILWeaverBindings { + [UnityEditor.InitializeOnLoadMethod] + public static void InitializeOnLoad() { + UnityEngine.Debug.LogError("Mono.Cecil not found, Fusion IL weaving is disabled. Make sure package com.unity.nuget.mono-cecil is installed."); + } + } +#endif +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverException.cs + +namespace Fusion.CodeGen { + using System; + using System.Diagnostics; + + public class ILWeaverException : Exception { + public ILWeaverException(string error) : base(error) { + } + + public ILWeaverException(string error, Exception innerException) : base(error, innerException) { + } + + + [Conditional("UNITY_EDITOR")] + public static void DebugThrowIf(bool condition, string message) { + if (condition) { + throw new ILWeaverException(message); + } + } + } +} + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverExtensions.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + + using System; + using System.Collections.Generic; + using System.Linq; + using System.Runtime.CompilerServices; + using System.Text; + using System.Text.RegularExpressions; + using System.Threading.Tasks; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + + public static class ILWeaverExtensions { + + public static bool IsIntegral(this TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + case MetadataType.SByte: + case MetadataType.UInt16: + case MetadataType.Int16: + case MetadataType.UInt32: + case MetadataType.Int32: + case MetadataType.UInt64: + case MetadataType.Int64: + return true; + default: + return false; + } + } + + public static int GetPrimitiveSize(this TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + case MetadataType.SByte: + return sizeof(byte); + case MetadataType.UInt16: + case MetadataType.Int16: + return sizeof(short); + case MetadataType.UInt32: + case MetadataType.Int32: + return sizeof(int); + case MetadataType.UInt64: + case MetadataType.Int64: + return sizeof(long); + case MetadataType.Single: + return sizeof(float); + case MetadataType.Double: + return sizeof(double); + default: + throw new ArgumentException($"Unknown primitive type: {type}", nameof(type)); + } + } + + public static bool IsString(this TypeReference type) { + return type.MetadataType == MetadataType.String; + } + + public static bool IsFloat(this TypeReference type) { + return type.MetadataType == MetadataType.Single; + } + + public static bool IsBool(this TypeReference type) { + return type.MetadataType == MetadataType.Boolean; + } + + public static bool IsVector2(this TypeReference type) { + return type.FullName == typeof(UnityEngine.Vector2).FullName; + } + + public static bool IsVector3(this TypeReference type) { + return type.FullName == typeof(UnityEngine.Vector3).FullName; + } + + public static bool IsQuaternion(this TypeReference type) { + return type.FullName == typeof(UnityEngine.Quaternion).FullName; + } + + public static bool IsVoid(this TypeReference type) { + return type.MetadataType == MetadataType.Void; + } + + public static TypeReference GetElementTypeEx(this TypeReference type) { + if (type.IsPointer) { + return ((Mono.Cecil.PointerType)type).ElementType; + } else if (type.IsByReference) { + return ((Mono.Cecil.ByReferenceType)type).ElementType; + } else { + return type.GetElementType(); + } + } + + public static bool IsSubclassOf(this TypeReference type) { + return !IsSame(type) && Is(type); + } + + public static bool IsGeneric(this TypeReference type, Type typeDefinition) { + if (typeDefinition.IsGenericTypeDefinition == false) { + throw new InvalidOperationException(); + } + if (!type.IsGenericInstance) { + return false; + } + + return type.GetElementType().FullName == typeDefinition.FullName; + } + + public static bool IsNetworkList(this TypeReference type) { + return IsNetworkList(type, out _); + } + + public static bool IsNetworkList(this TypeReference type, out TypeReference elementType) { + if (!type.IsGenericInstance || type.GetElementType().FullName != typeof(NetworkLinkedList<>).FullName) { + elementType = default; + return false; + } + + var git = (GenericInstanceType)type; + elementType = git.GenericArguments[0]; + return true; + } + + + public static bool IsNetworkArray(this TypeReference type) { + return IsNetworkArray(type, out _); + } + + public static bool IsNetworkArray(this TypeReference type, out TypeReference elementType) { + if (!type.IsGenericInstance || type.GetElementType().FullName != typeof(NetworkArray<>).FullName) { + elementType = default; + return false; + } + + var git = (GenericInstanceType)type; + elementType = git.GenericArguments[0]; + return true; + } + + + public static bool IsNetworkDictionary(this TypeReference type) { + return IsNetworkDictionary(type, out _, out _); + } + + public static bool IsNetworkDictionary(this TypeReference type, out TypeReference keyType, out TypeReference valueType) { + if (!type.IsGenericInstance || type.GetElementType().FullName != typeof(NetworkDictionary<,>).FullName) { + keyType = default; + valueType = default; + return false; + } + + var git = (GenericInstanceType)type; + keyType = git.GenericArguments[0]; + valueType = git.GenericArguments[1]; + return true; + } + + public static bool Is(this TypeReference type) { + return Is(type, typeof(T)); + } + + public static bool Is(this TypeReference type, Type t) { + if (IsSame(type, t)) { + return true; + } + + var resolvedType = TryResolve(type); + + if (t.IsInterface) { + if (resolvedType == null) { + return false; + } + + foreach (var interf in resolvedType.Interfaces) { + if (interf.InterfaceType.IsSame(t)) { + return true; + } + } + return false; + } else { + if (resolvedType?.BaseType == null) { + return false; + } + return Is(resolvedType.BaseType, t); + } + } + + public static bool Is(this TypeReference type, TypeReference t) { + if (IsSame(type, t)) { + return true; + } + + var resolvedType = TryResolve(type); + + if (t.TryResolve()?.IsInterface == true) { + if (resolvedType == null) { + return false; + } + + foreach (var interf in resolvedType.Interfaces) { + if (interf.InterfaceType.IsSame(t)) { + return true; + } + } + return false; + } else { + if (resolvedType?.BaseType == null) { + return false; + } + return Is(resolvedType.BaseType, t); + } + } + + public static bool IsSame(this TypeReference type) { + return IsSame(type, typeof(T)); + } + + public static bool IsSame(this TypeReference type, Type t) { + if (type == null) { + throw new ArgumentNullException(nameof(type)); + } + if (type.IsByReference) { + type = type.GetElementType(); + } + if (type.IsVoid() && t == typeof(void)) { + return true; + } + if (type.IsValueType != t.IsValueType) { + return false; + } + if (type.FullName != t.FullName) { + return false; + } + return true; + } + + public static bool IsSame(this TypeReference type, TypeOrTypeRef t) { + if (t.Type != null) { + return IsSame(type, t.Type); + } else if (t.TypeReference != null) { + return IsSame(type, t.TypeReference); + } else { + throw new InvalidOperationException(); + } + } + + public static bool IsSame(this TypeReference type, TypeReference t) { + if (type == null) { + throw new ArgumentNullException(nameof(type)); + } + if (type.IsByReference) { + type = type.GetElementType(); + } + if (type.IsValueType != t.IsValueType) { + return false; + } + if (type.FullName != t.FullName) { + return false; + } + return true; + } + + + public static IEnumerable GetHierarchy(this TypeDefinition type, TypeReference stopAtBaseType = null) { + if (stopAtBaseType?.IsSame(type) == true) { + yield break; + } + + for (; ; ) { + yield return type; + if (type.BaseType == null || stopAtBaseType?.IsSame(type.BaseType) == true) { + break; + } + type = type.BaseType.Resolve(); + } + } + + public static TypeDefinition TryResolve(this TypeReference type) { + try { + return type.Resolve(); + } catch { + return null; + } + } + + public static bool IsFixedBuffer(this TypeReference type, out int size) { + size = default; + if (!type.IsValueType) { + return false; + } + + if (!type.Name.EndsWith("e__FixedBuffer")) { + return false; + } + + var definition = TryResolve(type); + if (definition == null) { + return false; + } + + // this is a bit of a guesswork + if (HasAttribute(definition) && + HasAttribute(definition) && + definition.ClassSize > 0) { + size = definition.ClassSize; + return true; + } + + return false; + } + + public static bool HasAttribute(this ICustomAttributeProvider type) where T : Attribute { + for (int i = 0; i < type.CustomAttributes.Count; ++i) { + if (type.CustomAttributes[i].AttributeType.Name == typeof(T).Name) { + return true; + } + } + + return false; + } + + public static bool TryGetAttribute(this ICustomAttributeProvider type, out CustomAttribute attribute) where T : Attribute { + for (int i = 0; i < type.CustomAttributes.Count; ++i) { + if (type.CustomAttributes[i].AttributeType.Name == typeof(T).Name) { + attribute = type.CustomAttributes[i]; + return true; + } + } + + attribute = null; + return false; + } + + public static T GetAttributeArgument(this CustomAttribute attr, int index) { + return (T)attr.ConstructorArguments[index].Value; + } + + public static bool TryGetAttributeArgument(this CustomAttribute attr, int index, out T value, T defaultValue = default) { + if (index < attr.ConstructorArguments.Count) { + if (attr.ConstructorArguments[index].Value is T t) { + value = t; + return true; + } + } + + value = defaultValue; + return false; + } + + public static T GetAttributeProperty(this CustomAttribute attr, string name, T defaultValue = default) { + attr.TryGetAttributeProperty(name, out var result, defaultValue); + return result; + } + + public static bool TryGetAttributeProperty(this CustomAttribute attr, string name, out T value, T defaultValue = default) { + if (attr.HasProperties) { + var prop = attr.Properties.FirstOrDefault(x => x.Name == name); + + if (prop.Argument.Value != null) { + value = (T)prop.Argument.Value; + return true; + } + } + + value = defaultValue; + return false; + } + + static bool TryGetMatchingMethod(IEnumerable methods, IList parameters, out MethodDefinition result) { + foreach (var c in methods) { + if (c.Parameters.Count != parameters.Count) { + continue; + } + int i; + for (i = 0; i < c.Parameters.Count; ++i) { + if (!c.Parameters[i].ParameterType.IsSame(parameters[i].ParameterType)) { + break; + } + } + + if (i == c.Parameters.Count) { + result = c; + return true; + } + } + + result = null; + return false; + } + + public static bool TryGetMatchingConstructor(this TypeDefinition type, MethodDefinition constructor, out MethodDefinition matchingConstructor) { + return TryGetMatchingMethod(type.GetConstructors(), constructor.Parameters, out matchingConstructor); + } + + public static bool TryGetMethod(this TypeDefinition type, string methodName, IList parameters, out MethodDefinition method) { + var methods = type.Methods.Where(x => x.Name == methodName); + + if (TryGetMatchingMethod(methods, parameters, out method)) { + return true; + } + + //if (type.BaseType != null) { + // if (stopAtBaseType == null || !stopAtBaseType.IsSame(type.BaseType)) { + // return TryGetMethod(type.BaseType.Resolve(), methodName, parameters, out method, stopAtBaseType); + // } + //} + + method = null; + return false; + } + + public static bool Remove(this FieldDefinition field) { + return field.DeclaringType.Fields.Remove(field); + } + + public static FieldDefinition GetFieldOrThrow(this TypeDefinition type, string fieldName) { + foreach (var field in type.Fields) { + if ( field.Name == fieldName ) { + return field; + } + } + throw new ArgumentOutOfRangeException(nameof(fieldName), $"Field {fieldName} not found in {type}"); + } + + public static MethodReference GetGenericInstanceMethodOrThrow(this GenericInstanceType type, string name) { + var methodRef = type.Resolve().GetMethodOrThrow(name); + + var newMethodRef = new MethodReference(methodRef.Name, methodRef.ReturnType) { + HasThis = methodRef.HasThis, + ExplicitThis = methodRef.ExplicitThis, + DeclaringType = type, + CallingConvention = methodRef.CallingConvention, + }; + + foreach (var parameter in methodRef.Parameters) { + newMethodRef.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, parameter.ParameterType)); + } + + foreach (var genericParameter in methodRef.GenericParameters) { + newMethodRef.GenericParameters.Add(new GenericParameter(genericParameter.Name, newMethodRef)); + } + + return newMethodRef; + } + + public static void AddInterface(this TypeDefinition type, ILWeaverAssembly asm) { + type.Interfaces.Add(new InterfaceImplementation(asm.Import(typeof(T)))); + } + + public static bool RemoveAttribute(this IMemberDefinition member, ILWeaverAssembly asm) where T : Attribute { + for (int i = 0; i < member.CustomAttributes.Count; ++i) { + var attr = member.CustomAttributes[i]; + if ( attr.AttributeType.Is() ) { + member.CustomAttributes.RemoveAt(i); + return true; + } + } + return false; + } + + public static CustomAttribute AddAttribute(this IMemberDefinition member, ILWeaverAssembly asm) where T : Attribute { + CustomAttribute attr; + attr = new CustomAttribute(typeof(T).GetConstructor(asm)); + member.CustomAttributes.Add(attr); + return attr; + } + + public static CustomAttribute AddAttribute(this IMemberDefinition member, ILWeaverAssembly asm, A0 arg0) where T : Attribute { + CustomAttribute attr; + attr = new CustomAttribute(typeof(T).GetConstructor(asm, 1)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg0)); + member.CustomAttributes.Add(attr); + return attr; + } + + public static CustomAttribute AddAttribute(this IMemberDefinition member, ILWeaverAssembly asm, A0 arg0, A1 arg1) where T : Attribute { + CustomAttribute attr; + attr = new CustomAttribute(typeof(T).GetConstructor(asm, 2)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg0)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg1)); + member.CustomAttributes.Add(attr); + return attr; + } + + public static CustomAttribute AddAttribute(this IMemberDefinition member, ILWeaverAssembly asm, A0 arg0, A1 arg1, A2 arg2) where T : Attribute { + CustomAttribute attr; + attr = new CustomAttribute(typeof(T).GetConstructor(asm, 3)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg0)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg1)); + attr.ConstructorArguments.Add(new CustomAttributeArgument(asm.ImportAttributeType(), arg2)); + member.CustomAttributes.Add(attr); + return attr; + } + + private static TypeReference ImportAttributeType(this ILWeaverAssembly asm) { + if (typeof(T) == typeof(TypeReference)) { + return asm.Import(); + } else { + return asm.Import(); + } + } + + public static MethodReference GetConstructor(this Type type, ILWeaverAssembly asm, int argCount = 0) { + foreach (var ctor in type.GetConstructors()) { + if (ctor.GetParameters().Length == argCount) { + return asm.CecilAssembly.MainModule.ImportReference(ctor); + } + } + + throw new ILWeaverException($"Could not find constructor with {argCount} arguments on {type.Name}"); + } + + public static void AddTo(this MethodDefinition method, TypeDefinition type) { + type.Methods.Add(method); + } + + public static void AddTo(this PropertyDefinition property, TypeDefinition type) { + type.Properties.Add(property); + } + + + public static void AddTo(this FieldDefinition field, TypeDefinition type) { + type.Fields.Add(field); + } + + public static void AddTo(this TypeDefinition type, AssemblyDefinition assembly) { + assembly.MainModule.Types.Add(type); + } + + public static void AddTo(this TypeDefinition type, TypeDefinition parentType) { + parentType.NestedTypes.Add(type); + } + + public static MethodDefinition GetMethodOrThrow(this TypeDefinition type, string methodName) { + foreach (var method in type.Methods) { + if (method.Name == methodName) { + return method; + } + } + throw new ArgumentOutOfRangeException(nameof(methodName), $"Method {methodName} not found in {type}"); + } + + public static Instruction AppendReturn(this ILProcessor il, Instruction instruction) { + il.Append(instruction); + return instruction; + } + + public static void Clear(this ILProcessor il) { + var instructions = il.Body.Instructions; + foreach (var instruction in instructions.Reverse()) { + il.Remove(instruction); + } + } + + public static MethodDefinition AddEmptyConstructor(this TypeDefinition type, ILWeaverAssembly asm) { + var methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; + var method = new MethodDefinition(".ctor", methodAttributes, asm.Import(typeof(void))); + method.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0)); + + MethodReference baseConstructor; + + if (type.BaseType.IsGenericInstance) { + var gi = (GenericInstanceType)type.BaseType; + baseConstructor = gi.GetGenericInstanceMethodOrThrow(".ctor"); + } else { + if (!type.BaseType.Resolve().TryGetMatchingConstructor(method, out var baseConstructorDef)) { + throw new ILWeaverException("Unable to find matching constructor."); + } + baseConstructor = baseConstructorDef; + } + + + + method.Body.Instructions.Add(Instruction.Create(OpCodes.Call, asm.Import(baseConstructor))); + method.Body.Instructions.Add(Instruction.Create(OpCodes.Ret)); + type.Methods.Add(method); + return method; + } + + public static void AppendMacro(this ILProcessor il, in T macro) where T : struct, ILProcessorMacro { + macro.Emit(il); + } + + public class TypeOrTypeRef { + + public Type Type { get; } + public TypeReference TypeReference { get; } + + + public TypeOrTypeRef(Type type, bool isOut = false) { + Type = type; + } + + public TypeOrTypeRef(TypeReference type, bool isOut = false) { + TypeReference = type; + } + + public static implicit operator TypeOrTypeRef(Type type) { + return new TypeOrTypeRef(type); + } + + public static implicit operator TypeOrTypeRef(TypeReference type) { + return new TypeOrTypeRef(type); + } + + public override string ToString() { + if (Type != null) { + return Type.FullName; + } else if (TypeReference != null) { + return TypeReference.ToString(); + } else { + return "AnyType"; + } + } + } + + + public static bool GetSingleOrDefaultMethodWithAttribute(this TypeDefinition type, out CustomAttribute attribute, out MethodDefinition method) where T : Attribute { + + MethodDefinition resultMethod = null; + CustomAttribute resultAttribute = null; + + foreach (var m in type.Methods) { + if (m.TryGetAttribute(out var attr)) { + if (resultMethod != null) { + throw new ILWeaverException($"Only one method with attribute {typeof(T)} allowed per class: {type}"); + } else { + resultMethod = m; + resultAttribute = attr; + } + } + } + + method = resultMethod; + attribute = resultAttribute; + return method != null; + } + + + public static PropertyDefinition ThrowIfStatic(this PropertyDefinition property) { + if (property.GetMethod?.IsStatic == true || + property.SetMethod?.IsStatic == true) { + throw new ILWeaverException($"Property is static: {property.FullName}"); + } + return property; + } + + public static MethodDefinition ThrowIfStatic(this MethodDefinition method) { + if (method.IsStatic) { + throw new ILWeaverException($"Method is static: {method.FullName}"); + } + return method; + } + + public static MethodDefinition ThrowIfNotStatic(this MethodDefinition method) { + if (!method.IsStatic) { + throw new ILWeaverException($"Method is not static: {method}"); + } + return method; + } + + public static MethodDefinition ThrowIfNotPublic(this MethodDefinition method) { + if (!method.IsPublic) { + throw new ILWeaverException($"Method is not public: {method}"); + } + return method; + } + + public static MethodDefinition ThrowIfReturnType(this MethodDefinition method, TypeOrTypeRef type) { + if (!method.ReturnType.IsSame(type)) { + + throw new ILWeaverException($"Method has an invalid return type (expected {type}): {method}"); + } + return method; + } + + public static MethodDefinition ThrowIfParameterCount(this MethodDefinition method, int count) { + if (method.Parameters.Count != count) { + throw new ILWeaverException($"Method has invalid parameter count (expected {count}): {method}"); + } + return method; + } + + public static MethodDefinition ThrowIfParameter(this MethodDefinition method, int index, TypeOrTypeRef type = null, bool isByReference = false) { + var p = method.Parameters[index]; + if (type != null && !p.ParameterType.IsSame(type)) { + throw new ILWeaverException($"Parameter {p} ({index}) has an invalid type (expected {type}): {method}"); + } + if (p.ParameterType.IsByReference != isByReference) { + if (p.IsOut) { + throw new ILWeaverException($"Parameter {p} ({index}) is a ref parameter: {method}"); + } else { + throw new ILWeaverException($"Parameter {p} ({index}) is not a ref parameter: {method}"); + } + } + return method; + } + + public static bool IsBaseConstructorCall(this Instruction instruction, TypeDefinition type) { + if (instruction.OpCode == OpCodes.Call) { + var m = ((MethodReference)instruction.Operand).Resolve(); + if (m.IsConstructor && m.DeclaringType.IsSame(type.BaseType)) { + // base constructor init + return true; + } + } + return false; + } + + public static bool IsLdloca(this Instruction instruction, out VariableDefinition variable, out bool isShort) { + if (instruction.OpCode == OpCodes.Ldloca) { + variable = (VariableDefinition)instruction.Operand; + isShort = false; + return true; + } + if (instruction.OpCode == OpCodes.Ldloca_S) { + variable = (VariableDefinition)instruction.Operand; + isShort = true; + return true; + } + + variable = default; + isShort = default; + return false; + } + + public static bool IsLdlocWithIndex(this Instruction instruction, out int index) { + if (instruction.OpCode == OpCodes.Ldloc_0) { + index = 0; + return true; + } + if (instruction.OpCode == OpCodes.Ldloc_1) { + index = 1; + return true; + } + if (instruction.OpCode == OpCodes.Ldloc_2) { + index = 2; + return true; + } + if (instruction.OpCode == OpCodes.Ldloc_3) { + index = 3; + return true; + } + index = -1; + return false; + } + + public static bool IsStlocWithIndex(this Instruction instruction, out int index) { + if (instruction.OpCode == OpCodes.Stloc_0) { + index = 0; + return true; + } + if (instruction.OpCode == OpCodes.Stloc_1) { + index = 1; + return true; + } + if (instruction.OpCode == OpCodes.Stloc_2) { + index = 2; + return true; + } + if (instruction.OpCode == OpCodes.Stloc_3) { + index = 3; + return true; + } + index = -1; + return false; + } + } + + public interface ILProcessorMacro { + void Emit(ILProcessor il); + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverLog.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + using Mono.Cecil; + using UnityEngine; + + public partial class ILWeaverLog { + + public void AssertMessage(bool condition, string message, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + if (!condition) { + AssertFailed("Assert failed: " + message, filePath, lineNumber); + } + } + + + public void Assert(bool condition, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + if (!condition) { + AssertFailed("Assertion failed", filePath, lineNumber); + } + } + + [Conditional("FUSION_WEAVER_DEBUG")] + public void Debug(string message, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + Log(LogType.Log, message, filePath, lineNumber); + } + + public void Warn(string message, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + Log(LogType.Warning, message, filePath, lineNumber); + } + + public void Error(string message, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + Log(LogType.Error, message, filePath, lineNumber); + } + + public void Exception(Exception ex) { + LogExceptionImpl(ex); + } + + partial void AssertFailed(string message, string filePath, int lineNumber); + + private void Log(LogType logType, string message, string filePath, int lineNumber) { + LogImpl(logType, message, filePath, lineNumber); + } + + partial void LogImpl(LogType logType, string message, string filePath, int lineNumber); + partial void LogExceptionImpl(Exception ex); + +#if !FUSION_WEAVER_DEBUG + public struct LogScope : IDisposable { + public void Dispose() { + } + } + + public LogScope Scope(string name) { + return default; + } + + public LogScope ScopeAssembly(AssemblyDefinition cecilAssembly) { + return default; + } + + public LogScope ScopeBehaviour(TypeDefinition type) { + return default; + } + + public LogScope ScopeInput(TypeDefinition type) { + return default; + } + + public LogScope ScopeStruct(TypeDefinition type) { + return default; + } + +#else + public LogScope Scope(string name, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + return new LogScope(this, name, filePath, lineNumber); + } + + public LogScope ScopeAssembly(AssemblyDefinition cecilAssembly, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + return new LogScope(this, $"Assembly: {cecilAssembly.FullName}", filePath, lineNumber); + } + + public LogScope ScopeBehaviour(TypeDefinition type, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + return new LogScope(this, $"Behaviour: {type.FullName}", filePath, lineNumber); + } + + public LogScope ScopeInput(TypeDefinition type, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + return new LogScope(this, $"Input: {type.FullName}", filePath, lineNumber); + } + + public LogScope ScopeStruct(TypeDefinition type, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) { + return new LogScope(this, $"Struct: {type.FullName}", filePath, lineNumber); + } + + + partial void ScopeBeginImpl(ref LogScope scope, string filePath, int lineNumber); + partial void ScopeEndImpl(ref LogScope scope); + + +#pragma warning disable CS0282 // There is no defined ordering between fields in multiple declarations of partial struct + public partial struct LogScope : IDisposable { +#pragma warning restore CS0282 // There is no defined ordering between fields in multiple declarations of partial struct + public string Message; + + public TimeSpan Elapsed => _stopwatch.Elapsed; + + private ILWeaverLog _log; + private Stopwatch _stopwatch; + + public LogScope(ILWeaverLog log, string message, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = default) : this() { + _log = log; + Message = message; + _stopwatch = Stopwatch.StartNew(); + _log.ScopeBeginImpl(ref this, filePath, lineNumber); + } + + public void Dispose() { + _stopwatch.Stop(); + _log.ScopeEndImpl(ref this); + } + + partial void Init(string filePath, int lineNumber); + } +#endif + } +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverLog.ILPostProcessor.cs + +#if FUSION_WEAVER && FUSION_WEAVER_ILPOSTPROCESSOR && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using Unity.CompilationPipeline.Common.Diagnostics; + using UnityEngine; + + partial class ILWeaverLog { + + public List Messages { get; } = new List(); + + partial void LogImpl(LogType logType, string message, string filePath, int lineNumber) { + + DiagnosticType diagnosticType; + + if (logType == LogType.Log) { + // there are no debug diagnostic messages, make pretend warnings + message = $"DEBUG: {message}"; + diagnosticType = DiagnosticType.Warning; + } else if (logType == LogType.Warning) { + diagnosticType = DiagnosticType.Warning; + } else { + diagnosticType = DiagnosticType.Error; + } + + // newlines in messagedata will need to be escaped, but let's not slow things down now + Messages.Add(new DiagnosticMessage() { + File = filePath, + Line = lineNumber, + DiagnosticType = diagnosticType, + MessageData = message + }); + } + + partial void AssertFailed(string message, string filePath, int lineNumber) { + Error(message, filePath, lineNumber); + throw new AssertException(); + } + + partial void LogExceptionImpl(Exception ex) { + var lines = ex.ToString().Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) { + Error(line, null, 0); + } + } + + public void FixNewLinesInMessages() { + // fix the messages + foreach (var msg in Messages) { + msg.MessageData = msg.MessageData.Replace('\r', ';').Replace('\n', ';'); + } + } + + + +#if FUSION_WEAVER_DEBUG +#pragma warning disable CS0282 // There is no defined ordering between fields in multiple declarations of partial struct + partial struct LogScope { +#pragma warning restore CS0282 // There is no defined ordering between fields in multiple declarations of partial struct + public int LineNumber; + public string FilePath; + } + + partial void ScopeBeginImpl(ref LogScope scope, string filePath, int lineNumber) { + scope.FilePath = filePath; + scope.LineNumber = lineNumber; + Log(LogType.Log, $"{scope.Message} start", scope.FilePath, scope.LineNumber); + } + + partial void ScopeEndImpl(ref LogScope scope) { + Log(LogType.Log, $"{scope.Message} end {scope.Elapsed}", scope.FilePath, scope.LineNumber); + } +#endif + } + +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverLog.UnityEditor.cs + +#if FUSION_WEAVER && !FUSION_WEAVER_ILPOSTPROCESSOR && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + + using UnityEngine; + using System; + + partial class ILWeaverLog { + +#if FUSION_WEAVER_DEBUG + partial void LogExceptionImpl(Exception ex) { + UnityEngine.Debug.unityLogger.LogException(ex); + } + + partial void LogImpl(LogType logType, string message, string filePath, int lineNumber) { + UnityEngine.Debug.unityLogger.Log(logType, message); + } + + partial void ScopeBeginImpl(ref LogScope scope, string filePath, int lineNumber) { + Log(LogType.Log, $"{scope.Message} start", default, default); + } + + partial void ScopeEndImpl(ref LogScope scope) { + Log(LogType.Log, $"{scope.Message} end {scope.Elapsed}", default, default); + } + + partial void AssertFailed(string message, string filePath, int lineNumber) { + Error(message, filePath, lineNumber); + throw new AssertException(); + } +#endif + } +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverMethodContext.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL + +namespace Fusion.CodeGen { + + using System; + using System.Collections.Generic; + using Mono.Cecil; + using Mono.Cecil.Cil; + using Mono.Cecil.Rocks; + using static Fusion.CodeGen.ILWeaverOpCodes; + + unsafe partial class ILWeaver { + + ILMacroStruct AlignToWordSize() => new[] { + Ldc_I4(Allocator.REPLICATE_WORD_SIZE - 1), + Add(), + Ldc_I4(~(Allocator.REPLICATE_WORD_SIZE - 1)), + And() + }; + + ILMacroStruct LoadFixedBufferAddress(FieldDefinition fixedBufferField) => new Action(il => { + + var elementField = fixedBufferField.FieldType.Resolve().Fields[0]; + + int pointerLoc = il.Body.Variables.Count; + il.Body.Variables.Add(new VariableDefinition(elementField.FieldType.MakePointerType())); + int pinnedRefLoc = il.Body.Variables.Count; + il.Body.Variables.Add(new VariableDefinition(elementField.FieldType.MakeByReferenceType().MakePinnedType())); + + il.Append(Ldflda(fixedBufferField)); + il.Append(Ldflda(elementField)); + il.Append(Stloc(il.Body, pinnedRefLoc)); + il.Append(Ldloc(il.Body, pinnedRefLoc)); + il.Append(Conv_U()); + il.Append(Stloc(il.Body, pointerLoc)); + il.Append(Ldloc(il.Body, pointerLoc)); + }); + + struct ILMacroStruct : ILProcessorMacro { + Action generator; + Instruction[] instructions; + public static implicit operator ILMacroStruct(Instruction[] instructions) { + return new ILMacroStruct() { + instructions = instructions + }; + } + + public static implicit operator ILMacroStruct(Action generator) { + return new ILMacroStruct() { + generator = generator + }; + } + + public void Emit(ILProcessor il) { + if (generator != null) { + generator(il); + } else { + foreach (var instruction in instructions) { + il.Append(instruction); + } + } + } + } + + class MethodContext : IDisposable { + public readonly ILWeaverAssembly Assembly; + public readonly MethodDefinition Method; + + private Dictionary _fields = new Dictionary(); + private Action addressGetter; + private bool runnerIsLdarg0 = false; + + public MethodContext(ILWeaverAssembly assembly, MethodDefinition method, bool staticRunnerAccessor = false, Action addressGetter = null) { + if (assembly == null) { + throw new ArgumentNullException(nameof(assembly)); + } + if (method == null) { + throw new ArgumentNullException(nameof(method)); + } + + this.Assembly = assembly; + this.Method = method; + this.runnerIsLdarg0 = staticRunnerAccessor; + this.addressGetter = addressGetter; + } + + public void Dispose() { + } + + public VariableDefinition GetOrCreateVariable(string id, TypeReference type) { + if (_fields.TryGetValue(id, out var val)) { + if (!val.VariableType.IsSame(type)) { + throw new ArgumentException($"Variable of with the same name {id} already exists, but has a different type: {type} vs {val.VariableType}", nameof(id)); + } + return val; + } + var result = new VariableDefinition(type); + _fields.Add(id, result); + Method.Body.Variables.Add(result); + return result; + } + + public TypeReference ImportReference(TypeReference type) { + if (type == null) { + throw new ArgumentNullException(nameof(type)); + } + return Method.Module.ImportReference(type); + } + + public MethodReference ImportReference(MethodReference method) { + if (method == null) { + throw new ArgumentNullException(nameof(method)); + } + return Method.Module.ImportReference(method); + } + + public virtual ILMacroStruct LoadAddress() => addressGetter; + + public ForLoopMacro For(int start, int stop, Action body) => new ForLoopMacro(this, body, start, stop); + + public AddOffsetMacro AddOffset(int val) => AddOffset(Ldc_I4(val)); + + public AddOffsetMacro AddOffset(VariableDefinition val) => AddOffset(Ldloc(val)); + + public AddOffsetMacro AddOffset(Instruction val) => new AddOffsetMacro(this, instruction: val); + + public AddOffsetMacro AddOffset(Action generator) => new AddOffsetMacro(this, generator: generator); + + public AddOffsetMacro AddOffset() => new AddOffsetMacro(this, null, null); + + public ScopeRpcOffset AddOffsetScope(ILProcessor il) => new ScopeRpcOffset(il, this); + + public ILMacroStruct LoadRunner() { + return runnerIsLdarg0 ? + new[] { Ldarg_0() } : + new[] { Ldarg_0(), Ldfld(Assembly.SimulationBehaviour.GetField(nameof(SimulationBehaviour.Runner))) }; + } + + public virtual bool HasOffset => false; + + protected virtual void EmitAddOffsetBefore(ILProcessor il) { + } + + protected virtual void EmitAddOffsetAfter(ILProcessor il) { + } + + public readonly struct ForLoopMacro : ILProcessorMacro { + public readonly MethodContext Context; + public readonly Action Generator; + public readonly int Start; + public readonly int Stop; + + public ForLoopMacro(MethodContext context, Action generator, int start, int stop) { + Context = context; + Generator = generator; + Start = start; + Stop = stop; + } + + public void Emit(ILProcessor il) { + var body = Context.Method.Body; + var varId = body.Variables.Count; + var indexVariable = new VariableDefinition(Context.Assembly.Import(typeof(int))); + body.Variables.Add(indexVariable); + + il.Append(Ldc_I4(Start)); + il.Append(Stloc(body, varId)); + + var loopConditionStart = Ldloc(body, varId); + il.Append(Br_S(loopConditionStart)); + { + var loopBodyBegin = il.AppendReturn(Nop()); + Generator(il, indexVariable); + + il.Append(Ldloc(body, varId)); + il.Append(Ldc_I4(1)); + il.Append(Add()); + il.Append(Stloc(body, varId)); + + il.Append(loopConditionStart); + il.Append(Ldc_I4(Stop)); + il.Append(Blt_S(loopBodyBegin)); + } + } + } + + public readonly struct AddOffsetMacro : ILProcessorMacro { + public readonly MethodContext Context; + public readonly Instruction Instruction; + public readonly Action Generator; + + public AddOffsetMacro(MethodContext context, Instruction instruction = null, Action generator = null) { + Context = context; + Instruction = instruction; + Generator = generator; + } + + public void Emit(ILProcessor il) { + if (Context.HasOffset) { + Context.EmitAddOffsetBefore(il); + if (Instruction != null) { + il.Append(Instruction); + } else if (Generator != null) { + Generator(il); + } + Context.EmitAddOffsetAfter(il); + } + } + } + + public readonly struct ScopeRpcOffset : IDisposable { + public readonly MethodContext context; + public readonly ILProcessor il; + + public ScopeRpcOffset(ILProcessor il, MethodContext context) { + this.context = context; + this.il = il; + if (context.HasOffset) { + context.EmitAddOffsetBefore(il); + } + } + + public void Dispose() { + if (context.HasOffset) { + context.EmitAddOffsetAfter(il); + } + } + } + } + + class RpcMethodContext : MethodContext { + public VariableDefinition DataVariable; + public VariableDefinition OffsetVariable; + public VariableDefinition RpcInvokeInfoVariable; + + public RpcMethodContext(ILWeaverAssembly asm, MethodDefinition definition, bool staticRunnerAccessor) + : base(asm, definition, staticRunnerAccessor) { + } + + public override bool HasOffset => true; + + protected override void EmitAddOffsetBefore(ILProcessor il) { + il.Append(Ldloc(OffsetVariable)); + } + + protected override void EmitAddOffsetAfter(ILProcessor il) { + il.Append(Add()); + il.Append(Stloc(OffsetVariable)); + } + + public override ILMacroStruct LoadAddress() => new[] { + Ldloc(DataVariable), + Ldloc(OffsetVariable), + Add(), + }; + + public ILMacroStruct SetRpcInvokeInfoVariable(string name, Instruction value) => RpcInvokeInfoVariable == null ? new Instruction[0] : + new[] { + Ldloca(RpcInvokeInfoVariable), + value, + Stfld(Assembly.RpcInvokeInfo.GetField(name)), + }; + + public ILMacroStruct SetRpcInvokeInfoVariable(string name, int value) => RpcInvokeInfoVariable == null ? new Instruction[0] : + new[] { + Ldloca(RpcInvokeInfoVariable), + Ldc_I4(value), + Stfld(Assembly.RpcInvokeInfo.GetField(name)), + }; + public ILMacroStruct SetRpcInvokeInfoStatus(bool emitIf, RpcLocalInvokeResult reason) => RpcInvokeInfoVariable == null || !emitIf ? new Instruction[0] : + new[] { + Ldloca(RpcInvokeInfoVariable), + Ldc_I4((int)reason), + Stfld(Assembly.RpcInvokeInfo.GetField(nameof(RpcInvokeInfo.LocalInvokeResult))) + }; + + public ILMacroStruct SetRpcInvokeInfoStatus(RpcSendCullResult reason) => RpcInvokeInfoVariable == null ? new Instruction[0] : + new[] { + Ldloca(RpcInvokeInfoVariable), + Ldc_I4((int)reason), + Stfld(Assembly.RpcInvokeInfo.GetField(nameof(RpcInvokeInfo.SendCullResult))) + }; + + } + } +} + +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverOpCodes.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System; + using System.Diagnostics; + + using Mono.Cecil; + using Mono.Cecil.Cil; + + static class ILWeaverOpCodes { + // utils + public static Instruction Nop() => Instruction.Create(OpCodes.Nop); + public static Instruction Ret() => Instruction.Create(OpCodes.Ret); + public static Instruction Dup() => Instruction.Create(OpCodes.Dup); + public static Instruction Pop() => Instruction.Create(OpCodes.Pop); + public static Instruction Ldnull() => Instruction.Create(OpCodes.Ldnull); + public static Instruction Throw() => Instruction.Create(OpCodes.Throw); + + public static Instruction Cast(TypeReference type) => Instruction.Create(OpCodes.Castclass, type); + + // breaks + public static Instruction Brfalse(Instruction target) => Instruction.Create(OpCodes.Brfalse, target); + public static Instruction Brtrue(Instruction target) => Instruction.Create(OpCodes.Brtrue, target); + public static Instruction Brfalse_S(Instruction target) => Instruction.Create(OpCodes.Brfalse_S, target); + public static Instruction Brtrue_S(Instruction target) => Instruction.Create(OpCodes.Brtrue_S, target); + public static Instruction Br_S(Instruction target) => Instruction.Create(OpCodes.Br_S, target); + public static Instruction Br(Instruction target) => Instruction.Create(OpCodes.Br, target); + public static Instruction Blt_S(Instruction target) => Instruction.Create(OpCodes.Blt_S, target); + public static Instruction Ble_S(Instruction target) => Instruction.Create(OpCodes.Ble_S, target); + public static Instruction Beq(Instruction target) => Instruction.Create(OpCodes.Beq, target); + public static Instruction Bne_Un_S(Instruction target) => Instruction.Create(OpCodes.Bne_Un_S, target); + public static Instruction Beq_S(Instruction target) => Instruction.Create(OpCodes.Beq_S, target); + + // math + public static Instruction Add() => Instruction.Create(OpCodes.Add); + public static Instruction Sub() => Instruction.Create(OpCodes.Sub); + public static Instruction Mul() => Instruction.Create(OpCodes.Mul); + public static Instruction Div() => Instruction.Create(OpCodes.Div); + public static Instruction And() => Instruction.Create(OpCodes.And); + + // obj + public static Instruction Ldobj(TypeReference type) => Instruction.Create(OpCodes.Ldobj, type); + public static Instruction Stobj(TypeReference type) => Instruction.Create(OpCodes.Stobj, type); + + public static Instruction Newobj(MethodReference constructor) => Instruction.Create(OpCodes.Newobj, constructor); + + public static Instruction Initobj(TypeReference type) => Instruction.Create(OpCodes.Initobj, type); + + // fields + public static Instruction Ldflda(FieldReference field) => Instruction.Create(OpCodes.Ldflda, field); + + public static Instruction Ldfld(FieldReference field) => Instruction.Create(OpCodes.Ldfld, field); + public static Instruction Stfld(FieldReference field) => Instruction.Create(OpCodes.Stfld, field); + + public static Instruction Ldsfld(FieldReference field) => Instruction.Create(OpCodes.Ldsfld, field); + public static Instruction Stsfld(FieldReference field) => Instruction.Create(OpCodes.Stsfld, field); + + // locals + + public static Instruction Ldloc_or_const(VariableDefinition var, int val) => var != null ? Ldloc(var) : Ldc_I4(val); + + public static Instruction Ldloc(VariableDefinition var, MethodDefinition method) => Ldloc(method.Body, method.Body.Variables.IndexOf(var)); + + public static Instruction Ldloc(VariableDefinition var) => Instruction.Create(OpCodes.Ldloc, var); + public static Instruction Ldloca(VariableDefinition var) => Instruction.Create(OpCodes.Ldloca, var); + public static Instruction Ldloca_S(VariableDefinition var) => Instruction.Create(OpCodes.Ldloca_S, var); + public static Instruction Stloc(VariableDefinition var) => Instruction.Create(OpCodes.Stloc, var); + + public static Instruction Stloc_0() => Instruction.Create(OpCodes.Stloc_0); + public static Instruction Stloc_1() => Instruction.Create(OpCodes.Stloc_1); + public static Instruction Stloc_2() => Instruction.Create(OpCodes.Stloc_2); + public static Instruction Stloc_3() => Instruction.Create(OpCodes.Stloc_3); + + public static Instruction Ldloc_0() => Instruction.Create(OpCodes.Ldloc_0); + public static Instruction Ldloc_1() => Instruction.Create(OpCodes.Ldloc_1); + public static Instruction Ldloc_2() => Instruction.Create(OpCodes.Ldloc_2); + public static Instruction Ldloc_3() => Instruction.Create(OpCodes.Ldloc_3); + + public static Instruction Stloc(MethodBody body, int index) { + switch (index) { + case 0: + return Stloc_0(); + case 1: + return Stloc_1(); + case 2: + return Stloc_2(); + case 3: + return Stloc_3(); + default: + return Stloc(body.Variables[index]); + } + } + + public static Instruction Ldloc(MethodBody body, int index) { + switch (index) { + case 0: + return Ldloc_0(); + case 1: + return Ldloc_1(); + case 2: + return Ldloc_2(); + case 3: + return Ldloc_3(); + default: + return Ldloc(body.Variables[index]); + } + } + + + // ldarg + public static Instruction Ldarg(ParameterDefinition arg) => Instruction.Create(OpCodes.Ldarg, arg); + public static Instruction Ldarg_0() => Instruction.Create(OpCodes.Ldarg_0); + public static Instruction Ldarg_1() => Instruction.Create(OpCodes.Ldarg_1); + public static Instruction Ldarg_2() => Instruction.Create(OpCodes.Ldarg_2); + public static Instruction Ldarg_3() => Instruction.Create(OpCodes.Ldarg_3); + + // starg + + public static Instruction Starg_S(ParameterDefinition arg) => Instruction.Create(OpCodes.Starg_S, arg); + + // array + public static Instruction Ldlen() => Instruction.Create(OpCodes.Ldlen); + + public static Instruction Ldelem(TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + return Instruction.Create(OpCodes.Ldelem_U1); + case MetadataType.SByte: + return Instruction.Create(OpCodes.Ldelem_I1); + case MetadataType.UInt16: + return Instruction.Create(OpCodes.Ldelem_U2); + case MetadataType.Int16: + return Instruction.Create(OpCodes.Ldelem_I2); + case MetadataType.UInt32: + return Instruction.Create(OpCodes.Ldelem_U4); + case MetadataType.Int32: + return Instruction.Create(OpCodes.Ldelem_I4); + case MetadataType.UInt64: + return Instruction.Create(OpCodes.Ldelem_I8); + case MetadataType.Int64: + return Instruction.Create(OpCodes.Ldelem_I8); + case MetadataType.Single: + return Instruction.Create(OpCodes.Ldelem_R4); + case MetadataType.Double: + return Instruction.Create(OpCodes.Ldelem_R8); + + default: + if (type.IsValueType) { + return Instruction.Create(OpCodes.Ldelem_Any, type); + } else { + return Instruction.Create(OpCodes.Ldelem_Ref); + } + } + } + + public static Instruction Stelem(TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + case MetadataType.SByte: + return Instruction.Create(OpCodes.Stelem_I1); + case MetadataType.UInt16: + case MetadataType.Int16: + return Instruction.Create(OpCodes.Stelem_I2); + case MetadataType.UInt32: + case MetadataType.Int32: + return Instruction.Create(OpCodes.Stelem_I4); + case MetadataType.UInt64: + case MetadataType.Int64: + return Instruction.Create(OpCodes.Stelem_I8); + case MetadataType.Single: + return Instruction.Create(OpCodes.Stelem_R4); + case MetadataType.Double: + return Instruction.Create(OpCodes.Stelem_R8); + default: + if (type.IsValueType) { + return Instruction.Create(OpCodes.Stelem_Any, type); + } else { + return Instruction.Create(OpCodes.Stelem_Ref); + } + } + } + + public static Instruction Ldelema(TypeReference arg) => Instruction.Create(OpCodes.Ldelema, arg); + + // conversions + public static Instruction Conv_I4() => Instruction.Create(OpCodes.Conv_I4); + public static Instruction Conv_U() => Instruction.Create(OpCodes.Conv_U); + + // functions + public static Instruction Call(MethodReference method) => Instruction.Create(OpCodes.Call, method); + public static Instruction Ldftn(MethodReference method) => Instruction.Create(OpCodes.Ldftn, method); + + // constants + + public static Instruction Ldstr(string value) => Instruction.Create(OpCodes.Ldstr, value); + public static Instruction Ldc_R4(float value) => Instruction.Create(OpCodes.Ldc_R4, value); + public static Instruction Ldc_R8(float value) => Instruction.Create(OpCodes.Ldc_R8, value); + + public static Instruction Ldc_I4(int value) { + switch (value) { + case 0: return Instruction.Create(OpCodes.Ldc_I4_0); + case 1: return Instruction.Create(OpCodes.Ldc_I4_1); + case 2: return Instruction.Create(OpCodes.Ldc_I4_2); + case 3: return Instruction.Create(OpCodes.Ldc_I4_3); + case 4: return Instruction.Create(OpCodes.Ldc_I4_4); + case 5: return Instruction.Create(OpCodes.Ldc_I4_5); + case 6: return Instruction.Create(OpCodes.Ldc_I4_6); + case 7: return Instruction.Create(OpCodes.Ldc_I4_7); + case 8: return Instruction.Create(OpCodes.Ldc_I4_8); + default: + return Instruction.Create(OpCodes.Ldc_I4, value); + } + } + + public static Instruction Stind_I4() => Instruction.Create(OpCodes.Stind_I4); + public static Instruction Ldind_I4() => Instruction.Create(OpCodes.Ldind_I4); + + public static Instruction Stind_or_Stobj(TypeReference type) { + if (type.IsPrimitive) { + return Stind(type); + } else { + return Stobj(type); + } + } + + public static Instruction Ldind_or_Ldobj(TypeReference type) { + if (type.IsPrimitive) { + return Ldind(type); + } else { + return Ldobj(type); + } + } + + public static Instruction Stind(TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + case MetadataType.SByte: + return Instruction.Create(OpCodes.Stind_I1); + case MetadataType.UInt16: + case MetadataType.Int16: + return Instruction.Create(OpCodes.Stind_I2); + case MetadataType.UInt32: + case MetadataType.Int32: + return Instruction.Create(OpCodes.Stind_I4); + case MetadataType.UInt64: + case MetadataType.Int64: + return Instruction.Create(OpCodes.Stind_I8); + case MetadataType.Single: + return Instruction.Create(OpCodes.Stind_R4); + case MetadataType.Double: + return Instruction.Create(OpCodes.Stind_R8); + default: + throw new ILWeaverException($"Unknown primitive type {type.FullName}"); + } + } + + public static Instruction Ldind(TypeReference type) { + switch (type.MetadataType) { + case MetadataType.Byte: + return Instruction.Create(OpCodes.Ldind_U1); + case MetadataType.SByte: + return Instruction.Create(OpCodes.Ldind_I1); + case MetadataType.UInt16: + return Instruction.Create(OpCodes.Ldind_U2); + case MetadataType.Int16: + return Instruction.Create(OpCodes.Ldind_I2); + case MetadataType.UInt32: + return Instruction.Create(OpCodes.Ldind_U4); + case MetadataType.Int32: + return Instruction.Create(OpCodes.Ldind_I4); + case MetadataType.UInt64: + return Instruction.Create(OpCodes.Ldind_I8); + case MetadataType.Int64: + return Instruction.Create(OpCodes.Ldind_I8); + case MetadataType.Single: + return Instruction.Create(OpCodes.Ldind_R4); + case MetadataType.Double: + return Instruction.Create(OpCodes.Ldind_R8); + default: + throw new ILWeaverException($"Unknown primitive type {type.FullName}"); + } + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverSettings.cs + +#if FUSION_WEAVER +namespace Fusion.CodeGen { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + static partial class ILWeaverSettings { + + public const string RuntimeAssemblyName = "Fusion.Runtime"; + + internal static float GetNamedFloatAccuracy(string tag) { + float? result = null; + GetAccuracyPartial(tag, ref result); + if ( result == null ) { + throw new ArgumentOutOfRangeException($"Unknown accuracy tag: {tag}"); + } + return result.Value; + } + + internal static bool NullChecksForNetworkedProperties() { + bool result = true; + NullChecksForNetworkedPropertiesPartial(ref result); + return result; + } + + internal static bool IsAssemblyWeavable(string name) { + bool result = false; + IsAssemblyWeavablePartial(name, ref result); + return result; + } + + internal static bool ContainsRequiredReferences(string[] references) { + return Array.Find(references, x => x.Contains(RuntimeAssemblyName)) != null; + } + + internal static bool UseSerializableDictionaryForNetworkDictionaryProperties() { + bool result = true; + UseSerializableDictionaryForNetworkDictionaryPropertiesPartial(ref result); + return result; + } + + static partial void GetAccuracyPartial(string tag, ref float? accuracy); + + static partial void IsAssemblyWeavablePartial(string name, ref bool result); + + static partial void UseSerializableDictionaryForNetworkDictionaryPropertiesPartial(ref bool result); + + static partial void NullChecksForNetworkedPropertiesPartial(ref bool result); + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverSettings.ILPostProcessor.cs + +#if FUSION_WEAVER && FUSION_WEAVER_ILPOSTPROCESSOR +namespace Fusion.CodeGen { + + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Runtime.Serialization.Json; + using System.Text; + using System.Xml.Linq; + using System.Xml.XPath; + + static partial class ILWeaverSettings { + + + public enum ConfigStatus { + Ok, + NotFound, + ReadException + } + + const string DefaultNetworkProjectConfigPath = "Assets/Photon/Fusion/Resources/NetworkProjectConfig.fusion"; + + static partial void OverrideNetworkProjectConfigPath(ref string path); + + public static string NetworkProjectConfigPath { + get { + var result = DefaultNetworkProjectConfigPath; + OverrideNetworkProjectConfigPath(ref result); + return result; + } + } + + static Lazy _config = new Lazy(() => { + using (var stream = File.OpenRead(NetworkProjectConfigPath)) { + var jsonReader = JsonReaderWriterFactory.CreateJsonReader(stream, new System.Xml.XmlDictionaryReaderQuotas()); + return XDocument.Load(jsonReader); + } + }); + + static Lazy _accuracyDefaults = new Lazy(() => { + + var element = GetElementOrThrow(_config.Value.Root, nameof(NetworkProjectConfig.AccuracyDefaults)); + + AccuracyDefaults defaults = new AccuracyDefaults(); + + { + var t = ParseAccuracies(element, nameof(AccuracyDefaults.coreKeys), nameof(AccuracyDefaults.coreVals)); + defaults.coreKeys = t.Item1; + defaults.coreVals = t.Item2; + } + + { + var t = ParseAccuracies(element, nameof(AccuracyDefaults.tags), nameof(AccuracyDefaults.values)); + defaults.tags = t.Item1.ToList(); + defaults.values = t.Item2.ToList(); + } + + defaults.RebuildLookup(); + return defaults; + }); + + static partial void GetAccuracyPartial(string tag, ref float? accuracy) { + try { + if (_accuracyDefaults.Value.TryGetAccuracy(tag, out var result)) { + accuracy = result._value; + } + } catch (Exception ex) { + throw new Exception("Error getting accuracy " + tag, ex); + } + } + + static partial void NullChecksForNetworkedPropertiesPartial(ref bool result) { + result = _nullChecksForNetworkedProperties.Value ?? result; + } + + static partial void IsAssemblyWeavablePartial(string name, ref bool result) { + result = _assembliesToWeave.Value.Contains(name); + } + + static partial void UseSerializableDictionaryForNetworkDictionaryPropertiesPartial(ref bool result) { + result = _useSerializableDictionary.Value ?? result; + } + + public static bool ValidateConfig(out ConfigStatus errorType, out Exception error) { + try { + error = null; + if (!File.Exists(NetworkProjectConfigPath)) { + errorType = ConfigStatus.NotFound; + return false; + } + + _ = _config.Value; + _ = _assembliesToWeave.Value; + + errorType = ConfigStatus.Ok; + return true; + } catch (Exception ex) { + error = ex; + errorType = ConfigStatus.ReadException; + return false; + } + } + + static XElement GetElementOrThrow(XElement element, string name) { + var child = element.Element(name); + if (child == null) { + throw new InvalidOperationException($"Node {name} not found in config. (path: {element.GetXPath()})"); + } + return child; + } + + static IEnumerable GetElementsOrThrow(XElement element, string name) { + return GetElementOrThrow(element, name).Elements(); + } + + static float GetElementAsFloat(XElement element, string name) { + var child = GetElementOrThrow(element, name); + try { + return (float)child; + } catch (Exception ex) { + throw new InvalidOperationException($"Unable to cast {child.GetXPath()} to float", ex); + } + } + + static (string[], Accuracy[]) ParseAccuracies(XElement element, string keyName, string valueName) { + var keys = GetElementsOrThrow(element, keyName).Select(x => x.Value).ToArray(); + var values = GetElementsOrThrow(element, valueName).Select(x => GetElementAsFloat(x, nameof(Accuracy._value))) + .Zip(keys, (val, key) => new Accuracy(key, val)) + .ToArray(); + return (keys, values); + } + + static Lazy> _assembliesToWeave = new Lazy>(() => { + return new HashSet(GetElementsOrThrow(_config.Value.Root, nameof(NetworkProjectConfig.AssembliesToWeave)).Select(x => x.Value), StringComparer.OrdinalIgnoreCase); + }); + + static Lazy _nullChecksForNetworkedProperties = new Lazy(() => { + return (bool?)_config.Value.Root.Element(nameof(NetworkProjectConfig.NullChecksForNetworkedProperties)); + }); + + static Lazy _useSerializableDictionary = new Lazy(() => { + return (bool?)_config.Value.Root.Element(nameof(NetworkProjectConfig.UseSerializableDictionary)); + }); + + + public static string GetXPath(this XElement element) { + var ancestors = element.AncestorsAndSelf() + .Select(x => { + int index = x.GetIndexPosition(); + string name = x.Name.LocalName; + return (index == -1) ? $"/{name}" : $"/{name}[{index}]"; + }); + + return string.Concat(ancestors.Reverse()); + } + + public static int GetIndexPosition(this XElement element) { + if (element.Parent == null) { + return -1; + } + + int i = 0; + foreach (var sibling in element.Parent.Elements(element.Name)) { + if (sibling == element) { + return i; + } + i++; + } + + return -1; + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/ILWeaverSettings.UnityEditor.cs + +#if FUSION_WEAVER && !FUSION_WEAVER_ILPOSTPROCESSOR +namespace Fusion.CodeGen { + + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + + static partial class ILWeaverSettings { + + static partial void GetAccuracyPartial(string tag, ref float? accuracy) { + if (NetworkProjectConfig.Global.AccuracyDefaults.TryGetAccuracy(tag, out var _acc)) { + accuracy = _acc.Value; + } + } + + static partial void IsAssemblyWeavablePartial(string name, ref bool result) { + if (Array.Find(NetworkProjectConfig.Global.AssembliesToWeave, x => string.Compare(name, x, true) == 0) != null) { + result = true; + } + } + + static partial void UseSerializableDictionaryForNetworkDictionaryPropertiesPartial(ref bool result) { + result = NetworkProjectConfig.Global.UseSerializableDictionary; + } + + static partial void NullChecksForNetworkedPropertiesPartial(ref bool result) { + result = NetworkProjectConfig.Global.NullChecksForNetworkedProperties; + } + } +} +#endif + +#endregion + + +#region Assets/Photon/FusionCodeGen/InstructionEqualityComparer.cs + +#if FUSION_WEAVER && FUSION_HAS_MONO_CECIL +namespace Fusion.CodeGen { + using System.Collections.Generic; + using Mono.Cecil.Cil; + + internal class InstructionEqualityComparer : IEqualityComparer { + public bool Equals(Instruction x, Instruction y) { + if (x.OpCode != y.OpCode) { + return false; + } + + if (x.Operand != y.Operand) { + if (x.Operand?.GetType() != y?.Operand.GetType()) { + return false; + } + // there needs to be a better way to do this + if (x.Operand.ToString() != y.Operand.ToString()) { + return false; + } + + } + + return true; + } + + public int GetHashCode(Instruction obj) { + return obj.GetHashCode(); + } + } +} +#endif + +#endregion + +#endif diff --git a/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs.meta b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs.meta new file mode 100644 index 0000000..58b5957 --- /dev/null +++ b/Assets/Photon/FusionCodeGen/Fusion.CodeGen.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 800c2efef414be248b2a8c6cf25bf27f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/PhotonLibs.meta b/Assets/Photon/PhotonLibs.meta new file mode 100644 index 0000000..a076eb8 --- /dev/null +++ b/Assets/Photon/PhotonLibs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0e561730343a7dd4f966ba529ec7a101 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/PhotonLibs/changes-library.txt b/Assets/Photon/PhotonLibs/changes-library.txt new file mode 100644 index 0000000..58bc978 --- /dev/null +++ b/Assets/Photon/PhotonLibs/changes-library.txt @@ -0,0 +1,1815 @@ + +Photon C# Client Library - Changelog +Exit Games GmbH - www.photonengine.com - forum.photonengine.com + + +Version 4.1.6.11 (23. November 2021 - rev6288) + Fixed: Payload encryption initialization (without DiffieHellman) for TCP connection. + Fixed: Protocol16.DeserializeCustom() to check if length of read bytes is available (else, a byte[] is returned). + Changed: Custom Type arrays will return UnknownType[] if the array's item-type is unknown. If the custom type is known but items do not read properly, they will be null. + Added: Checks if each generated Custom Type instance can be assigned to the Custom Type array (and skips those that are null or can't be assigned). + Changed: Protocol18Read.ReadCustomType() and ReadCustomTypeArray() to throw error for size < 0. In that case, it is not possible to continue reading and the data is compromized. + Removed: Some logging code that was specific for Unity. + Removed: Exception handling for OnEvent and OnOperationResponse calls. This turned out somewhat inflexible and was hiding valuable debug information. Instead of the low level try/catch, projects should wrap DispatchIncomingCommands in try/catch blocks. + +Version 4.1.6.10 (21. October 2021 - rev6243) + Fixed: ReadCustomTypeArray to return an object[], if any item in the array is unreadable. This object[] contains any properly read custom types but also items of UnknownType. This is of course likely to cause exceptions in the dispatch callbacks. + Added: Exception handling to OnEvent and OnOperationResponse calls. + +Version 4.1.6.9 (12. October 2021 - rev6231) + Fixed: Reliable UDP protocol to send pings, if no reliable command was acknowledged or sent recently. This bug was preventing a client timeout in some cases. + +Version 4.1.6.8 (30. September 2021 - rev6222) + Fixed: ReadCustomType to not use custom deserialization methods, should the reported size be less than available bytes. + Fixed: ReadCustomType and ReadCustomTypeArray to also skip custom types when size is 0. + +Version 4.1.6.7 (27. September 2021 - rev6214) + Fixed: A bug in Protocol18 reader for very long arrays. + +Version 4.1.6.6 (21. September 2021 - rev6204) + Changed: Dns resolution. It is suspected to fail sometimes due to a Mono/Unity bug (issue 6940). Changing from only attempting Dns.GetHostEntry to first attempting Dns.GetHostAddresses followed by Dns.GetHostByName in case of an Exception (yes obsolete api but maybe a different implementation). + Removed: C# wrapper classes for native plugins (sockets and encryption). These classes will be part of the plugin packages. + Changed: PhotonPeer values NativeSocketLibAvailable, NativePayloadEncryptionLibAvailable and NativeDatagramEncryptionLibAvailable are now obsolete. Unity's IL2CPP does not implement PrelinkAll in IL2CPP and we moved the wrapper classes for native libs out of the assembly. + Added: PayloadEncryptorType and related usage to be able to set another Payload Encryption implementation. This is only for completeness. The managed implementation, which is used by default, is fine. + Fixed: TrafficStatsIncoming for PhotonClientWebSocket (used by default in WS/WSS connections in the .Net Standard 2.0 dll). + +Version 4.1.6.5 (24. August 2021 - rev6181) + Fixed: Protocol16 to throw a NotSupportedException when a length value can not be serialized as short (as defined). This affects arrays (except byte[]), strings, hashtables, dictionaries and other variable length values. + Updated: C# classes to the latest state of the native PhotonEncryptorPlugin API. + +Version 4.1.6.4 (26. July 2021 - rev6143) + Changed: ByteArraySlice and ByteArraySlicePool to achieve thread safety. + Changed: PhotonClientWebSocket.AsyncConnectAndReceive to reuse memory. It may still grow somewhat as memory allocated for very large messages is not released but rather held to receive as big messages. This class is not used for WebGL exports. + Removed: Surplus logging in PhotonClientWebSocket implementation. + Added: Null-check in PhotonClientWebSocket.Send() if the socket may be disconnecting already. Added fitting error log. + Changed: TCP implementation to avoid memory allocations. It now uses a Queue which is pooled and re-used. DispatchIncomingCommands() releases the StreamBuffers back into the pool. + Fixed: TrafficStats counting of ping-result command size. This was still counting the buffer size instead of received-data size (usually less). + +Version 4.1.6.3 (24. June 2021 - rev6099) + Fixed: An issue with deserialization of custom types. + +Version 4.1.6.2 (23. June 2021 - rev6092) + Fixed: Potential NullReferenceException in PhotonClientWebSocket.Disconnect(). If called before the connect-flow finished, the instance was null. + +Version 4.1.6.1 (09. June 2021 - rev6086) + Fixed: TPeer.TotalPacketCount and TotalCommandsInPackets counters. They were increased in two places for the Init message. + Fixed: TPeer.TrafficStatsOutgoing.TotalCommandsInPackets counting in SendData, which was adding the remaining count in queues instead of +1. + Changed: TPeer.SendOutgoingCommands(), which will now stop sending if more than MTU size of data was sent. + Note: Messages are not fragmented for TCP/WSS, so they can be well over MTU size as such. But SendOutgoingCommands() will now return if there are more messages, so sending can be limited somewhat. + Changed: PhotonClientWebSocket will pause the task only for 50ms if nothing was received. + Changed: Log level of PhotonClientWebSocket incoming bytes logging. It's now level "ALL". + +Version 4.1.6.0 (19. May 2021 - rev6070) + Changed: PhotonPeer.ClientVersion is now marked as obsolete. The value should be static and so we added PhotonPeer.Version instead as replacement. + Internal: EnetPeer.Reset() now sets the timeoutInt = 0 and does a sentReliableCommands.Clear() instead of replacing it. + Changed: PhotonPeer.IsSendingOnlyAcks is now obsolete. Use SendAcksOnly() where needed. + Changed: EnetPeer.QueueSentCommand will not set the timeoutInt. Instead, this should be set when the next resends are checked in SendOutgoing. + Changed: Resends of reliable commands are now done directly, without them ever leaving the sentReliableCommands list. This has the benefit of being able to resend those commands in sequence. + Internal: On resend, timeoutInt becomes the lowest value found in the sentReliableCommands. It stays unchanged, if any command wasn't resent. + Added: SerializeCommandToBuffer, which is split out of SerializeToBuffer(Queue commandList). Allows sending single commands. + Changed: QueueSentCommand will not add reliable commands to the sentReliableCommands list, if the parameter signals it was in there already. + Changed: PepareWebSocketUrl() will now write the parameter "libversion" instead of "clientversion". Renamed according to Slack discussion. + Changed: PhotonPeer time keeping to use a Stopwatch per peer, instead of one in the SupportClass. + Changed: PeerBase now has a Stopwatch and PeerBase.timeInt will use that. + Removed: All internal usages of SupportClass.GetTickCount(). + Note: Socket implementations should replace their usage of SupportClass.GetTickCount() - peerBase.timeBase with this.peerBase.timeInt. + Changed: PhotonPeer.LocalMsTimestampDelegate, SupportClass.IntegerMillisecondsDelegate, .IntegerMilliseconds and .GetTickCount() are obsolete now. + Added: EnetPeer.QUICK_RESEND_QUEUELIMIT (value: 25) to suspend quick resending if the sent-queue is larger than that value. + Internal: Minor changes to NCommand. + Added: PhotonPeer SequenceDeltaLimitSends and SequenceDeltaLimitResends to prevent sending more commands, when the server does not keep up with acknowledging them. If those values are 0, this new logic is disabled. + Added: EnetChannel.highestReceivedAck to keep track of the highest sequence number ACKed by the server. + Changed: EnetPeer.SendOutgoingCommands() and .SendAcksOnly() to implement the limit. + +Version 4.1.5.5 (06. May 2021 - rev6050) + Changed: Protocol18 ReadCustomType to no longer throw an exception when a unknown custom type arrives. Instead pass UnknownType. + Added: Type "UnknownType", which is used to pass not-deserialized custom types to the game logic. + Changed: Protocol18 ReadCustomType to use a "boundedSize" to read remaining data if the incoming size value is > remaining message data. + Internal: StreamBuffer.Available to get remaining bytes in buffer. Returns value >= 0. + Fixed: Protocol18 WriteArraySegmentByte to not write any bytes, if count is 0. + Fixed: Potential deserialization issues in protocol 1.6, which could be exposed to crash clients. + Changed: String deserialization in protocol 1.6 avoids some memory allocation. + Changed: String deserialization in protocol 1.6 throws a NotSupportedException if the length is negative (was never supported but now it's a clear error). + Changed: DeserializeDictionary in Protocol16, will no longer allow key- or value-types Dictionary or Array. Both were read incorrectly. + Changed: DeserializeDictionaryType in Protocol16, will no longer allow key- or value-types Dictionary or Array. Both were read incorrectly. + Fixed: Error message in 1.8 GetAllowedDictionaryKeyTypes. + +Version 4.1.5.2 (12. March 2021 - rev6017) + Fixed: A compatibility issue of the Native Encryptor API v2 and Unity UWP exports using the .Net Runtime as runtime. The incompatibility made the reference rewriter fail. Introduced in the Metro build of the library in v4.1.5.0. + Fixed: EncryptorNative will not use the new "native-to-managed callback API" when compiling for NETFX_CORE (UWP Metro). + +Version 4.1.5.1 (01. March 2021 - rev5999) + Note: Release as SDK. + +Version 4.1.5.1 (25. February 2021 - rev5991) + Fixed: Handling of IPv6 addresses (bug was introduced in v4.1.5.0). + +Version 4.1.5.0 (23. February 2021 - rev5986) + Added: Optional parameter CustomInitData to Connect() methods. This data/object is used in InitV3. + Changed: Parameter name for photonToken (was "customdata" or similar). + Internal: PrepareInitV3ConnectData(...) is now WriteInitRequest() without the bunch of parameters which were all coming from the PeerBase anyways. + Fixed: NonAllocDictionary.Remove will also remove the value-reference by setting a default. + Changed: Handling of server IP addresses. IpAddress.TryParse has a few false positives. Those are double-checked and may cause ServerAddressInvalid. + Changed: Failed DNS lookup uses status code: DnsExceptionOnConnect. This clarifies a set of runtime errors, which were hard to identify. + Added: StatusCode ServerAddressInvalid = 1050 and DnsExceptionOnConnect = 1051. Used in IPhotonPeerListener.OnStatusChanged(). + Changed: Native Encryptor API v2 is now required. This affects native Datagram Encryption plugins (and allows GCM / Mode 13). + Changed: PDB files are now in "portable" format. This should be equivalent to "full" (previously used) but compatible with latest Unity versions. + +Version 4.1.4.9 (11. January 2021 - rev5966) + Fixed: EnetPeer to reset values for unsequenced commands on connect. Before, clients would send illegal command numbers after switching connections. + Added: Special handling for StructWrapper in DictionaryToString(). Pretty-prints type and value. + Added: StructWrapper.ToString() and and ParameterDictionary.ToStringFull() to simplify logging. + Fixed: Add overload in ParameterDictionary was sending ints to byte, rather than object. + Changed: NonAllocDictionary now implements IDictionary. + Changed: ValueIterator and KeyIterator now implement IEnumerator, System.Collections.IEnumerator. + Changed: PairIterator now implement IEnumerator>. + +Version 4.1.4.8 (03. December 2020 - rev5915) + Fixed: Issue with DisconnectTimeout, which could be set to int.MaxValue and force a timeout. + Note: The maximum value that is applied as DisconnectTimeout is now 65535 (ushort.MaxValue). Negative values set to default timeout. + Added: PhotonPeer.RemoveAppIdFromWebSocketPath option to skip the appid in websocket connects. Defaults to false. + Internal: PeerBase.PepareWebSocketUrl to skip the appid when RemoveAppIdFromWebSocketPath is true. Changed order of parameters. + Note: The AppId is not mandatory on the PhotonCloud but might be needed to connect to Photon OnPremise when the port does not define the app type. + +Version 4.1.4.7 (26. November 2020 - rev5893) + Fixed: PhotonClientWebSocket handling of messages > MTU. Incoming messages may be incomplete and are now reassembled. Before the fix, common result was that deserialization failed with incorrect / incomplete data. + Added: ExitGames.Client.Photon.Hashtable Add() and Remove() for byte keys. This makes sure ht.Add(0, obj) and ht.Remove(0) match how the access via ht[0] works. + +Version 4.1.4.6 (17. November 2020 - rev5865) + Changed: The EventData.Paramters dictionary is now readonly. It does not need to be settable and can no longer be null. So null-checks are removed. + Changed: If the ByteArrayPool is used for reading events, the sender's actorNumber will not longer be in the EventData parameter-table. Instead, only the Sender property is used. + Note: This makes receiving EventData events non-alloc by not adding the Sender key/value to the Parameters (which would box the int value). + Note: The EventData.Sender property was available for a while and was a better way to access the event-sender's actornumber. + Changed: PhotonPeer.SendOperation() and PhotonPeer.SendMessage() will now check a few conditions (is connected, etc) and call PeerBase.SerializeOperationToMessage(). + Internal: Changed the code path for operations and messages serialization. There are lots of internal changes for this but no externals. + Internal: Message header length is set in EnqueueMessageAsPayload along with other updates (was in TPeer.SerializeOperationToMessage()). + Removed: PhotonPeer.OpCustom(), which was already obsolete for a long time. + Added: PeerBase.IsTransportEncrypted() to help figure out if Payload Encryption should be skipped. + Changed: PeerBase.SerializeMessageToMessage no longer updates the header for TCP. Done in EnqueueMessageAsPayload, used by EnqueuePhotonMessage. + Changed: SerializeMessageToMessage to check existing value isRawMessage instead of re-checking the condition when applying message type. + Fixed: Detection of Generic-Dictionary-typed arrays. Was lacking a test if the element was of type Dictionary. + Changed: Connect(string serverAddress, string applicationName) to call variant with proxy-support directly. + Updated: Connect methods and their docs. + Removed: More of the RHTTP support. + Added: DisconnectMessage deserialization and an event for this case: PhotonPeer.OnDisconnectMessage. This does not break the dll's compatibility. + Changed: Connect() workflow. This simplified the TPeer and EnetPeer somewhat and unifies some actions that were scattered in the classes hierarchy. + Changed: PhotonPeer to create the SocketImplementationConfig in the constructor. Connect() will use this to create the actual IPhotonSocket. + Changed: TPeer.Connect and EnetPeer.Connect are a bit simpler now (don't have to create the actual IPhotonSocket). + Removed: PeerBase.SocketImplementation member. + Changed: Setter and handling for the Version.clientVersion (conditional for SERVERSDK). + Changed: Version.clientVersion is readonly, unless SERVERSDK is defined. + Changed: CreatePeerBase() will only create a new EnetPeer or TPeer on demand (if the protocol changed or there was no PeerBase implementation). + Added: Special treatment for byte-typed keys to the Photon Hashtable to avoid boxing. Pre-boxed items are stored in a static array. + Added: NonAllocDictionary support in SupportClass (DictionaryToString()). + Added: ParameterDictionary as type and support to de/serialize it. + Note: This is a WiP commit to get LoadBalancing non-allow (or less alloc). + Added: PhotonPeer.WrapIncomingStructs to control how to de-serialize responses and events. Setting this requires adjusted code. + Note: As long as WrapIncomingStructs is not set, code changes are not mandatory. + Fixed: Error message in SerializeDictionaryHeader to include the type of the value which failed (it was logging the key's type). + Changed: The PhotonClientWebSocket does a DNS request before connecting, to figure out if IPv6 should be requested by Photon servers. + Added: PhotonPeer.TargetFramework to identify the target framework that the dll was built-for. Might help identify the used variant. + Changed: Code order a little, to get obsolete fields up in the range for them. + Removed: RhttpMinConnections and RhttpMaxConnections, which were entirely obsolete. + Changed: OperationResponse indexer is not obsolete. This was used to detect where it's used. + Changed: ParameterDictionary now has a Get() method, so the API is analog to Unwrap() in this case. + Changed: EnetChannel to use NonAllocDictionary instead of Dictionary. Our replacement is internal and non-allocating for iteration. + Changed: the logging level of events possibly happening on each Service() call changed from INFO to ALL + Changed: EnqueueMessageAsPayload stats-keeping no longer throws an exception, when the delivery mode is "out of range". + Internal: Changed how the client writes acknowledgements. + Internal: TPeer.ReceiveIncomingCommands() will only log "Wrong MagicNumber", if the received length is more than 0. It also logs the length then. + Changed: PhotonClientWebSocket (in .Net Standard 2.0 dll) now checks if the clientWebSocket state is still "Open" before handling incoming data. + Changed: PhotonClientWebSocket logs INFO, when the received data has length 0 but the socket is still Open. + + +Version 4.1.4.5 (04. September 2020 - rev5733) + Fixed: Serialization of a ByteArraySlice will now release it, as described in the docs. So you can pass a ByteArraySlice to RaiseEvent and control of it is passed over to the Photon API. + Fixed: Potential issues with SupportLogger.StartBackgroundCalls() when there were 255 threads running (and or stopped). + Changed: When PhotonClientWebSocket fails to connect, this will now correctly result in a disconnect with code/reason ExceptionOnConnect. + Changed: The socket reading-thread not call it's disconnect method, if already disconnecting/disconnected. This avoids a surplus call (and callback). + Internal: The thread locking object in SocketNative is no longers static. + Note: The next release will change the native datagram encryptor api. Most likely this is the last one compatible with the old native plugin. + +Version 4.1.4.4 (29. June 2020 - rev5625) + Fixed: NonAllocDict. Capacity change was broken. This is a critical fix. + Added: Indexer to NonAllocDict to allow Dict[key] get and set. + Added: NonAllocDict.Clear() method. + +Version 4.1.4.3 (24. June 2020 - rev5622) + Added: PhotonPeer.UseByteArraySlicePoolForEvents and PhotonPeer.ByteArraySlicePool to avoid memory garbage when events consist only of a byte-array. See comments / doc. + Added: Class NonAllocDictionary as replacement for the suboptimal implementation in Mono. Some of our code relies on Dictionaries and can be optimized with this. Note: This is neither documented well yet and considered "beta" until we got feedback on some use cases. + Added: PhotonPeer.TrafficRecorder and ITrafficRecorder. This can be used to capture traffic on UDP connections. + Changed: IPhotonSocket.ConnectAddress is now a local field. Before, it accessed the peer which might already connect to another server. + Changed: Debug log for DNS resolution (INFO level). This includes a count of IPs that were returned. + Added: Static PhotonPeer.NoNativeCallbacks to disable callbacks from native code to C#. SocketNative is the first class to use NoNativeCallbacks in DnsAndConnect(). + Changed: SocketUdp and SocketTcp are now public, like other implementations. This means you can assign them to the SocketImplementationConfig and select these deliberately. + Internal: Datagram Encryption was updated and optimized. (rev5548+rev5550). Added PhotonPeer.NativeEncryptorApiVersion constant to be able to check which native encryptor API is being compiled into the assembly. + Changed: EnetPeer.ReceiveIncomingCommands(): bytesIn counter simplified: now it simply incremented by input data size. + Removed: "CommandLog" feature. It was replaced by Traffic Recording. Remaining setting values for this (in PhotonPeer) are now Obsolete and without function. + Changed: WebSocket init workflow to make it compatible with AuthOnce / AuthOnceWss. It can send an init request now. + Internal: WebSockets need to use the ConnectAddress, as the TPeer will setup the address and path as required. + Changed: The IPhotonSocket does not set the ConnectAddress again, so the Peer can set this address before connecting. This is used for WebSocket connects. + Fixed: NETFX_CORE variant of payload encryption initialization. + Added: PreserveAttribute and used it on IPhotonSocket constructors. This keeps Unity's code stripping from constructors which we need. + Fixed: A rare threading issue for UDP connections. + Internal: SupportClass.StartBackgroundCalls() and related methods are now locked. A proper cleanup of StartBackgroundCalls is pending. + Changed: SupportClass.StopBackgroundCalls(id) will now clear the reference to the used thread but not remove the entry in the list. + Changed: SupportClass.StopAllBackgroundCalls() will now clear all referenced threads. Could be used when disconnected / before another connect to reduce number of Threads. + Removed: SupportClass.CallInBackground, which was replaced by StartBackgroundCalls a while ago. + + +Version 4.1.4.2 (08. May 2020 - rev5519) + Updated: DotNet SDK (net35, UWP and .Net Standard 2.0) and Unity SDK. +Version 4.1.4.1 (30. April 2020 - rev5482) + Fixed: String serialization for characters with more bytes. UTF8.GetBytes was called with byte-count instead of character-count as parameter. +Version 4.1.4.0 (28. April 2020 - rev5474) + Internal: Changed serialization of strings to produce no memory garbage. Strings that exceed a UTF8-encoded byte-length of 32767 (short.MaxValue) are not supported in either protocol (now throwing an exception). + Internal: Improved the serialization of float, double, float-array and double-array to produce less memory garbage with better performance. + Fixed: Cross platform serialization of float and double values in protocol 1.8 (which is used by default by PUN 2). Undiscovered, the C# client was using a wrong byte-order for network. The server and native clients were receiving a wrong value. Between C# clients, everything was fine. + Added: WebSocket support built-in to the Metro / UWP assembly. The PhotonMessageWebSocket is automatically used, unless some override is defined externally. + Changed: The socket implementations now disconnect when an exception happens due to sending. This uses the StatusCode.SendError, reported via OnStatusChanged callback. The LoadBalancingClient will handle this and report a DisconnectCause.Exception. Careful when updating only the library: Handle this in LoadBalancingClient. + Changed: The internal exception logging is now in log level INFO. This means the handled exception is no longer logged as if it's not. + Internal: Reverted to using the datagram encryption API v1. The new one is not fully implemented and should not be used yet (but was in v4.1.3.0). + Fixed: If a timeout message arrives after the client disconnected locally, the timeout is neither reported (via OnStatusChanged) nor does it trigger (another) disconnect. This avoids rare issues where clients would get stuck in Disconnecting state. + Added: Initial changes to support proxies for WSS. The PhotonPeer got a Connect overload which sets the proxy address and the IPhotonSocket got ProxyServerAddress. Higher level changes are upcoming. + Fixed: When the StreamBuffer for a large message got fragmented, this goes back to the StreamBuffer pool. + Changed: ExecuteCommand() for fragments. We now use a StreamBuffer from the pool and only adjust the buffered size if needed. + Added: Static PhotonPeer.MessageBufferPoolSize() to have access to the current StreamBuffer pool size. + +Version 4.1.3.0 (23. March 2020 - rev5399) + Internal: Changed Visual Studio projects which build the C# Photon libraries. Added a .Net Standard 2.0 assembly, which has built-in WebSocket support. + Added: IPhotonSocket.SerializationProtocol property. This provides the protocol of the current PhotonPeer.SerializationProtocolType as string. + Note: Some WebSocket implementations use a static value of the same name and need to be updated! Some projects contain SocketWebTcp.cs, SocketWebTcpThread.cs or similar files. + Changed: It is now possible to signal "on demand encryption" (known as Payload Encryption within Photon terms) even on secure connections (WSS / Datagram Encryption). This is important (only) for mixed connection types. A server update is required. The Photon Cloud is updated. + Added: PhotonPeer.SendInCreationOrder. This defaults to true, enabling the new behaviour explained below. + Changed: The send order of reliable and unreliable commands (UDP). This improves throughput of unreliable commands, when there are multiple datagrams of data. Before, a datagram got filled with the reliable commands first. Only when those were gone, unreliable commands were sent. The server was discarding some messages are too late. + Updated: Pool to be used in higher level APIs. Pool.Count is now also locked. + Internal: EnetPeer ocal variable fragmentLength to currentFragmentSize. It no longer hides EnetPeer.fragmentLength, which was probably causing issues in the Native Toolchain (Master builds for HoloLens 2). + Internal: Datagram Encryption now has a new mode: GCM. It can be used seamlessly with the Photon Cloud. + Internal: Native Datagram Encryption plugins and APIs are now more efficient. + Removed: rHttp support. + +Version 4.1.2.20 (12. December 2019 - rev5296) + Changed: DiffieHellmanCryptoProviderNative is now always compiled into the assembly using dynamic linking. If the payload encryption native library is present, it will be used automatically. + Changed: Extern methods with the DllImport attribute are now public. This allows a PrelinkAll() check if the dll is available and can be loaded. + Internal: EncryptorNative will no longer use native extern methods in static fields. This causes execptions in unexpected context. + Added: PhotonPeer NativePayloadEncryptionLibAvailable, NativeDatagramEncryptionLibAvailable and NativeSocketLibAvailable values. + Changed: SocketNative is autmatically used if the native socket lib is available, based on the checks above. + Changed: The wrappers for the native libraries (sockets and datagram encryption) will now setup a debug logging callback with debug level. This requires new native libraries. + +Version 4.1.2.19 (13. November 2019 - rev5271) + Internal: Handling for DllNotFoundException in the SocketNative.DnsAndConnect(). + Internal: Added support for setting the serialization protocol when using native websocket. + Internal: Added logging to native plugins for sockets and encryptor. + Internal: Changed the access level of PeerBase.debugOut from 'internal' to 'public'. + Changed: UDP socket classes now trigger a disconnect in the send methods, should the lower-level socket be disconnected. This gives us a quicker "disconnected" state in some error cases. So far, only reading-errors triggered disconnects in UDP. + Changed: Logging in case of send errors. + Added: Exception handling to deserialization. This adds a safety layer for receiving messages, which can be skipped. Important: It will be unknown, what message got discarded and if it's important or if it was fake / surplus. + Fixed: Internal class EncryptorNet, which was not thread safe, so DatagramEncryption was failing sooner or later. This affected only v4.1.2.18, which was an internal release. + +Version 4.1.2.18 (1. October 2019 - rev5229) + Removed: Surplus logging "Deserializing OpResponse Ping." for TCP/WebSocket connections. + Changed: Datagram Encryption to use an interface for the encryptor. + Changed: PhotonPeer.Encryptor is now public and can be set before connect. Applications can switch to new Datagram Encryption implementations. InitDatagramEncryption will create an instance and use that or falls back to the managed encryptor. + Note: PhotonPeer.Encryptor replaces DgramEncryptor. Setting the class before connect is likely what the LoadBalancingClient will do anyways. + Changed: The EncryptorNative class is for dynamic linked libs. It can be present in all Unity projects. + Changed: PhotonPeer.NativeDatagramEncrypt is obsolete, as it's always true now. On demand, an alternative Encryptor can be set. + Internal: ClientSdkIdShifted is simplified (because it was causing an error in another compiler / in Unity). + Changed: Reading Hashtables and Dictionaries will now check if the read key is null. If so, the key-value pair will be dropped silently. This applies to Protocol 1.6 and 1.8 alike. + Changed: All built-in socket implementations which are now more alike to one another. + Changed: When DNS resolution provides more than one IP, the socket will use the IPv6 ones first. If there is no route to the remote IP, the next in list is used. It does not resolve strings with IPv4 (e.g. a local address). + +Version 4.1.2.17 (9. August 2019 - rev5187) + Removed: Obsolete StatusCode values: DisconnectByServer is now DisconnectByServerTimeout. Other values are simply removed. + Updated: Used StatusCode.DisconnectByServer to DisconnectByServerTimeout. A follow up to renaming and removing the code. Some implementations of IPhotonSocket can be in existing projects and may still use the old naming. Simply rename. + Changed: PeerBase.timeInt is now a property which calculates the current time: SupportClass.GetTickCount() - this.timeBase. + Fixed: The change for timeInt fixes a problem with TCP connections which go entirely silent and would no longer update the PhotonPeer.ConnectionTime. + +Version 4.1.2.16 (28. June 2019 - rev5168) + Changed: TrafficStats Stopwatch initialization. This keeps the Stopwatch but resets it. Also it's started on initialization, if trafficStatsEnabled. + Changed: VitalStatsToString() can now return a useful stats string, even if TrafficStatsGameLevel is null (which is never the case currently). + Added: VitalStatsToString() now logs the "Longest send" time. This is a new value to debug connections. + Changed: NETFX_CORE now also uses Activator.CreateInstance, if a SocketImplementation is set (as in other implementations). + +Version 4.1.2.16 (24. June 2019 - rev5154) + Fixed: DispatchIncomingCommands() for reliable UDP. In some cases, an unreliable command/message could dispatch before the related reliable command (which was written and sent earlier). This issue was introduced in v4.1.2.11, 15. April 2019. + Fixed: Each thread created via SupportClass.StartBackgroundCalls will now try-catch a ThreadAbortException. Apparently the handling of a ThreadAbortException changed with Unity 2019, which started to log the exceptions on Android, IL2CPP, 4.x runtime. + +Version 4.1.2.15 (07. June 2019 - rev5137) + Changed: Tcp messages with magic byte 0xF4 are no longer accepted (this was used on a deprecated platform). + Changed: An Internal Operation Response for OpPing is now executed right away, fixing timing problems when using Op Ping (in WSS, e.g. on Xbox). + Added: PhotonPeer.InitialResentTimeMax. It guarantees resends for commands, despite bad lag (which may be temporary). + Changed: NCommand.ToString() for slightly better organization and naming of values. This is mostly a debugging help for the Photonians. + +Version 4.1.2.14 (16. May 2019 - rev5128) + Changed: The single "release history" file is now split. There is one "changes" file per api/level. For example: changes-realtime.txt lists the LoadBalancing / Realtime API changes. Find them in the respective subfolders. + Changed: The structure of the Unity SDK. As in PUN 2, there is a "Photon" folder, which wraps up all apis and libraries within a Unity project. + Updated: The readme txt. +Version 4.1.2.14 (6. May 2019 - rev5097) + Changed: EventData now has Sender or CustomData properties as well as SenderKey and CustomDataKey. The keys can be defines according to API. The default are the Realtime API values. Sender and CustomData access is cached. If an EventData instance is reused (PhotonPeer.ReuseEventInstance), the values will reset. + +Version 4.1.2.13 (3. May 2019 - rev5086) + Changed: EventData to not contain the Sender or CustomData properties anymore. They have been specific for the Realtime API and confusing for APIs like Chat. + Added: PhotonPeer.ReuseEventInstance as option to use a single EventData instance for all incoming events. This reduces memory garbage. The drawback is: An event provided via OnEvent(EventData photonEvent) is invalidated right after the callback finished. That event's content will get modified. Typically this is not a problem as events are rarely cached. + Internal: Added caching for a EventData instance (on demand). + +Version 4.1.2.12 (26. April 2019 - rev5046) + Fixed: A problem with the new EventData code of v4.1.2.11. It optimistically assumed that events with codes below 200 were always just containing Sender and CustomData. Photon Chat uses different codes, however. + Added: Access the Sender and CustomData via the EventData.Parameters dictionary, even if that is null (due to the new rules). In that case, the Sender and CustomData is accessed via the properties instead. + +Version 4.1.2.11 (15. April 2019 - rev5043) + Note: This version has a few changes that optimize memory usage mostly. + Internal: Changed EventData class (custom and Photon-defined events). Custom events set the Sender and Code and the CustomData but not Parameters (dict) now. EventData indexer checks if the "Parameters" are non-null. If the indexer is used to access non-existing Parameters, the result is null. Custom events are NOT setting Parameters anymore, saving the Dictionary<> use. + Internal: StreamBuffer.ReadByte() to return a byte or throw an exception when the end of the stream was reached. The -1 value was never used though. + Changed: EnetPeer.DispatchIncomingCommands() as optimization. When dispatching unreliable commands, the code now checks if the next command is available. If so, this is dispatched, without going through the keys of the dictionary (which was creating garbage to clean up). + +Version 4.1.2.10 (11. March 2019 - rev5023) + Fixed: A timeout issue for WS / WSS transport protocols by sending a ping. + +Version 4.1.2.9 (20. February 2019 - rev5003) + Note: Release .Net and Unity SDK from our page. + +Version 4.1.2.9 (13. February 2019 - rev4985) + Removed: Setting of Thread.Name for internally used threads. This was used for debugging only (since a while) but caused issues on a console platform (due to an issue in Unity IL2CPP exports). + +Version 4.1.2.8 (31. January 2019 - rev4975) + Fixed: The cap for the lastRoundtripTime. When the current roundTripTime was very low this meant the rtt wasn't going up again. This was a problem with local servers. + Fixed: Setting the roundTripTime when the connect-command gets acknowledged. This sets a minimum of 15ms now. Before, a local server could get a 0ms roundTripTime (due to using Environment.TickCount). + +Version 4.1.2.7 (25. January 2019 - rev4954) + Removed: CommandBufferSize and LimitOfUnreliableCommands. Technically, those are "Obsolete" but no longer used anywhere. See comments. + Fixed: MessageBufferPoolTrim() was not using a lock(), so this wasn't thread safe. + + +Older Versions + +*** Version 4.1.2.6 (12. December 2018 - rev4935) + Changed: Unsequenced delivery is now available in all builds. Internally, this was wrapped in compile conditions. + +*** Version 4.1.2.5 (10. December 2018 - rev4911) + Added: PhotonPeer.SentReliableCommandsCount - reliable commands sent but not yet acknowledged. This can be used as indicator for the quality of service, aslong with ResentReliableCommands. + Note: This build include the "Best Region Ping" classes, to minimize changes when released with Quantum. + +*** Version 4.1.2.4 (23. October 2018 - rev4868) + Fixed: Fragment bug introduced in 4557 (12. March 2018). This kept one command per reassembled message in the incoming queue (without payload). + Added: An upper limit for the calculated lastRoundtripTime (per ack) to prevent uncontrolled rtt changes due to garbled incoming messages. Protecting the rtt timer improves resending commands if needed. INFO level logging starts with: "LastRoundtripTime is suspicious". + Added: SocketTcpAsync class, which is receiving with BeginReceive and EndReceive in callback. This should work for Unity's 4.x Runtime, which has a bug that blocks Socket.Receive() indefinitely. + Changed: TPeer.ReceiveIncomingCommands() will now use dataLength as indicator how much was received. + Internal: CalculateCrc() now uses a cached lookup table, which gives it much better performance (most obviously for longer datagrams). + +*** Version 4.1.2.3 (17. October 2018 - rev4847) + Fixed: Fragment handling. When a duplicate fragment arrived after the "original" was reassembled, this led to an exception and disconnect. + +*** Version 4.1.2.2 (16. October 2018 - rev4843) + Changed: Enum StatusCode values. DisconnectByServer is obsolete and replaced with DisconnectByServerTimeout. + Added: StatusCode.DisconnectByServerReasonUnknown, as a generic case (unknown reason). + Added: You can now send List, which gets de/serialized as object[]. + Internal: PeerBase.MessageBufferPool is now a Queue, which means the usage changed minimally. This is faster than before. + Internal: When a client detects a timeout disconnect, it will enqueue the status-change, to allow the Disconnect()-call to finish first. This provides a clearer order of actions happening internally for a timeout. + Internal: Simplified the ExecuteCommand for a received Disconnect command. The StatusCallback is now enqueued, as Disconnect() also enqueues changes. + Updated: SocketUdpAsync class is now sending in blocking mode but receiving with BeginReceive and EndReceive in callback. This should work for Unity's 4.x Runtime, which has issues with blocking socket receive. + +*** Version 4.1.2.1 (31. July 2018 - rev4787) + Fixed: OutOfMemory exception while building 'Master' version on UWP with .Net Native Toolchain. This is a workaround for an alleged bug in ilc.exe. + Added: EventData.Sender to simplify access to this info. If there is no sender or if the server sent the event, the actorNumber is 0. +LoadBalancing: + Changed: OnStateChangeAction is now named StateChanged and provides a "previous state" value. State changes only trigger the event-call when the value actually changes. + Renamed: OnEventAction to EventReceived and OnOpResponseAction to OpResponseReceived. + Added: LoadBalancingClient now has AddCallbackTarget and RemoveCallbackTarget to simplify registering for various callbacks. +Chat: + Changed: Namespace from "ExitGames.Client.Photon.Chat" to "Photon.Chat". + Added: ConnectAndSetStatus method. + +*** Version 4.1.2.0 (3. May 2018 - rev4660) + Added: Unsequenced delivery for messages (reliable and unreliable). This way, operations and resulting responses and events can be sent as unsequenced, which is good for use cases like streaming voice, input and such. + Internal: The Photon library now re-uses memory for messages and commands. This avoids garbage collection in Unity, which can be a performance problem. + Changed: There is a new method to send operations: PhotonPeer.SendOperation. It uses the SendOptions struct to define the delivery mode, encryption, etc. This replaces the obsolete OpCustom(). + Added: Optionally, the Serialization Protocol v1.8 can now be used. Consider it a pre-release/test version for which we could use feedback. Set PhotonPeer.SerializationProtocolType. + Internal: Replaced foreach with while in SerializeParameterTable, SerializeHashTable and SerializeDictionaryElements in SerializationProtocol v1.6. + Internal: Changed EnetPeer to have a CommandQueue. Instead of queueing ExecuteCommand(cmd) delegates, we now queue the commands. This is leaner. + Internal: The SendOptions.DeliveryMode enum's order changed. + Added: PeerBase.MessageBufferPool, MessageBufferPoolGet() and MessageBufferPoolPut(StreamBuffer buff) as a simple memory pool implementation. + Added: PhotonPeer.MessageBufferPoolTrim() to give an option to limit the amount of memory buffered. + Added: PhotonPeer.MessageBufferPoolGet() as a (brute force) way to externally debug the memory buffer. This is temporary. Don't rely on this. + Added: For TCP, a client-side timeout. This is based on checking timestampOfLastReceive in the DispatchIncomingCommands method (timeouts only get called when you dispatch). + Fixed: The TCP implementation only checks for a TimeoutDisconnect, while in ConnectionStateValue.Connected. Otherwise, no timeout can happen. + Internal: Renamed various values for more fitting names. + Internal: Added special handling of Arrays of int and byte to DeserializeArray() in Protocol 1.6, using DeserializeByteArray and DeserializeIntArray respectively. +LoadBalancing: + Changed: The namespace to the simpler "Photon.Realtime". + Added: Various callbacks to signal specific situations / events. To get those, a class must implement the interface and be added to a list of "Targets". See: ILoadBalancingCallbacks. + Added: RegionHandler, which provides methods to ping a list of regions and to find the one with best ping. This moves PUN's "Best Region" feature to the LoadBalancing API. + Moved: The PhotonPing was part of the dll but is now part of LoadBalancing. + Added: LoadBalancingClient.UseAlternativeUdpPorts. This way, UDP may use ports of the Steam Multiplayer port-range by simply replacing existing port strings in addresses. + Changed: RaiseEvent now has an overload, that uses RaiseEventOptions and SendOptions. The old variant is obsolete but will still work. + Changed: CheckUserOnJoin is now set by default. The RoomOptions.CheckUserOnJoin got removed. + Added: Client-side checks and limits for OpFindFriends. + Added: Optional parameter sendAuthCookie to OpLeaveRoom. The app can control what's passed from Photon to a server via WebHook. + Changes: The room list for lobbies is no longer part of the LoadBalancingClient. Instead, implement the callback for the changed room list. + Added: AppSettings, a base class to host AppId and some settings for a title. This will help make it available across products. Right now, the LoadBalancingClient is not using it yet. + Changed: Player.ID is now .ActorNumber, which mirrors the server's naming. + Fixed: Unity compile defines to support Unity 2018, + +*** Version 4.1.1.19 (9. April 2018 - rev4590) + Fixed: WSS now sends an operation "ping" in SendAcksOnly() (and SendOutgoingCommands()) to avoid getting timed out. This happens right away, while other commands/operations stay in queue. + +*** Version 4.1.1.18 (19. December 2017 - rev4540) + Fixed: Exception in QueueOutgoingAcknowledgement() with a lock(). This avoids threading issues between Send- and Receive-thread. This was an issue mostly seen in Photon Voice, which uses the library multi-threaded. + Changed: Network Simulation now only stores the actual data of messages, instead of storing an action/delegate. + Note: The delegate IL code for Network Simulation was instantiating a "display class" instance and caused GC garbage, even if no simulation was running. +LoadBalancing: + Changed: FriendInfo.Name is now "UserId", which is up to date with it's usage. + Changed: CheckUserOnJoin is now set by default. The RoomOptions.CheckUserOnJoin got removed. + +*** Version 4.1.1.17 (11. October 2017 - rev4465) + Fixed: Fetching the server timestamp now works for the "No C# Sockets"-library build for WebSockets. This affected libraries used for XB1 and as a result, the serve time was not synced. E.g. PhotonNetwork.time was locked at 0. + Changed: XML doc of PhotonPeer.DisconnectTimeout. This is no longer a UDP exclusive setting and clearly states that the unit is milliseconds. + Updated: Several documentation comments for the PhotonPeer. Using shorter summaries and remarks for the details. +LoadBalancing: + Changed: OperationCode const byte Join = 255 is now marked obsolete. We use "JoinGame" instead. + Added: DisconnectCause.AuthenticationTicketExpired. + Fixed: DebugReturn call in Unity WebGL. +Chat: + Fixed: Unity "6" compile define is now UNITY_2017. + +*** Version 4.1.1.16 (1. September 2017 - rev4349) + Fixed: The native implementations for "PhotonPing", which are used for PUN's "Best Region" selection in the "No Sockets" dlls (Android and iOS for Unity 4.x). Disposing the "dynamic" version was causing an exception (due to an attempted fix of a less-obvious memory leak). This caused the Best Region selection to stall. + +*** Version 4.1.1.15 (17. July 2017 - rev4232) +WebSocket: + Changed: WebSockets are now treated like "addons", as their implementations are complex and potentially not running on some platforms (be it UWP or some Unity export). The Readme-Photon-WebSocket.txt tells you how to add them to your project and set them up. + Added: Folder "PhotonWebSocket" with SocketWebTcpCoroutine and SocketWebTcpThread classes, to support platforms with and without Thread API. + Added: PingHttp.cs for Unity WebGL exports, to support "Best Region" selection in PUN. +LoadBalancing: + Added: LoadBalancingClient.TransportProtocol as shortcut to the use PhotonPeer's TransportProtocol value. This enables setting the protocol easily while not connected. + Added: LoadBalancingClient.SocketImplementationConfig as shortcut to modify PhotonPeer's SocketImplementationConfig. This enables you to setup which IPhotonSocket implementation to use for which network protocol. + Changed: LoadBalancingPeer.ConfigUnitySockets() to try to find our websocket implementations in the assembly, making the SocketWebTcpCoroutine and SocketWebTcpThread classes optional. + Removed: Class "SocketWebTcp" is no longer found by ConfigUnitySockets(). +Chat: + Added: ChatClient.TransportProtocol as shortcut to the use PhotonPeer's TransportProtocol value. This enables setting the protocol easily while not connected. + Added: ChatClient.SocketImplementationConfig as shortcut to modify PhotonPeer's SocketImplementationConfig. This enables you to setup which IPhotonSocket implementation to use for which network protocol. + Changed: ChatPeer.ConfigUnitySockets() to try to find our websocket implementations in the assembly, making the SocketWebTcpCoroutine and SocketWebTcpThread classes optional. + Removed: Class "SocketWebTcp" is no longer found by ConfigUnitySockets(). + +*** Version 4.1.1.14 (5. July 2017 - rev4191) + Changed: SupportClass StartBackgroundCalls method now assigns an ID to each thread/task and enables you to cancel it explicitly via StopBackgroundCalls. CallInBackground is now obsolete due to renaming. +LoadBalancing + Changed: The ClientState "Uninitialized" is now "PeerCreated". This is the initial state. ConnectedToMaster is now ConnectedToMasterserver (both use the same value). + Updated: ClientState values descriptions. + Internal: GameEnteredOnGameServer() first sets the local player's actorNumber, then updates the player lists. +Chat: + Added: ChatClient can optionally run a thread to call SendOutgoingCommands in intervals. This makes sure the connection doesn't fail easily (e.g. when Unity is loading scenes, etc.). You still have to call Service to dispatch received messages. + Added: ChatClient.UseBackgroundWorkerForSending. Set this to true, to use the new background thread. Note: Do not use this in WebGL exports from Unity cause Threads are unavailable in them. +WebSocket: + Changed: Updated enabled protocols for WebSocket.cs. Now it is posible to connect to a server which supports only either TLS 1.1 or TLS 1.2 or TLS 1.0 or any combination of them. + +*** Version 4.1.1.13 (2. June 2017 - rev4112) + Internal: Fixed StreamBuffer.Seek() which was throwing an exception when seeking position 0 in an empty stream. + Removed: The queue-length checks which were using OnStatusChanged to warn about the amount of data queued for sending. Several StatusCode values are now obsolete accordingly. + Removed: StatusCode InternalReceiveException = 1039 and TcpRouter* which were obsolete for a longer time. + +*** Version 4.1.1.12 (24. May 2017 - rev4086) + Fixed: Peer.timeLastSendOutgoing was set in SendAcksOnly and ReceiveIncomingCommands. This should not happen. In PUN, this led to an issue with the fallback thread, which could not keep the connection. + Fixed: DNS resolution in the UDP socket is no longer inside a lock. Even if it takes longer (without network), the socket can now be closed before it's even opened properly, avoiding a freeze. + Fixed: UWP clients can set a SocketImplementationConfig. This opens up the use of WebSockets or any IPhotonSocket implementation in UWP, too. + Internal: Acknowledgements are no longer created as "commands" and serialized on send. Instead, they are directly written to a byte[] buffer, which is using less memory. + Added: PhotonPeer.EnableServerTracing to enable UDP Datagram Tracing on server side. We might ask you to use this when debugging connection issues. Otherwise, don't use this, as you can't access the server's logs anyways. + +*** Version 4.1.1.11 (13. April 2017 - rev3922) + Fixed: Bug of v4.1.1.10, which caused disconnects after a short time. + +*** Version 4.1.1.10 (11. April 2017 - rev3916) + Internal: Memory Allocation for nCommand and EnetPeer within SendoutGoingCommand, and AreReliableCommandInTransit + Internal: Refactoring of SerializeToBuffer to prevent memory allocation and access udpBuffer directly instead of using BlockCopy. + Internal: EnetPeer.channels got removed and replaced by a simple array and a GetChannel() method to access all channels. + +*** Version 4.1.1.9 (15. March 2017 - rev3884) + Fixed: Size calculation for Datagram Encryption (used on one console only, so far), when the MTU was changed from default. In some cases, an Exception was thrown: ArgumentException: Offset and length were out of bounds [...] at System.Buffer.BlockCopy. Adjusted GetFragmentLength() and CalculateBufferLen() internally. + +*** Version 4.1.1.8 (24. February 2017 - rev3873) + Fixed: Handling of non-fatal "WouldBlock" SocketExceptions when receiving data via TCP. This led to disconnects before. This affects only TCP connections in libs other than Windows Store. + Changed: Memory usage for TCP socket usage. Less memory is being allocated per receive. As we have to queue incoming data, it still has to get copied once. + Changed: Memory usage for encrypting messages. + Changed: SupportClass.DictionaryToString() now logs the length of a byte-array (not the content). + Changed: Deserializing unknown "Custom Types" returns the sent byte[]. There is no warning/error log yet but deserialization won't fail entirely. + Changed: PeerBase.SerializeMessageToMessage() to use less memory for encryption. Also, Raw-Messages can now be de/encrypted successfully. + Internal: Added StreamBuffer.ToArrayFromPos(), enabling you to get a buffer copy, starting with some offset (position of the buffer). + Internal: Removed some NETFX special build cases (obsolete due to using our own SteamBuffer). +LoadBalancing: + Added: Player.UserId field and code to read published UserIds from the player properties in CacheProperties(). When publishing the UserId in a room (RoomOptions.PublishUserId = true), the UserId becomes available for all players in a room. Good to find/make friends or follow a team player into another room. + Added: New matchmaking operation: OpGetGameList(typedLobby, sqlFilter). This fetches a list of rooms that match the filter. You can show lists of rooms with specific properties, if needed (or still use OpJoinRandom). + Fixed: WebFlags properties setters. + +*** Version 4.1.1.7 (16. December 2016) + Note: No new lib version. Just updated demos for the SDK. +Demos: + Fixed: Demos with persistent (Turnbased) games. The Memory Demo was not setting up rooms correctly (which led to errors joining them) and used a "join" rather than a "rejoin" to get into saved games (getting added to the room once more). + +*** Version 4.1.1.6 (9. December 2016 - rev3801) + Changed: Default SentCountAllowance = 7. +Loadbalancing: + Added: OpJoinRandom will now "remember" to send ExpectedUsers to the Game Server (by caching the value). + Added: AuthEvent and it's handling. This (internally sent) event can now update the client's token anytime (before that expires). + Added: LoadBalancingClient.OpChangeGroups(). + Changed: LoadBalancingClient.Disconnect() no longer sets it's own State to Disconnected. It waits till the state-change callback gets called by the lib. + +*** Version 4.1.1.5 (18. November 2016 - rev3775) +Internal: + Fixed: Photon-init request is now created by the factory method CreateAndEnqueueCommand to make sure we fragment the init if needed (for small MTU and more init-data). + Fixed: Bug in TrafficStatsReset method, which caused the averaged stats to go up infinitely after a reset. + +*** Version 4.1.1.4 (21. October 2016 - rev3737) + Internal: Added ArraySegment support for byte[]. This way, we can internally reuse more memory. + Changed: Implementations of PeerBase Disconnect() are now using EnqueueStatusCallback(StatusCode.Disconnect) to delay the "callback". That enables any thread to call Disconnect() while the status change correctly gets called in the main thread via DispatchIncomingCommands(). + Changed: When a SocketImplementationConfig for UDP is set, this will be used via Activator.CreateInstance(socketImplementation, this). + +*** Version 4.1.1.3 (20. September 2016 - rev3673) + Changed: NETFX_CORE implementation for UDP. This no longer attempts to detach the stream after every single Send, which caused issues when connecting and getting a lot of messages. + +*** Version 4.1.1.2 (13. September 2016 - rev3652) + Changed: There are less variants of the Photon library now, which makes it easier to integrate, run and test. There is a general "DotNet" version and a Windows Store (universal) dll. These two also come as Unity build and in Debug and Release. UWP can use the Universal/WSA library. + Added: PhotonPeer.SocketImplementationConfig. This allows easier configuration of the IPhotonSocket type to use per protocol, so that switching protocols is easier (compared to having to set a SocketImplementation before connecting). + Changed: PhotonPeer.SocketImplementation can't be set public. Use the SocketImplementationConfig instead. + Internal: This release summarizes a lot of internal refactorings. It's easy now to switch protocols (internally), to set socket implementations (platform dependent) if needed, etc. +LoadBalancing: + Removed: LoadBalancingClient.PlayerName and Player.Name. Were obsolete for more than a year. There is a NickName and the UserId can be set in the AuthValues. + Removed: OpJoinRoom() overload with actorNumber. This was obsolete. To enable clients to return to a room, set AuthValues and a userID. + Changed: LoadBalancingClient no longer overrides the protocol for Unity WebGL. This is done in the LoadBalancingPeer.ConfigUnitySockets(). + Changed: GetNameServerAddress() is the same in Chat and LoadBalancing APIs now. + Added: DisconnectCause.DisconnectByServerLogic and handling for this case. You can check this DisconnectedCause when the LoadBalancingClient.State is ClientState.Disconnected. + Added: Hashtable definition to use Photon's own implementation for Windows Store builds (NETFX_CORE). This must be used but it means you to use the same Hashtable definition in all builds (no matter if 8.1 or 10). + Added: Support for WebGL export in Unity. + Changed: OnStateChangeAction, OnEventAction and OnOpResponseAction are now events. To register a method in one of those, use += and to deregister you need to use -=. This prevents assigning a new method and de-registering any previously registered ones. +Chat: + Changed: ChatPeer now has ConfigUnitySockets(), which defines the SocketImplementationConfig. It's only used in Unity (using UNITY define). + Changed: ChatClient is not setting socket implementations anymore. + Added: Hashtable definition to use Photon's own implementation for Windows Store builds (NETFX_CORE). This must be used but it means you to use the same Hashtable definition in all builds (no matter if 8.1 or 10). + Added: Support for WebGL export in Unity. + +*** Version 4.1.1.1 (22. August 2016 - rev3549) + Fixed: IPv6 support. The init-message was missing it in 4.1.1.0. + Fixed: UWP build for Unity now has (Photon-)Hashtable class once more. Unlike Windows RT, UWP does support Hashtable (again). But in Unity, we always use ours. + +*** Version 4.1.1.0 (15. August 2016 - rev3536) + Changed: Unity exports now need to set the API Compatibility to ".Net 2.0". The ".Net 2.0 Subset" won't work anymore, due to need of some features for a new encryption mode. + Fixed: Windows Store implementation of TCP Socket. This is now compatible with 8.1 and 10 and the locally configured timeout is also applied while connecting initially. + Fixed: OpWebRPC documentation. +LoadBalancing: + Fixed: Room.ClearExpectedUsers() is now sending it's current, local "expected users" to update the server with "CAS" (Check and Swap). This gives the client an update when the values become valid (which updates the local cache after the roundtrip). + Added: Support for the 'Server Side Master Client' feature. The Room will read master client updates from the server accordingly. Room.SetMasterClient() enables you to override the server's selection (provided it did not change before your operation gets executed). + Changed: Option for bool WebForward into the new "WebFlags". This allows fine control of which data is being sent to WebHooks. This affects all SetProperties, OpWebRPC and the RaiseEventOptions. + Added: WebRPC.cs to the LoadBalancing API folder (was available separately before). It contains WebFlags and WebRpcResponse. +Internal: + Changed: Instead of Windows Phone 8.0 support, we now have a Windows 8.1 Universal library ("Metro") and one for Windows 10 Universal ("UWP"). + Changed: Changed initialization of PhotonPeer and related classes. + Changed: Workflow to send Init command. + Added: Option for "Datagram Encryption" and a new Authentication Workflow ("AuthOnce" and "AuthOnceWss"). This is part of the LoadBalancing API. + Added: ClientSdkId, which is used internally for reference. + +*** Version 4.1.0.6 (30. June 2016 - rev3400) + Changed: ExchangeKeysForEncryption() and internally called DeriveSharedKey() can now be executed in their own Thread. + Added: static PhotonPeer.AsyncKeyExchange to define if encryption calculations are done in parallel. +Internal: + Changed: NetworkSimulationSet.IsSimulationEnabled only does any work, if the value gets changed (saving some surplus work). + +*** Version 4.1.0.6 (21. June 2016 - rev3376) +Internal: + Removed: The wrapper for the optional "native encryption library" from most assemblies. It didn't get certified for the Windows Store and caused problems in the Unity WebPlayer. This will be provided on demand instead. + Removed: Our upcoming protocol implementation until it's fully compatible with all supported platforms. Despite not being used, it also caused some issues on some Unity exports. + Changed: Usage of MemoryStream is being replaced with a StreamBuffer. This is our own implementation and always grants access to the underlying byte[] (which is not possible in Windows Phone / Store API in some cases). For your Custom Type serialization, replace MemoryStream with StreamBuffer. That's all. + Internal: Commands are now split into header and payload byte-arrays, instead of copying them into yet another buffer before sending them. + Added: Support for IPv6 adresses in Photon Ping classes. This supports "Best Region" usage in PUN. + Fixed: After DNS resolution, IPv6 adresses are preferred over IPv4 ones. +LoadBalancing: + Fixed: LoadBalancingPeer.OpRaiseEvent(...) to send operations (and events) unencrypted again. + +*** Version 4.1.0.4 (19. May 2016 - rev3322) +Internal: + Updated: For Unity, the usage of the optional "native sockets" library is now compatible with IPv6 addresses, as required by Apple. + +*** Version 4.1.0.3 (28. April 2016) +Internal: + Added: An optional native library for encryption. This speeds up the (rarely used) key-exchange and encryption of messages. The usual case is to use the C# variant, as before. + +*** Version 4.1.0.2 (21. April 2016 - rev3283) +Internal: + Changed: PeerBase: ipv6 flag set in INIT_BYTES[5] after dns lookup, when ip address type already known + Changed: PeerBase: INIT_BYTES[4] contains clientlibid and release flag (15) + Changed: PeerBase: client version packed in INIT_BYTES[5,7,6] bytes + Changed: pProtocol prefix and path parsed (and trimmed) in IPhotonSocket.TryParseAddress to support websocket urls + Changed: Client version moved to separate version.cs + Changed: Protocol static methods reworked to instance methods, IProtocol interface extracted + Changed: PeerBase.DeserializeMessageAndCallback() to use a variant of new MemoryStream that exists in Windows 10 Universal APIs, too. +LoadBalancing: + Added: Expected Users. This affects the Room, LoadBalancingClient, JoinRoom, JoinOrCreateRoom and CreateRoom. + Added: null check in Extensions.StripToStringKeys(). + Fixed: FriendInfo.IsInRoom, which returned the opposite of it's naming! Also changed FriendInfo ToString() according to PUN's. + Added: RoomInfo expectedUsersField, which is updated with room properties (well known ones). + Added: Room.ExpectedUsers and ClearExpectedUsers() to expose the list of expected players. + Added: RoomInfo.serverSideMasterClient and masterClientIdField (also updated with well known properties). + Changed: OpRaiseEvent now re-uses a Dictionary in the LoadBalancingPeer. It uses Clear(), rather than creating a new Dict each time. + Changed: AuthenticationValues to also use C# properties and and backup-fields. This is guaranteed to work in Unity. + Updated: EventCode ErrorInfo reference with a link to "WebHooks" doc online. + Changed: Disconnect handling in the LoadBalancingClient. The client should reset correctly and log info, if it's in a State where a disconnect is a proper error. Note: In some cases like "switching server", a disconnect is expected, so it's not an error then. + Fixed: PlayerProperties sent to game server will now include well-known properties again. This fixes the "NickName missing" bug. + Fixed: LoadBalancingClient.State value when the client fails to join or create a game on the Master Server. The state is correctly re-set to ClientState.JoinedLobby or ClientState.ConnectedToMaster. + Internal: Added private inLobby value, to store if the client was/is in a lobby on the Master Server. + Fixed: DemoClient (in demo-loadbalancing) now makes use of the Name Server by using: ConnectToRegionMaster("eu"). + Added: DemoClient now has debug output when the connection times out or can't be established. + + +*** Version 4.0.5.1 (18. January 2016 - rev3187) + Fixed: EnetPeer.ExecuteCommand(). Fixed: Receiving a Disconnect-command didn't clear the receive-buffers. However, it has to be the last command executed. + Note: The bug related to the Disconnect-command happened, when an app paused, queued incoming commands and executed the Disconnect while incoming commands were queued. + Fixed: Setting of DisconnectTimeout for TCP connections (ReceiveTimeout and SendTimeout). + Changed: Our protocol serializes Enums implicitly as their underlying Type. This means you can easily send them but will lose the Type info (they don't arrive as your Enum type). This is now also working in Windows Store libraries (NETFX_CORE). +LoadBalancing: + Added: OpSetCustomPropertiesOfActor() and OpSetCustomPropertiesOfRoom() now check locally, if the client is currently in a room. It must be, to be able to set these properties. An exception exists for setting properties for the local player's actorNumber, but those are better set via LoadBalancingClient.LocalPlayer. +Unity SDK: + Changed: The Unity condition which defines "using Hashtable = ExitGames.Client.Photon.Hashtable;". All versions of Unity 4 and up now define that Photon's Hashtable is needed. This is only in the LoadBalancing API, not in the demos. + Added: WebGL support + +*** Version 4.0.5.0 (3. December 2015 - rev3144) + Changed: Signature of SetCustomProperties methods. All overloads now include a final, optional "webForward" parameter. This enables you to update a WebHook when properties change. This is intended for turnbased games, not for high-frequency updates - use with care. + Internal: Added more debug output to error messages from the socket usage. This should now always include the ServerAddress to make things easier to debug server-side, if needed. + Added: Serveral new ErrorCode values, which will be used by v4RC5 and newer servers. See ErrorCode.JoinFailed***, HttpLimitReached and ExternalHttpCallFailed. + Fixed: LoadBalancing API now reads the correct "NickName" key from the server's authentication response. So far, it was reading a key that is never used. Note: This means you can set a user's NickName server-side to override the client's nickname. +Chat + Added: A MessageLimit field for ChatClient and ChatChannel to limit the number of messages the client keeps locally. It might be useful to limit memory usage in long running chats. Set ChatClient.MessageLimit to apply the limit to any channel subscribed afterwards or apply a limit individually. + +*** Version 4.0.0.12 (3. November 2015 - rev3112) + Added: Support for IPv6. Note: IPv6 does not work in Unity yet. It has issues with IPv6. (Case 740910) + Note: Host name resolution will prefer IPv4 over IPv6, if both IPs should be available. IPv6 Addresses must use brackets! Example: [::1]:5055. This separates the port from the address. + Added: Error logging when Addresses can not be resolved to IPs. + Changed: LoadBalancingClient OpJoinOrCreateRoom() no longer allows you to re-join a room. Simply remove the ActorNumber from the parameters. To re-join, use OpJoin with actorNumber (Player.ID that was assigned in the room). + Added: Support for PS4 in Unity LoadBalancing SDK. Note: The demos are not all updated with controller support, as we use the old UI, still. To test export, use the Particle Demo. + +*** Version 4.0.0.11 (28. October 2015 - rev3093) + Changed: Sending a generic Dictionary (with specific types) will now throw an Exception, if any key or value is null. This limitation does not include Dictionaries which use object as type. Those Exceptions are one of the few, which are not catched and turned into a debug message. Catch them by wrapping Operation calls, where needed (OpRaiseEvent()). + Changed: TrafficStatsGameLevel public properties are now settable. This enables you to reset individual values to (e.g.) show "LongestDeltaBetweenSending of the past second". + Added: CommandLog debugging option. This can be used to get a list of sent reliable commands and their ACKs (from the server). Default is 0 size ("off"). + Added: CommandLogSize and CommandLogToString() to PhotonPeer. This is part of a LoadBalancingClient.loadBalancingPeer. + Added: Several PhotonPeer values to analyze connections: ConnectionTime, LastSendAckTime and LastSendOutgoingTime. PacketLossByChallenge is probably a tempoary addition to check if we have to drop corrupted packages due to bad "challenge" value. + Added: Log for incoming reliable commands. The most recent 200 entries will be logged with the CommandLogToString(). This is probably temporary. + Changed: Timing for resending reliable commands in RUDP. The peer will check the sent-queue more frequently now, no matter at what time some random command would have to be repeated. Repeats should be more timely, based on their dynamic resend-timing. + Changed: PhotonPeer.MaximumTransferUnit minimum is now 576 (was 520, which was lower than on the server). + Internal: Channels in the EnetPeer are now stored in an array, so we can replace some foreach-loops with for-loops. +LoadBalancing (Realtime and Turnbased API) + Added: LeaveLobby handling in OnOperationResponse(), which sets the client's state correctly. + Changed: Order of execution for Ev Join. If user is known (inactive user rejoins), the player's props are read. The actor list is used, if available. + Changed: RoomOptions to use properties with backup-fields to avoid issues in Unity which has issues with Object Initializer (curly brackets). + Changed: JoinMode 2 is now "JoinOrRejoin". Was: "Rejoin". + Added: ErrorCode constant AuthenticationTicketExpired. + Internal: OpJoinRoom, OpCreateRoom and OpJoinRandomRoom no longer use a (growing) list of properties. Instead, classes were created to "sum up" their parameters. The api for games didn't change. + Internal: Related to the refactoring of Join/Create, the LoadBalancingClient now creates a Room instance when the client arrived on the GameServer (before, it got created in the initial "create" call). +Chat + Added: More sanity checks on operations (empty userId or secret, max friends). + Added: Special debug logging when the server returns an error for "Operation Unknown". In this case, it's highly likely that you don't use a Chat AppId. + Added: More helpful error logging. + +*** Version 4.0.0.10 (14. July 2015 - rev2988) + Removed: LitePeer class and complete "Lite" namespace. It's highly recommended to use the LoadBalancing API (LoadBalancingClient, etc). The (few) operations that were defined in Lite are no longer required really. + Refactored: Some "base" enumerations that were provided by the Lite peer. They are now in LoadBalancingPeer. + Added: support for RoomOptions.Plugins. Which we need now since we support multiple plugins per plugin dll - for testing purposes for instance. + Fixed: The wrapper classes for the native sockets now do a Sleep(15) when there's nothing to receive. This reduces CPU load considerably. + Fixed: Unity library SocketWebTcp class for websocket support. It requires a coroutine on a new GameObject which is now marked as DontDestroyOnLoad(go) and survives scene loading. + Fixed: The Windows 8 SDKs now include the release assemblies. This makes sure you can submit your app to the Windows Store. + Added: ConnectionProtocol WebSocket and WebSocketSecure. It's simply a different protocol, compared to UDP and TCP, so it should be separated. + Internal: DoFraming is now a part of TPeer (was in IPhotonSocket). It's set by the ConnectionProtocol which avoids misconfiguration. + Changed: SendPing can now send a ping binary message or enqueue the Ping Operation (when DoFraming is false). + Added: A null-check for TrafficStatsStopwatch to avoid NullReferenceExceptions. + Added: Compile condition for Ping result handling. It's only used when the client uses Sockets as well (for the time being). + Added: Unity WebGL export also sets a "using" Hashtable definition. + Fixed: An exception in Photon.Hashtable.ToString() if a value was null. The assumption was that there are no null-values. + Changed: SocketUdp and SocketTcp now implement IDisposable, which seems to help with infrequent freezes in the Unity Editor. + Added: PhotonPeer.QuickResendAttempts. Sets how many resend attempts for a reliable command will be done in quick succession (after RTT+4*Variance), before the time between repeats will be increased. Use with care and with low values. + Added: IP/Hostname to logged exceptions when Connect fails. This is easier to support (e.g. DNS lookup fails). + Fixed: Library for PUN+ export to WebGL. Originally, we forced UDP as protocol for PUN+ export, as the native sockets library doesn't support TCP. However, WebGL export introduced a new use case. + Added: LoadBalancingClient.EnableLobbyStatistics and .LobbyStatistics. They provide an overview which lobbies your game uses and how busy they are. + Fixed: The LB Demo should set CustomProperties instead of directly setting (any) properties. + Fixed: SocketWebTcp is completely empty, unless WEBSOCKET is defined. Before the file still contained the "using" part of the class. +LoadBalancing (Realtime and Turnbased API) + Updated: Description for IsConnectedAndReady. + Changed: NameServerAddress to return a fitting address depending on protocol (including WebSocket but not yet RHTTP). + Updated: The only name server host is now "ns.exitgames.com", which gets turned into a proper address by protocol. + Changed: LoadBalancingClient.CustomAuthenticationValues is now .AuthValues. You can use those values to identify a user, even if you don't setup an external, custom authentication service. + Changed: LoadBalancingClient.UserId no longer directly stores the id but puts it into AuthValues. This means, the UserId could also be set via setting AuthValues. + Changed: The API of AuthenticationValues. There is now the UserId and AddAuthParameter() replaces the less general SetAuthParameters() (which only set specific key/values). + Changed: PlayerName gets renamed to NickName, so PhotonPlayer.Name becomes .NickName and LoadBalancingClient.Name becomes .NickName, too. The old naming is marked as obsolete. + Changed: Particle Demo now connects to the Cloud by default (because it's easier to setup and try). You can define your own Master Server (Photon OnPremise) of course. + Added: GamePropertyKey.MasterClientId (248) and ParameterCode.MasterClientId (203) + Added: ParameterCode.ExpectedValues (231) + Added: ParameterCode.SuppressRoomEvents (237) +Chat API: + Added: A Unity 4.6 demo with uGUI. It's missing a few features but should give you a good start to making your own. + Added: Unity/WebGL support (merged from PUN). + Added: Breaking! IChatClientListener.DebugReturn(). Photon lib and chat client log via this method (no logging to console by default). + Changed: ChatClient.CustomAuthenticationValues is now .AuthValues. You can use those values to identify a user, even if you don't setup an external, custom authentication service. + Changed: ChatClient.UserId no longer directly stores the id but puts it into AuthValues. This means, the UserId could also be set via setting AuthValues. + Changed: The API of AuthenticationValues. There is now the UserId and AddAuthParameter() replaces the less general SetAuthParameters() (which only set specific key/values). + Note: All users should have a UserId. You can set chatClient.UserId before you connect, or you can set the AuthenticationValues in Connect(..., authValues) to set a UserId. + Added: ChatChannel.ToStringMessages(), which gets all messages in a single string, line by line. The format is "Sender:Message". + Added: ChatClient.TryGetChannel() to find a channel only by name, no matter if public or private. +Photon Unity SDK + Changed: Organization of APIs and Assemblies in SDK. Now you can copy the content of folder "PhotonAssets" into your project's Assets folder and you have all APIs. + Added: PhotonAssets-U5 folder which includes only the Windows Universal DLL. + +*** Version 4.0.0.8 (14. January 2015 - rev2765) + Fixed: Serialization of custom types with nested Serialize-calls. In this case, re-using a specific memory stream breaks it. + +*** Version 4.0.0.7 (12. January 2015 - rev2763) + Fixed: Serialization of arrays of custom-types. +Chat API + Internal: Changed code for UserID from 7 to 225. The latter is used in LoadBalancing, too, so we want to re-use the code here. + +*** Version 4.0.0.6 (05. December 2014 - rev2758) + Added: ChatApi and LoadBalancingApi folders to Unity SDK. They are needed in any Photon project with Unity. When updating, copy and paste the files over existing ones and make sure to replace the assembly-files, too. + Changed: Protocol to save more memory or re-use it. The idea is to have less Garbage Collection (primarily for Unity/PUN and custom types). + Added: New CustomType de/serialization methods which provide the MemoryStream, instead of a byte[] COPY from the stream. + Changed: Now using one method to identify a Type. This was duplicated code before. + Changed: De/Serialization of some types. + Note: The drawback is now, that there are more places with: lock(). This is far from optimal but the alternative would be to make Protocol instances per thread. As most is static at the moment, this would not be an easy task. + Added: position check for DeserializeStreamFunction() call. Stream position must be "previous + customtype length". It gets corrected but at the moment no error is thrown. + Changed: DispatchIncomingCommands() no longer instantiates the commandsToRemove each call. This is reused and thus saves memory. + Changed: net_fx build will now check IsConstructedGenericType to detect if something is a dictionary +LoadBalancing + Added: LoadBalancingClient.OpJoinOrCreateRoom overload which has lobby as parameter. If a room gets created, this defines in which lobby it belongs. + Changed: LoadBalancingPeer: Added new error code PluginMismatch, documentation for Plugins parameter code. + +*** Version 4.0.0.5 (23. September 2014 - rev2738) + Updated: AddFriends and RemoveFriends doc. + Changed: Logging level for two cases. Dropping a package due to failed CRC-check is now logged for INFO. It's expected and certainly not an error. Dropping a package when the incoming challenge does not match is also not an ERROR. It is expected when you switch servers and packages arrive late. This is now debug level ALL. + +*** Version 4.0.0.4 (19. September 2014 - rev2736) + Fixed: Fragmentation when CRC checks are enabled. This kept clients from sending fragmented commands when the additional 4 bytes CRC were included later on. + Fixed: An issue in the ChatClient which was referring to a class from Photon Unity networking. This caused compile issues in the Unity Chat Demo. + Updated: Reference doc generation. + +*** Version 4.0.0.3 (15. September 2014 - rev2731) + Updated: Doc generation settings and style. + Note: This version has no code changes to rev2728 described below. That version is already released in the Unity Asset Store in PUN. + +*** Version 4.0.0.3 (11. September 2014 - rev2728) + Fixed: A simple "order of things" issue when detecting a timeout (due to resends). We first have to set "Zombie" state so that any Disconnect() call created a disconnect-command with reserved byte = 2 = "due to timeout". + Fixed: Chat to be compatible with native sockets of PUN+ (iOS and Android exports from Unity). + Fixed: Access to native sockets (in classes SocketUdpNativeDynamic and SocketUdpNativeStatic) is now using a lock(). The native methods are not thread safe but we need more than one socket for PUN+ and Chat (with native sockets, too). + Changed: Logging for the case "Ignoring received package due to wrong challenge". This got logged on log-level ERROR but maybe is better as WARNING only. Now this should log less often. + Internal: Updated to a newer native-sockets interface. + Internal: Updated to a newer native-sockets interface (affects PUN+ only). Cleaned up precompile defines and #if usage. + +*** Version 4.0.0.2 (01. August 2014 - rev2715) + Added: PhotonPing class and subclasses per platform. Allows clients to use regular UDP messages to ping our servers and find the best region. + Added: Native and Win8 support for PhotonPing. + Known Issue: Native ping has to be done "one by one" and without any other connection in Unity. It's not yet thread safe (but that is ok as we don't want to ping most of the time but only rarely and out of game). + Added: PhotonPing class/file to Win8 platforms. + Changed: The extern static methods for the native libs are now internal (instead of private). Pings are using them, too. + Changed: WebRpcResponse.ReturnCode comment to include fail code. + Changed: OpWebRpc doc is now much more complete and helpful. + Updated: Unity SDK Particle Demo (more) and LoadBalancing Demo (just a bit). + +*** Version 4.0.0.1 (17. June 2014 - rev2663) + Fixed: DotNet assembly no longer contains classes that try to include our Unity native socket libs. This was causing issues in some cases. + Added: PhotonPeer.CommandInfoCurrentDispatch. This property gives you the debug string of the currently dispatched command (events or responses). Only useful for UDP. +LoadBalancing: + Added: LoadBalancingClient.OpRaiseEvent(). Now that LoadBalancingClient USES a loadBalancingPeer (and doesn't extend it), things are much easier by offering this method, too! + Added: LoadBalancingClient.IsConnected and .IsConnectedAndReady to LB API. Going to be part of the API from now on. + Removed: Unused fields clientId and clientCount. + Changed: Field for internal use "lastJoinActorNumber" is now private as intended. + Changed: LoadBalancingClient.Disconnect is now setting it's own state to Disconnected if the connection got closed (as expected). +Chat: + Changed: How the server responds to Subscribe and Unsubscribe. Events will now contain success/failure of those. This allows us to send the answer after calling a WebHook if needed and we can even send it to multiple clients (which authenticated with the same userID). + Changed: Handling of subscription responsed. This is done to allow web services to subscribe a client remotely and to be able to prevent joining some channel that a user should not join (the channel of some guild or another team, e.g.). + Changed: Debug loggging. In Unity we can't use Debug.Assert, etc. So we have to log more cleanly. This works in Editor and several platforms (but not all). + Changed: Folder for Chat API. It now begins with "Photon" which provides some context no matter where you copy the files. Easier to find in Unity projects. + Changed: Operation FriendList and method SendFriendList renamed to AddFriends + Added: Operation RemoveFriends and corresponding method in ChatClient.cs + Added: Console Demo has new command 'fr' to remove friends + +*** Version 4.0.0.0 (23. May 2014 - rev2614) + Changed: This version contains a few features that are not compatible with the Photon Server SDK v3.x. Notable features that are not in the Server SDK are: NameServer, WebHooks and Turnbased API features. + Changed: This SDK is the first that contains all current APIs for Realtime, Turnbased and Chat. + Fixed: Release build of the Unity assembly now also excludes native-socket using code, fixing a Unity Free export issue. We only use the debug assembly in our demos though and suggest you do the same. +LoadBalancing: + Changed: LoadBalancingClient.FriendList creation/update is delayed until the server's response is available. This avoids cases where the friends are offline for the moment between requesting the update and getting it. Initially, it is null as before. + Added: some methods to Player to find next player, etc. Useful for turnbased games to find an opponent. + Added: LoadBalancingClient.UserId, which is the ID of a user(account). This is used in FindFriends and when you fetch account-related data (like save-games for Turnbased games). Set it before Connect*(). As fallback when empty during connect, the PlayerName is used instead. + Removed: LoadBalancingPeer.OpSetCustomPropertiesOfActor and OpSetPropertyOfRoom which were too special to be so low level. Could be implemented to LBClient. +Turnbased: + Fixed: OpJoinRandomRoom and OpCreateRoom which didn't reset the ActorNr to claim when entering the room. Depending on previous actions, some calls of those methods did fail when the actorNumber wasn't available. + Changed: OperationCode.Rpc is now called OperationCode.WebRpc. It's simply much cleaner (considering PUN has RPCs as well but in a different context). + Changed: WebRpcResponse reading to be able to handle additional data. + Added: Parameter webForward to: OpSetCustomPropertiesOfRoom and OpSetPropertiesOfRoom. The "old" overloads of these methods are still there, too. If webForward is true, the properties are sent to the WebHooks. +Chat: + Added: SendPrivateMessage() overload that has option to encrypt private messages. Public messages don't need encryption. + Removed: lastId and LastMessageIndex from channels. Those were impractical and should be removed from the API. + Changed: UserStatus class to ChatUserStatus. + Changed: Most classes are defined in their own file now. + Removed: Folders "Shared" and their subfolders. This gives a much better overview. + Added: Handling for event SubscribeResponse. This is not actually a response but gives you info about channels that got subscribed (e.g. when you re-login quickly or when a user is logged in in multiple clients). + Added: HandleSubscriptionResults() method to handle the event and proper responses. + +*** Version 3.2.2.6 (13. May - rev2575) + Fixed: Windows Store and Windows Phone libraries now only send the bytes they should send. This means we have to copy the payload from the "complete package buffer" in order to send it. + Fixed: SocketTcp now handles all exceptions during reading. Still, abort-by-server is treated as ServerDisconnect. Everything else as client side disconnect. This fix is especially for iOS exports from Unity. The iOS Power-button will immediately cut any connection. The Home-button allows us to keep the connection if we return the app to focus within a few seconds. + Fixed: TPeer.StopConnection() now clears the incoming queue when it disconnects. This avoids getting any more (already received) commands. + Changed: TPeer.Disconnect() now uses StopConnection instead of implementing the same code again. + +*** Version 3.2.2.5 (30. April - rev2566) +LoadBalancing: + Added: TypedLobby class to replace lobby name/type pair. + Added: LoadbalancingClient.CurrentLobby property. CurrentLobbyName and CurrentLobbyType are obsolete. + Added: New overloads in LoadbalancingClient with TypedLobby parameter instead of separate lobby name and type: OpJoinLobby, OpJoinRandomRoom. Old methods marked obsolete. + Added: New overloads in LoadbalancingClient for OpJoinOrCreateRoom, OpCreateRoom, CreateRoom with parameters packed in RoomOptions class. Old methods marked obsolete. + Breaking: LoadbalancingClient.CreateRoom parameters changed to (string roomName, RoomOptions opt). + Internal: Removed obsolete LoadBalancingPeer overloads of OpCreateRoom and OpJoinRoom + Internal: Added 'onGameServer' parameter to LoadBalancingPeer OpCreateRoom, OpJoinRoom; used to avoid sending extra data to master (player- and room-props) + Internal: Loadbalancing Room constructor(string roomName, RoomOptions opt). + Internal: Added use of the "JoinMode" parameter which is used in context of Turnbased games. + Fixed: Bug in OpLeaveLobby which joined the default lobby instead of leaving any lobby. +General: + Fixed: Server ports were read as short, which was wrong. We now use the correct unsigned short to convert from the address string). + Fixed: A minor issue in the SupportClass ToString conversion which used a Hashtable's key type instead of the value's type in one place. + +*** Version 3.2.2.4 (21. March 2014 - rev2519) + Internal: For Unity, the classes that handle native sockets can now be compiled in a variant that does not actually reference the native-socket-libs. The dll for PUN+ uses native sockets and need the native libs. Any regular dll will have the (not used and empty) classes for build-compatibility reasons. + Added: Values to enum EventCaching: SliceIncreaseIndex, SliceSetIndex, SlicePurgeIndex and SlicePurgeUpToIndex. They are in Lite but used in LoadBalancing. This is likely to be cleaned up in the next major release. + Changed: EventCaching MergeCache, ReplaceCache and RemoveCache as they belong to an outdated form of caching. The "RoomCache" is the better option in any case. +LoadBalancing: + Added: RaiseEventOptions class. It's used for OpRaiseEvent to avoid further parameter- and overload-clutter for this operation. While it's still not optimal for all cases, the fields in the RaiseEventOptions class are hopefully more clear how to use. Maybe some constructors will be added soon. + Changed: All OpRaiseEvent variants, except the one with RaiseEventOptions is now obsolete. + Added: Event Cache Slicing. Cached events can now be organized into "slices" which allows you to handle them in chunks. You can purge events in specific slices (e.g. get rid of the previous game-rounds events). +Turnbased: + Added: RaiseEventOptions.ForwardToWebhook which allows you to forward an event to a webhook (to be defined in Dashboard). Use this rarely, as it has an impact on (server) performance! + +*** Version 3.2.2.3 (18. February 2013 - rev2493) + Added: const PhotonPeer.NoSocket, so programs using our assemblies can detect if they must provide an external SocketImplementation. Some builds avoid using the Socket class (cause Unity Free doesn't support it on all platforms). + Added: PhotonPeer.SendMessage method in order to send any serializable object to server. + Added: IPhotonPeerListener.OnMessage in order to be notified about getting message from server. + Added: new 'Connect' method, which accepts as third parameter any serializable object. You may use this object on server before creating peer. + Added: OnMessage callback to samples + Changed: TCP and UDP both set the socket to null explicitly in Disconnect(). Hopefully this fixes a misbehaviour in Unity Editor which locked up often. + Changed: SocketTCP now has a syncer object and locks in Disconnect(), so only one Disconnect call can be made anytime. + Fixed: Nullreference when calling DispatchIncomingCommands() before Connect(). This was due to a (new) use of the socket wrapper. Commented this out until needed. + Fixed: Nullreference when calling SendAcksOnly() before Connect() with a new non-null check. + Fixed: breaking issue in Hashtable replacement class. Enumerators used in 2 "foreach" loops were breaking with a nullreference. Fix: No caching of enumerator. + Changed: AutoJoinLobby now uses this.CurrentLobbyName and this.CurrentLobbyType to join a specified lobby. + Changed: EnetPeer.StopConnection will always reset the state to be able to re-connect. + Changed: Disconnect() in SocketTcp and SocketUdp sets this.socket = null, even if socket.Close() caused an exception. This is what was expected. + Added: SocketUdpNativeDynamic and SocketUdpNativeStatic to "regular" Unity Lib, to improve compatibility in Unity for different export platforms (with and without native sockets). + +*** Version 3.2.2.1 (17. October 2013 - rev2335) + Note: This lib contains a lot of breaking changes and socket handling has been refactored. By now, this is well tested and confirmed working. + Changed: The way sockets are handled and added native-socket-lib support. There is a new IPhotonSocket interface which even allows to use external classes as socket wrapper. + Added: SocketImplementation property to set a class as socket implementation (Unity. sets native implementation or c# socket at compile time) + Changed: Win 8 RT and Phone now use fewer separate classes and files. Instead, more files from the (regular) DotNet client are used. RT and Phone are now part of the trunk folder in our SVN. + Added: TrafficStats.TimestampOfLastAck and .TimestampOfLastReliableCommand + Changed: Handling of server-side shutdown (a form of TCP disconnect) is now handled specifically as server-side-disconnect (was: generic receive exception) + Added: If a UDP connection times out in a client, it sends a special flag in it's disconnect command (to the server). This enables us to detect which side is triggering timeouts more often (and we can improve things). +LoadBalancing API + Fixed: issue where we called a virtual member from a constructor (http://confluence.jetbrains.com/display/ReSharper/Virtual+method+call+in+constructor) + Changed: LocalPlayer is now a property which checks null and returns a new Player (via virtual CreatePlayer) on demand. + Added: OpJoinRoom now optionally creates a room if parameter "createIfNotExists" is set to true and the room didn't exist. Room properties can't be set "on create" this way. LocalPlayer.IsMasterClient will be true. + Added: When OpJoinRoom can create a room, it also won't define which properties go into the lobby. You can use the new Room.SetPropertiesListedInLobby(). + Added: You can pass a actorNumber to OpJoinRoom when you re-enter a room and want to reclaim a specific actorNumber in that room. In best case, the client can re-join after a disconnect/crash and seamlessly go on playing. + +*** Version 3.2.1.6 (15. August 2013 - rev2272) + Changed: The library for Unity now contains a ExitGames.Client.Photon.Hashtable to be compatible with Win 8 exports. This must be used from now on! + Note: In Unity, the compiler will complain about ambiguous Hashtable definitions. To solve this, add this to the "using" part of your code: using Hashtable = ExitGames.Client.Photon.Hashtable; + Removed: Builds for Silverlight and Windows Phone 7.1 (this is not affecting Windows 8 RT and Windows 8 Phone SDKs which are of course supported) + Fixed: A null-reference check for a TCP connection's SendAcksOnly(). + +*** Version 3.2.1.5 (06.08.2013 - rev2242) + Added: Steam and Facebook entries to CustomAuthenticationType enum. + Fixed: Potential nullreference exception in TCP SendAcksOnly() code. If called before Connect(), this always failed. + Updated: Replacement classes for datatypes not supported on some platforms (Hashtable mostly). + Added: Hashtable got a new GetEnumerator that returns a IDictionary just like the standard Hashtable does. + Changed: Constructor with int InitialSize now calls the matching base constructor. + Removed: Synchronized() method which didn't do much and is not used. + Changed: ToString is now an override instead a "new" method. + Changed: DataTypes.cs: the Stopwatch is only implemented for Silverlight (non Windows Phone 8) + Updated: Description. + Changed: Protocol to expect Hashtable always providing a DictionaryEntry. Related to change in DataTypes.cs. + Changed: Protocol now has conditional "Dictionary" detection. In WP8 the API is different for that. Uses #if WINDOWS_PHONE. same file now works in W8 and WP8. + Changed: Removed PRPCAttribute from SupportClass.cs. This is used only in PUN and needs conditional per-platform compilation anyways, so it gets implemented there. + Removed: surplus debug output in ReceiveIncomingCommands(). + Fixed: Debug output in FetchServerTimestamp() depended on the Thread calling the method. Correct: The output is enqueued and dispatched later on. + Fixed: FetchServerTimestamp no longer fails with a SendError when the state is not "Connected". + Internal: Metro-Alike project now uses DataTypes.cs of Silverlight (like everyone else). Removed surplus copy. + Internal: DataTypes.cs and Protocol.cs files can now be used in DotNet 3.5, Windows Store and Windows 8 Phone. + Internal: outdated compiler-definitions "Photon" and "METROALIKE". + +*** Version 3.2.1.4 (10.07.2013 - rev2209) + Added: "Typed Lobby" API. Photon Cloud and Loadbalancing now support multiple lobbies per app/title. Also, different types of lobbies are now possible, each can have different options and different rules for matchmaking. + Added: enum LobbyType with "Default" and "SqlLobby". The SqlLobby is a new type of lobby that uses up to 10 room properties with sql-like filters. The filter being written like the "where" part of a sql query. + Changed: FetchServerTimestamp now enqueues callbacks (can be called by socket-receive-thread). also no longer causes a disconnect callback if offline + Changed: RemoveSentReliableCommand now enqueues callbacks (can be called by socket-receive-thread) + Internal: SendAcksOnly override in TCP's TPeer class. This now sends pings but nothing else. That resets the server's timeout for this peer +LoadBalancing API + Updated: LoadBalancing API in the Unity demos (now gets copied over at build time, making sure it's identical to the DotNet "original") + Fixed: LoadBalancingClient now handles SecurityException and InternalReceiveExceptions and disconnects correctly. Before, especially Unity web clients would get stuck in "Disconnecting" state. + Fixed: LoadBalancingClient state on disconnect (no matter what caused the disconnect). + +*** Version 3.2.1.3 (19.06.2013 - rev2170) + Fixed: surplus conversion of incoming data to string, which was used in debugging. + +*** Version 3.2.1.2 (17.06.2013 - rev2160) + Fixed: custom auth will send custom auth parameters if any authentication params are set + +*** Version 3.2.1.1 (10.06.2013 - rev2148) + Added: new POST value for Custom Authentication. POST can carry more data than GET (usually used). AuthenticationValues has a setter for this. +LoadBalancing API + Changed: LoadBalancingClient.AuthValues is renamed to CustomAuthenticationValues property (sets the custom authentication values). + Changed: Player class now compares by ActorNumer (assigned by server) instead of comparing the instance. + Internal: SupportClass.GetMethods() now returns type.GetRuntimeMethods(), filtered by attribute (if at all needed). This is used by Photon Unity Networking (PUN) internally. It also returns inherited methods now, not only Declared. + +*** Version 3.2.1.0 (24.05.2013 - rev2112) + Added: Feature "Custom Authentication" which lets you authorize players/users in the Photon Cloud with an external account/user service. More on that online: http://doc.photonengine.com/photon-cloud/CustomAuthentication + Added: LoadBalancing API Feature "Friend Finding" which enables a client to find friends in rooms by userId. If an external service provides a userID per player and a friend list, this can be used to find a friend's room (game) and join it (unless closed or full). + Added: CustomAuthenticationType enum to enable differnt types of custom auth later on (only one actually useful value so far). + Added: Class AuthenticationValues as container for authentication values. + Added: LoadBalancingClient.Connect overload which takes a AuthenticationValues parameter. + Added: LoadBalancingPeer.AuthValues property to set the custom authentication values. + Added: Parameter authValues to OpAuthenticate. This is used to provide the authentication parameters and or the secret/ticket provided by Photon. + Added: ErrorCode.CustomAuthenticationFailed to be used in switches for OperationResponse.ErrorCode (for OpAuthenticate). + Changed: LoadBalancingClient.PlayerName can be set before connecting to get a UserId which is "findable" by OpFindFriends(). Find friends does NOT use any values set for custom authentication! + Added: Class FriendInfo to contain a friend's name, online state and room name (if available and after using OpFindFriends()). + Added: OpFindFriends() to actually find the friends. Use on the Master Server only, not on a room. + Added: LoadBalancingClient.FriendList, a List of FriendInfo entries. Filled by using OpFindFriends (don't modify this list directly!). + Added: LoadBalancingClient.FriendListAge, to let you know how old the FriendList is. Only get updates when the list gets "old". + Fixed: OpRaiseEvent will no longer send "group" if it's 0 (which is the default). + Added: OpRaiseEvent overload to send object instead of Hashtable. This overload uses another parameter order to not mix up with the older implementation. You can send any serializable datatype now but must be aware if the event is Hashtable or something else. + Changed: Several variants of OpAuthenticate(), Connect() and ConnectToMaster() are now obsolete or removed. Use the alternative implementations (which should be cleaner). + Internal: Added several (operation) parameters to enum ParameterCode: ClientAuthenticationType, ClientAuthenticationParams, FindFriendsRequestList, FindFriendsResponseOnlineList, FindFriendsResponseRoomIdList. + Added: PhotonPeer.ResentReliableCommands to get count of re-sent commands (might be higher than out command count (as that counts created commands only) + Internal: Address (string) handling now uses string.Split instead of IndexOf to separate port from address and short.TryParse instead of short.Parse + Added: TrafficStatsGameLevel.ResetMaximumCounters() to reset those values that could max-out easily. Allows to get "longest delta between SendOutgoingCommands()-calls since last query". + +*** Version 3.2.0.2 (21.02.2013 - rev2066) + Fixed: Potential lock-up during sending. This could cause infinite blocking and thus a crash in some apps. (Win8 / Win Store api only) + +*** Version 3.2.0.1 (15.02.2013 - rev2060) + Fixed: Issue with delayed sending of operations in udp. The data could become overwritten before being sent. The bug was leading to high disconnect rates for clients using Windows Phone 7 and 8 and Silverlight or any client that used Network Simulation. + +*** Version 3.2.0.0 (13.02.2013 - rev2053) + Note: This release only changed the version, matching the new Server SDK v3.2.0.0 + Updated: readme.txt + Fixed: Reference for Windows 8 RT and Windows Phone 8 SDKs. + Added: Particle Demo to Unity Client SDK. + +*** Version 3.0.1.18 (11.02.2013 - rev1998) + Added: Optional per package CRC checksums to filter out compromised packages (avoiding more issues, compared to reading bad values). + Added: PhotonPeer .CrcEnabled and .PacketLossByCrc to handle CRC and get the count of affected (incoming) packages. + Note: Checking packages with CRC will take some computation time. Consider this an option to detect if/why someone's connection is bad. It's likely not good to be enabled by default. +Windows 8 RT & Windows 8 Phone: + Fixed: Serialization of foat and double values. These caused exceptions when used in object-arrays. + +*** Version 3.0.1.17 (19.12.2012 - rev1946) + Added: New Platform: Mono 4 Android. Please check the Readme.txt for hints how to build the demo in Mono 4 Android. + Changed: The referenced DotNet assemblies used by our libraries, which makes ours compatible with Mono 4 Android and others. + Changed: The Particle Demo Logic to also handle events sent by JavaScript clients. In case these are used, the types used in event differ from what DotNet or other clients send. + Changed: PhotonPeer.LocalTimeInMilliSeconds property now uses SupportClass.GetTickCount(). That method is using Environment.TickCount (which can be replaced if needed). + Changed: Any place that directly used Environment.TickCount (as the way SupportClass.GetTickCount() gets the value can be replaced). + Renamed: GetLocalMsTimestampDelegate is now: SupportClass.IntegerMillisecondsDelegate (rarely used if at all). + +*** Version 3.0.1.16 (29.11.2012 - rev1923) + Internal: A client timeout now internally sets connectionState to Zombie and then calls Disconnect() instead of stopping the connection right away. + Changed: Disconnect() sends a disconnect-command in any case (except not connected or disconnecting). If the connection is not in state connected anymore, said command is unsequenced (unreliable) and the disconnect is locally executed immediately as call to StopThread(). As before, disconnecting and disconnected clients won't send this. + Changed: Ping creation is now more strict and checks also if any reliable commands are outgoing AreReliableCommandsInTransit(). this avoids a few pings. + Fixed: NullReference exception in StopConnection() if it's called before being connected for the first time (late object creation made this fail). + Changed: PhotonPeer.LocalTimeInMilliSeconds property now uses SupportClass.GetTickCount(). That method is using Environment.TickCount (which can be replaced if needed). + Changed: Any place that directly used Environment.TickCount (as the way SupportClass.GetTickCount() gets the value can be replaced). + Renamed: GetLocalMsTimestampDelegate is now: SupportClass.IntegerMillisecondsDelegate (rarely used if at all). + + +*** Version 3.0.1.15 (27.11.2012 - rev1917) + Note: Silverlight SDK release only! + Updated: Silverlight projects with proper references (hopefully). In case you wonder: Some projects are included even though only their (source) files are linked in Silverlight. We can't reference DotNet projects directly, so we use the (shared) files instead. + Updated: Silverlight Particle Demo now has a basic gui and hopefully helps with your first steps. + +*** Version 3.0.1.14 (16.11.2012 - rev1891) + Added: Interest Groups! In rooms, you might send events to an interest group, identified by a byte (255 groups are currently allowed). OpChangeGroups lets you add or remove groups you're interested in. + Added: New platform! Welcome Windows 8 RT and Windows Phone 8. Both are "preview" releases but based on the stable DotNet basis we have. + Note: The Windows Phone 8 SDK does not yet have a LoadBalancing demo but the API is available (Windows Phone 8 is separate from the still existing Windows Phone 7.1 SDK). + Added: Another new platform: Playstation Mobile! This is Sony's SDK for mobile platforms. Find out more about it: www.playstation.com/psm + Added: Silverlight 4 SDK is back. Now with LoadBalancing API (the demo implementation is not finished but the basic "logic" is running). + Fixed: Windows Phone 7 and Silverlight TCP error handling while connecting to the server. This should fix issues with failing connects due to missing policy settings. + Internal: Windows Phone 7 and Silverlight TCP connections now set their state a bit differently (this doesn't affect the workflow though). + Internal: Http implementation now checks if a Proxy was set deliberately. Check is: (WebRequest.DefaultWebProxy as WebProxy != null). + Internal: DispatchIncomingCommands() now avoids copying lists when checking for commands that need a repeat. + Internal: SendOutgoingCommands() now re-uses a buffer to create UDP packages in before sending. This should save a lot of memory allocation. +LoadBalancing API: + Added: New demo "Particle". You will notice it's similar to the "Realtime Demo" but LoadBalancing- and Cloud-compatible and it makes better use of the default features. Check out Particle "Logic". + Added: LoadBalancingClient.DisconnectedCause to track certain disconnect causes (no matter if the connection or an operation caused the disconnect). + Added: DisconnectCause enum to enumerate those disconnection causes. + Changed: LoadBalancing OnOperationResponse() and OnStatusChanged() to track most disconnect reasons (in DisconnectedCause). + Removed: LoadBalancing Connect() variants that essentially were duplicates of others. + Changed: LoadBalancingClient debug output now goes to: Debug.WriteLine (which is available in debugger, while Console is not always). + Changed: CacheProperties method is now virtual for Room and Player. This allows you to override it and use this as callback to update props. + Added: Player.Tag to store arbitrary (game) data with the Player. Put in (e.g.) a player's representation/avatar or similar. + Added: ErrorCode constants MaxCcuReached and InvalidRegion. These are important for the Photon Cloud. + Added: Handling for DisconnectedByUserLimit. This is a status of OnStatusChanged when a Photon Server License's CCU limit is reached. This no longer will try to connect to a Game Server (where it gets rejected, too). + Changed: Debug output of loadBalancingClient now goes to Debug.WriteLine (which is available in debugger). + Changed: API now uses a factory method to create Room instances (this makes it possible to extend the Room class and instantiate the new class instead). + Changed: The Player constructor now has an "actorProperties" parameter and will cache the provided properties. This makes sure actor-props are available locally. +Windows Phone 8: + Added: Demo for Cloud / LoadBalancing. The Particle Demo only has a special WP8 GUI and links it's logic from a separate project (read: folder). +Windows 8 RT: + Added: Demo "Phong", which is a simplified, basic multiplayer game. It's focus is to show how to sync data, not to make it super smooth and error free. Let us know any issues but bear with us as it isn't fully featured. + +*** Version 3.0.1.13 (26.09.2012 - rev1731) + Fixed: Internals of method DispatchIncomingCommands() for UDP. In some cases this removed commands from a dictionary inside a foreach loop (which causes an Exception due to changing the dictionary) + Added: Support for Dictionary<,>[]. This is not a very lean way to send data (especially when using ) but if needed, it now works + Changed: Replaced several foreach loops with for loops (it doesn't change usage but in Unity exports to iOS, foreach uses more memory than for) + Added: Doc for public methods in Protocol class (they are useful to quickly write values into an existing byte-array) + Fixed: Unity UDP send code: iOS 5 devices will kill a socket when the power button is pressed (screen locked). This case was not detectable by checking socket.Connected. + Added: Unity UDP send code: Now tries to open another socket to refresh/keep the connection. This is affected by timeouts still, of course (as are all connections). + Internal: locked usage of UDP / enet channels + +*** Version 3.0.1.12 (26.07.2012 - rev1683) + Changed: The DotNet client libraries are now Thread safe! You could start a background Thread to keep calling SendOutgoingCommands in intervals and still call it from a game loop, too + Changed: Due to the thread safety, the demos no longer use excessive locks. This is now solved by the lib, more streamlined and hidden. One Thread is used instead of Timers (which could fire concurrently if execution was longer then their interval) + Changed: Moved the enable/disable property fro NetworkSimulationSettings to PhotonPeer.IsSimulationEnabled (this should now be thread safe) + Changed: NetworkSimulation will create and keep one thread when you first enable it in a (debug) client. Disabling it, will execute any delayed action immediately (in IsSimulationEnabled!) and pause the simulation thread + Changed: All demos are updated. We assigned new event codes (starting at 0, like any developer's code should) and extended the comments. Check them out + Changed: All Loadbalancing demos are now using the same DemoBasisCode linked in, so it can be changed in one position. Where needed an extension is made + Updated: comments / documentation for LoadBalancing API, Lite API and basic Photon API (basically anything public) + Changed: SupportClass.NumberToByteArray is now obsolete. It can be replaced with Protocol.Serialize() easily and that is performing better + Fixed: Windows Phone UDP socket was sending a full package of zeros on connect. It didn't break anything but is not needed, of course. + Fixed: SupportClass.StripKeysWithNullValues method was prone to throw an exception +LoadBalancing API: + Changed: LoadBalancingClient.OpLeaveRoom() skips execution when the room is null or the server is not GameServer or the client is disconnecting from GS already + Note: LoadBalancingClient.OpLeaveRoom() returns false in those cases and won't change the state, so check return of this method + Fixed: workflow for authentication (which should be called only once per connection, instead of "any time we establish encryption) + +*** Version 3.0.1.11 (05.06.2012 - rev1569) + Fixed: Udp issue with channels and unreliable commands. Unreliable commands of one channel were discarded, when another channel had unreliable commands, too + +*** Version 3.0.1.10 (04.06.2012 - rev1561) + Fixed: TCP connection issues for DotNet and Unity (Silverlight and WindowsPhone are different) + Fixed: DotNet+Unity TCP send calls with 0 bytes to send (this was ignored by the socket but useless anyways) + Moved: DNS resolution and socket.Connect() are now handled in the connection thread (TCP in DotNet and Unity) + Fixed: Issue with (TCP) socket connections being closed directly while connecting. in this case, socket.Receive() might receive 0 bytes instead of blocking until more bytes are available. without sending anything, the socket never updates its .Connected state and never throws a Exception. now we send a ping and thus trigger a exception + Fixed: Some documentation errors (due to changed API, etc) +Loadbalancing API: + Changed: LoadBalancingClient.OnEvent() now uses a join-event's actornumber-list to create Player instances for anyone who wasn't created as Player before + Fixed: LoadBalancingClient.OnEvent() handling for join-event does not expect any actor/player properties anymore (which fixes a potential null-reference exception when not even a name is set) + +*** Version 3.0.1.9 (10.05.2012 - rev1512) + Fixed: Reference to project in Windows Phone SDK + +*** Version 3.0.1.8 (09.05.2012 - rev1508) + Fixed: The OpJoinRandom of the LoadBalancingAPI failed to filter rooms for their custom room properties. Instead, any room matched. This is fixed now. + Added: New Demo for Windows Phone: Cloud Basics + Changed: The loadbalancing / cloud-based demos are refactored to share a similar codebase + +*** Version 3.0.1.6 (07.05.2012 - rev1489) + Note: This is a "stable" release, containing only a few updates. The bulk of changes are in the "odd" numbered releases. Read those updates carefully. + +*** Version 3.0.1.5 + Changed: adopted the even/odd version numbering system. versions ending on a odd number = intermediate/in-development version, even number = released (that makes 3.0.1.5 a intermediate) + Fixed: When NetworkSimulation is disabled, all remaining packages are sent/received immediately (ignoring the former delays) + Note: NetworkSimulation should be working nicely now. Be aware that sudden, additional lag might (!) lead to a disconnect. Play with the settings to find out which ones work for you + Changed: Protocol class now has a few methods to (effectively) serialize some datatypes to arrays (and into existing arrays) + Removed: Surplus public methods from Protocol that were "type-named" like SerializeFloat. The functionality is in still with overloaded methods + Added: count of packages (requests) outgoing if TrafficStatsEnabled +Demo Realtime: + Changed: The commandline arguments are now server:port, protocol (udp,tcp,http), reliable sending, interval dispatch, interval send, interval move. Example: localhost:5055 Udp false 15 25 15 + Changed: Demo Realtime: If the commandline sets an unknown protocol, the client shows a message and closes gracefully + Changed: Demo Realtime: The demo now starts in the grid view (showing something). Local player and player list are created with the Game instance. Player startpoint is randomized. +Loadbalancing API: + Renamed: LoadBalancingClient.lbPeer to .loadBalancingPeer + Fixed: LocalPlayer.SetCustomProperties() usage + Added: Service() method, which calls the LoadBalancingClient's Service simply + Changed: LoadBalancingClient is no longer extending LoadBalancingPeer but instead using one + Changed: the many overloads of Operations are gone in LoadBalancingPeer to streamline the api + Changed: ActorProperties are no longer set via JoinRoom, JoinRandomRoom or CreateRoom. instead, set the properties in the LocalPlayer and let the LoadBalancingClient send and sync them where necessary + Fixed: MasterClientId is now 0 when there are no more players in the room (it was set to int.max before) +Internal: + Changed: all DispatchIncomingCommands now use a while loop to dispatch the ActionQueue (in the hope this is the fastest way to do it) + Changed: DispatchIncomingCommands now looks for the received unreliable command with lowest unreliable seqNr to dispatch this + Changed: DispatchIncomingCommands discards commands if the reliable OR unreliable sequence is beyond the command's sequences + Changed: DispatchIncomingCommands now truncates the incoming unreliable commands to limitOfUnreliableCommands (if that's > 0) + Changed: the next reliable command to dispatch is now fetched with Dictionary.TryGetValue() (for being faster) + Changed: no longer using BinaryReader streams anywhere (this should improve speed and reduce mem usage) + Changed: PeerBase accordingly + Changed: Unit test MyType de/serialization now supports null-references (as 1 byte being 0) + Changed: Protocol.SerializeOperationRequest is now used in the same way, no matter if request is "top level" or inside some other datatype + Changed: the peer bases accordingly to use only one SerializeMemStream and lock it + Changed: how encryption fits in to the new serialization (it is a special case, as only the operation bytes get encrypted) + Added: Protocol.SerializeParameterTable() as requests, events and responses all use the same way to write their parameters + Changed: SerializeOperationToMessage parameter order + Changed: Order of Protocol methods to make more sense (from byte to more complex types for serialization) + New: PhotonDotNet library prototype for windows 8 metro + +*** Version 3.0.1.3 (13.04.2012 - rev1430) + Known issues: The Network Simulation is currently not guaranteed to work properly. Please bear with us. + Note: the following change might be a breaking one: + Changed: When dispatching a server's disconnect-command, the state is changed to ConnectionStateValue.Disconnecting BEFORE any callback due to state change is called. This should disallow game-code from calling any operations immediately. + Changed: Many internals. This should result in better performance + Changed: Service() now calls SendOutgoingCommands() until send-queues are empty. This might take more time but gets important commands out. If you need more control, Service() can be replaced with DispatchIncomingCommands and SendOutgoingCommands! + Added: null check to GetEndpoint() to avoid issues when the host address is null + Fixed: queueIncomingCommand() debug out message when a command is being received AND in in-queue (the list it accesses is now a dict) + Added: new "vital" stats to TrafficStats + Added: LongestOpResponseCallback and LongestOpResponseCallbackOpCode (opcode and time of longest callback) + Added: LongestEventCallback and LongestEventCallbackCode (event code and time of longest callback) + Added: LongestDeltaBetweenDispatching and LongestDeltaBetweenSending to detect "gaps" between subsequent calls of those + Added: DispatchCalls and SendOutgoingCommandsCalls to measure average call-rate + Fixed: PeerBase.TrafficStatsEnabledTime now checks if a stopwatch is set, else it returns 0 + Fixed: TrafficStatsReset() now works as intended (starting a new stopwatch, too) +Internal: + Changed: name of variable timeLastReceive. is now: timeLastAckReceive (better fit with what it does) + Internal: queueOutgoingReliableCommand() to use a lock on the channel it accesses + Internal: SerializeOperationRequest() now locks the MemoryStream while using it (avoids threading-issues with calling OPs) + Internal: SendUdpPackage() now checks if socket is obsolete (and disconnected for a reason) or not. only if not, a error is logged + Internal: EnetChannel now uses Dictionary and Queue for commands (should be faster to access) + Internal: simplified access methods in EnetChannel according to changes + Internal: outgoingAcknowledgementsList is now a Queue + Internal: receiveIncomingCommands() no longer has a local variable sentTime. instead using this.serverSentTime directly + Internal: UDP sending is now done with a synchronous socket call (profiling told us: this is cheaper) + Internal: re-using the socket arguments for receiving packages (saves some buffer allocation) + Internal: socket to non-blocking (maybe not possible on all devices) + Removed: initial-HTTP-protocol support (HTTP support not public yet) + Added: support for encryption with HTTP protocol + +*** Version 3.0.1.2 +- Added: Rooms now have a "well known" property to list the custom properties that should be available in the lobby. This can be set per room (but most likely makes sense per title/application). +- Added: LoadBalancingClient.OpCreateRoom() has a new parameter "propsListedInLobby" and Room.PropsListedInLobby is available to check this list (if needed at all). +- Added: GameProperties.PropsListedInLobby as "well known property" key +- Changed: LoadBalancingPeer.OpCreateRoom now sets ParameterCode.CleanupCacheOnLeave to true by default. This makes the server clean a player's event cache on leave. +- Added: SupportClass.DictionaryToString() will now print values of string[] and optionally leaves out type information. +- Note: 3.0.1.1 didn't get it's own SDK, so read that version's changes, too + +*** Version 3.0.1.1 +- Added: PhotonPeer.TrafficStatsElapsedMs, which gives you the milliseconds that the traffic stats are enabled. This internally uses a stopwatch (for now) which might not be available on all platforms. Please report if this new SDK causes issues. +- Added: PhotonPeer.TrafficStatsReset() to reset the traffic stats and the timer. This could be useful to get stats of "in game" versus "out of game". Note: Loadbalancing includes frequent server-switching and each disconnect/reconnect causes a reset. +- Changed: In LoadBalancingPeer EventCode.SetProperties is obsolete and replaced with EventCode.PropertiesChanged. Please switch to new constant. +- Added: Support in LoadBalancingAPI for Player.IsMasterClient. For this, the Players now get a RoomReference set (when added). The active player with the lowest ID is the master (per room). +- Added: Room.MasterClientId, which is updated when new players are added or the current master is removed. +- Added: SupportClass.DictionaryToString() has an overload which doesn't "print" the Type per key/value. +- Added: Loadbalancing API overload for OpJoinRandomRoom(...) taking additional parameter 'playerProperties' +- Added: Loadbalancing API CacheProperties() and Room.GetPlayer() are public now +- Added: LoadBalancingClient will now handle ExceptionOnConnect and keep clients from re-connecting if establishing a connection fails +- Note: The following changes affect only HTTP, which is an upcoming option for connections. So far, the public server SDKs don't support this. Feel free to contact us about it. +- Added: setter for PhotonPeer.ServerAddress to allow setting a http url (even while connected) +- Added: PhotonPeer.HttpUrlParameters setting parameters to be added to end of url (must begin with '&') +- Added: HttpUrlParameters to PeerBase +- Added: HttpUrlParameters is now attached to the end of a URL in http usecase +- Added: "Http2" support to Unity library +- Internal: method HttpBase.ConnectAsync is no longer needed and Request() is now directly passed to thread + +*** Version 3.0.1.0 +- Added: Loadbalancing (Cloud) Features +- Added: Project with the Loadbalancing sourcecode for DotNet, WindowsPhone and Unity3d (usable without PUN) +- Added: Initial, simple Loadbalancing demos for each platform (will update and extend those) +- Note: The API of the client libraries didn't change. The new features were added on top of the known API +- Added: VS2010 solutions for DotNet and Windows Phone SDKs containing the demos and APIs in the package +- Added: readme.txt with initial help to setup the Cloud/Loadbalancing demos +- Added: default appId for Loadblanacing demos: "" + +*** Version 3.0.0.10 +- Added: When UDP StartConnection (internal method) fails, callbacks to OnStatusChanged(StatusCode.Disconnect) are now done additionally to the SecurityExceptionOnConnect and ExceptionOnConnect calls. This happens direcly inside PhotonPeer.Connect()! +- Changed: When Unity UDP implementation fails to connect due to missing DNS resolution, it now also calls OnStatusChanged(StatusCode.ExceptionOnConnect) +- Removed: StatusCode.Exception_Connect value (obsolete, replaced by ExceptionOnConnect, same value) +- Fixed: Http connections (DotNet & Unity) now skip results while in disconnected state +- Fixed: Http connections (DotNet & Unity) now ignore results after a disconnect and reconnect was done (this applies only to HttpBase, not HttpBase2) +- Fixed: misleading debug out (pointing to WindowsPhone while the class is now in general use) +- Changed: DotNet UDP connection now only logs socket errors if the connection isn't obsolete (disconnected) already + +*** Version 3.0.0.9 +- Fixed: issue with HTTP connections and EstablishEncryption() +- Changed: ActionQueue is now a Queue, allowing Dequeue in a while loop instead of foreach(i in list) and clear() +- Changed: Unity HttpBase DispatchIncomingCommands() to make use of the queue +- Fixed: init byte[] length (internal. did not have consequences) +- Fixed: LitePeer OpRaiseEvent() was sending encrypted +- Internal: ContainsUnreliableSequenceNumber() check if reliable list needed sorting +- Fixed: Unity/Silverlight bug with encryption. Their implementation of BigInteger.GetBytes() failed when the 2nd, 3rd or 4th of the first 4 bytes was 0 but the previous wasnt. This led to incompatible secrets. +- Changed: TCP socket sending debug output now checks debuglevel (when send is skipped, cause the sender is obsolete already) +- Added: caching option RemoveFromRoomCacheForActorsLeft = 7 +- Internal: Added another http-based communication protocol. Please note: The fitting server's are not yet publicly released. This does not affect UDP or TCP protocols. + +*** Version 3.0.0.8 +- Fixed: Udp fragment reassembly in case fragments are received out of order and incoming queue was not yet sorted +- Fixed: Handling of incoming reliable commands (udp) which were skipped in some cases, if not received in order +- Fixed: Network simulation issue which caused lost incoming commands +- Fixed: Demo Realtime. protocol is now again Udp, fitting the default server address "localhost:5055" (make sure to build the demo with your server's address if Photon is not on the same machine) + +*** Version 3.0.0.7 +- Changed: Udp socket usage for Unity 3d lib. Both threads (send (in game loop) and receive (separate)) now have minimal locks while using the socket +- Fixed: SendOutgoingCommands now returns true if anything didn't make it into the outgoing UDP package +- Internal: TCP connections also skip network simulation when it's turned off + +*** Version 3.0.0.6 +- Fixed: SendOutgoingCommands now returns true if commands are remaining in outgoing queues (UDP only sends one package per call, TCP will send anything outgoing). +- Added: New "RoomCache" for Events. The EventCaching enum allows you to use it. Events in this cache will keep the order in which they arrived in the server. A filter makes deleting them very flexible. +- Internal: Ability to make lib send only ACKs and nothing else. This is probably a temp solution as it might be better to make sending and calling ops completely thread safe. +- Internal: PhotonPeer.IsSendingOnlyAcks, which is locked with the sending (not changing while sending). This makes SendOutgoingCommands() thread safe, which is good if you need a separate thread to keep connection. You could call operations while sending. +- Internal: Unity3d's connection now also syncs socket usage + +*** Version 3.0.0.5 +- Fixed: ObjectDisposedException in DotNet UDP workflow. This was caused by disconnecting while incoming data was processed (and before the next datagram was accepted) +- Added: PhotonPeer.LimitOfUnreliableCommands property. This helps you skip potentially "outdated" unreliable commands (events), which helps if you couldn't dispatch for a while +- Internal: Minor performance improvements. Example: The check if network simulation is turned on is done earlier in the workflow, which avoids a bit of overhead + +*** Version 3.0.0.4 +- Fixed: Tcp connections have been throwing ArgumentNullException in DispatchIncomgingCommands() if they were not connected yet +- Internal: Adjusted Http client to server rev2360 + +*** Version 3.0.0.3 RC2 +- Internal: Communication with HTTP server is WIP (Work In Progress - not a publicly available feature) + +*** Version 3.0.0.2 +- Fixed: OpRaiseEvent overload with EventCaching and ReceiverGroup parameters was not sending the customEventContent as expected. This was always null. +- Fixed: Time fetching case where no time was accepted. Servertime is now accepted, if the fetch-time-command was less or equal as the current roundtrip time. Avoids issues if rtt is exceptionally low immediately. +- Internal: When using multiple channels, dispatching incoming commands now will continue with the next channel, if one doesn't yet have the next reliable command (reliable sequence of one channel does not affect others) +- Internal: Changed protocol for TCP and message headers. This will support bigger message sizes. Also changed debug out related to unknown headers. +- Internal: Changed handling of TCP receive-callbacks for unfinished messages in Silverlight and WP. This should fix handling of very big data that's received in multiple "chunks" +- Internal: Http messages are now deserialized the same way that content in tcp or udp is handled + +*** Version 3.0.0.1 RC1 +- Fixed: Packaging of SDK now includes all files in demo folders, except a list of ignored file-endings (xaml and jpg files were missing in previous Silverlight and WindowsPhone SDKs) + +*** Version 3.0.0.0 RC1 +- Changed: Filenames! Now include a '3' for Photon v3. Update your references! Also, Silverlight libraries now use "Silverlight" in the filename (was: SL) +- Changed: Versioning. A dll's version has now 4 digits. The first 2 match Major and Minor number of the Server SDK. The latter 2 are Release and Build respectively +- Changed: Silverlight DataTypes (like Hashtable) are now in namespace ExitGames.Client.Photon. This is easier to include (as that namespace is in "using" in most cases) + +*** Version 6.4.5 +- Changed: Parameters for OpCustom are now of type Dictionary, making sure that only byte-codes are used for parameters +- Changed: Most IPhotonPeer names (to match those in server code): EventAction -> OnEvent, OperationResult -> OnOperationResponse, PeerStatusCallback -> OnStatusChanged +- Added: SupportClass.DictionaryToString(), which converts the content to string (includes support for Hashtables) +- Moved: Definitions of Lite and Lite Lobby specific codes for Parameters, operations and events are now in LitePeer. Will be available as source and could be replaced +- Changed: Usage of codes in Lite and Lite Lobby. Now pre-defined codes are starting at 255 and go down. Your events, operations and operation-parameters can now start at 0 and go up without clashing with pre-defined ones +- Changed: Constants that are non-exclusive (like event codes and OpKeys, which can be extended) are no longer "defined" as enums but as class of const byte values. Less casting but also less convenient "name" representation in debug output +- Added: LiteEventKey.CustomContent as key to access the content you sent via OpRaiseEvent ("Data" seems a bit misleading but is also available) +- Changed: Namespace of LitePeer to ExitGames.Client.Photon.Lite (the Lite-specific class is still compiled into the library for convenience but can be ignored quite easily this way) +- Added: Property MaximumTransferUnit. The default is 1200 bytes. Usually this is ok. In few cases, it might make sense to lower this value to ~520, which is commonly assumed the minimum MTU. Don't change this, if you don't know why. +- Added: New classes to wrap up op-requests (OperationRequest), op-results (OperationResponse) and events (EventData). Those new classes are now used in callback methods OnEvent and OnOperationResponse +- Changed: by using the new classes (note above), the client is a bit more like the server in its naming. We didn't want to change every last bit though. +- Internal: Changed protocol (to 1.6) so that it does not require any parameter codes internally. Any application can now define any operation, parameter and event codes it wants to. +- Changed: Encryption is now triggered by you and resolved by the library. You don't have to look out for the result of EstablishEncryption and use it. Instead: wait for OnPeerStateChanged call with either EncryptionEstablished or EncryptionFailedToEstablish +- Removed: InvocationId. This concept was very rarely used but confusing. It's easy to implement, if needed. If you don't know what this means: Nevermind. +- Changed: Operation calls now return bool: if they could be enqueued or not. If enqueued (cause you are connected and the data was serializable), then SendOutgoingCommands will send those operations (as before). +- Added: Support to de/serialize Dictionary. If the types are more specific than object, the serialization writes the type-code only once (lean byte usage in protocol) +- Added: Support to de/serialize null. Enables you to send a null value, e.g. in a Hashtable +- Added: ReceiverGroup enum to select a range of players that get an event via Operation Raise Event +- Added: Event Caching. Any event sent via RaiseEvent can now be buffered on the server side and is "repeated" when a new player is joining a room. This is similar to Properties but lets you categorize your info better and works just like regular events, too. +- Added: EventCaching enum to select if an event is to be cached and how it's cached: either "not at all" (default), replacing anything cached so far (fast) or "merge" (which will add new and replace old keys with new values). Optionally, a event can be raise with option "remove". +- Added: new overload of OpRaiseEvent() with the two new parameters noted above +- Added: Support for custom de/serializer methods. By writing 2 methods to convert a object into a byte-array (and back from that), Photon now supports any custom object type (standard datatypes are still supported out of the box) +- Added: PhotonPeer.RegisterType() to register serializer and deserialize methods for a certain type. Per object, a length and one byte 'type code' are added to the serialized data +- Added: Support for non-strict object[]. Unlike strictly-typed array, here each element will carry its own type. +- Note: If you want to use the new Custom Types or the object[], you have to update your server! Older Servers don't support the new features. As long as you don't use these features, the library is compatible with previous servers. +- Added: ByteCountCurrentDispatch and ByteCountLastOperation properties to PhotonPeer (the ancestor of LiteGame, etc). A game can now access the size of operation-results and events as well as operation-call size. +- Added: Traffic statistic set: PhotonPeer.TrafficStatsGameLevel as "high level" game-related traffic statistic. Counts bytes used by operations, their results and events. This includes overhead for these types of messages, but excludes connection-related overhead +- Added: Traffic statistic set: PhotonPeer.TrafficStatsIncoming and PhotonPeer.TrafficStatsOutgoing as low level statistics of the traffic +- Added: PhotonPeer.TrafficStatsEnabled which enables two sets of traffic statistics. By default, statistics are turned off. +- Added: Classes TrafficStats and TrafficStatsGameLevel for the two statistic cases metioned above +- Changed: NetworkSimulation now starts a Thread when it becomes enabled and the thread ends on simulation disable. Disable the NetworkSimulation to stop the thread, as Disconnect does not change the simulation settings! +- Internal: Cleanup and renaming of several properties +- Internal: Each new peer increases the PeerCount but it is no longer reduced on disconnect (it is existing still, after all) +- Internal: Udp commands will be buffered when serialized. This saves some work when re-sending a reliable command +- Added: TCP Routing code (not in Silverlight). To be used when running Photon on Azure (can be ignored in regular use) +- Added: to StatusCode: TcpRouterResponseOk = 1044, TcpRouterResponseNodeIdUnknown = 1045, TcpRouterResponseEndpointUnknown = 1046 and TcpRouterResponseNodeNotReady = 1047, +- Added: override for PhotonPeer.Connect() with node +- Internal: DotNet now reads the 2 bytes routing response, if a routing request was made (also, not in Silverlight) +- Internal: If TConnect sent a routing request, nothing else will be sent until 2 bytes response are read. +- Internal: If the routing-response does not start with ProxyResponseMarkerByte = 0xF1, a debug message is enqueued and TCP will disconnect +- Internal: Init request for TCP is now always enqueued instead sent directly. This way, it can be delayed if a routing node is selected +- Internal: TPeer EnqueueInit() and SendProxyInit() now create init and routing request respectively +- Internal: TConnect.sendTcp() checks isRunning before it tries to send (the socket might close before the NetSim does). This won't be an issue anytime, still INFO-level callback to DebugReturn is done. +- Removed: debug out for "send package" situation (even on ALL-level, this is more or less spam) +- Internal: updated version numbers of init to 6.4.5 +- Changed: SupportClass HashtableToString() returns "null" if parameter is null +- Internal: Removed SortedCommandList and CommandList classes. Replaced by List and a Sort() where necessary +- Internal: EnetPeer.channels is now a Dictionary instead of a SortedList +- Internal: the channels are initialized with channel 0xff first - this makes 0xff high prio in all foreach usaged +- Internal: NCommand class is now IComparable for usage in Sort() + + +*** Version 6.4.4 +- Added: PhotonPeer.TimestampOfLastSocketReceive now provides the time when something was received. Can be used warn players of bad communication-timing even before the disconnect timeout will be happening +- Fixed: OpGetPropertiesOfActor did use the actorNrList correctly, which always got you all properties of all players + +*** Version 6.4.3 +- Changed: A udp connection timeout in Unity will now end the socket-handling thread correctly +- Changed: The thread for Network simulation is now stopped when the client disconnects and started on connection (instead of keeping it per peer) +- Fixed: Exceptions in network simulation, when Disconnect() was called soon after Connect() but before the connection was established. + +*** Version 6.4.2 +- Fixed: It was possible to send PhotonPeer.FetchServerTimestamp() before being connected properly. Now the method triggers debug output (INFO level) and the callback PeerStatusCallback(StatusCode.SendError) +- Internal: Added a lock in the UDP version of SendOutgoingCommands(). It's still illegal to access a peer from multiple threads but the follow-up issues this lock avoids are very difficult to track. +- Internal: to stay compatible with all exports of Unity, the use of System.Threading.Interlocked.Exchange was replaced by simply replacing the list's reference instead + +*** Version 6.4.1 +- Changed: The Unity library now uses the WWW class for Http based requests. Results are checked within DispatchIncomingCommands(). Important: Unity allows handling WWW requests only on the MainThread, so dispatch must be called from this context! +- Note: Photon does not support Http requests out of the box. Customers get access to a fitting server on demand +- Changed: outgoing list is now replaced on send, instead of calling remove(0) repeatedly (which takes longer). Internal: this uses System.Threading.Interlocked.Exchange to switch to a new outgoing list in one step + +*** Version 6.4.0 +- Fixed: TCP handling of incoming data. This avoids loss of data (operation-results or events) when a lot of data is incoming. +- Changed: PeerStatusCallback() is less often called for queue-length warnings (e.g.: StatusCode.QueueIncomingReliableWarning). Only if a queue has a multiple of PhotonPeer.WarningSize items. +- Changed: WarningSize is now 100 by default +- Changed: Description of PhotonPeer.WarningSize and PhotonPeer.CommandBufferSize, which really is just the initial size of any buffer. The warnings are there to avoid situations where all heap is used up. +- Changed: Naming: StatusCode.Exception_Connect is now Obsolete and replaced with StatusCode.ExceptionOnConnect +- Added: Missing summary for StatusCode.SecurityExceptionOnConnect +- Added: NetworkSimulationSet.ToString override to provide a better overview +- Added: Support for arrays of Hashtables + +*** Version 6.3.1 +- Fixed: Network simulation now delays incoming packages by IncomingLag and IncomingJitter as expected (it was using the outgoing values, too) + +*** Version 6.3.0 +- Added: Network simulation (lag, jitter and drop rate) to debug builds +- Added: class NetworkSimulationSet with properties to control network simulation +- Added: NetworkSimulationSettings.NetworkSimulationSettings property to get current simulation settings +- Changed: only the first peerId of a VerifyConnect is accepted in client (avoids surplus peerID changes) +- Internal: added PeerBase.SendNetworkSimulated() and PeerBase.ReceiveNetworkSimulated() and a Thread to run delay simulation +Siverlight: +- Updated: to Silverlight v4.0 +- Added: Encryption to Silverlight library +- Internal: updated internal BigInteger class for Silverlight +- Internal: DiffieHellmanCryptoProvider in Silverlight, so it uses AesManaged instead of Rijndael (which is not part of Silverlight 3) +- Added: Stopwatch class to DataTypes.cs (for Silverlight only) + +*** Version 6.2.0 +- Added: "Demo LiteLobby Chatroom" to Unity SDK +- Updated: Demo Realtime in Unity client SDK. It's still compatible with the demo on other platforms but cleaned up and much better commented +- Updated: Documentation is now clearer on where the Lite logic is used (it runs on Photon but is not the only application logic) +- Updated: Documentation for the enumerations in IPhotonListener. The Lite application based ones are better described and it's now clear which ones are essential to the Photon client (not only in Lite) +- Updated: Documentation in several other places +- Added: StatusCode.SecurityExceptionOnConnect which is thrown if a security exception keeps a socket from connecting (happens in Unity when it's missing a policy file) +- Added: PhotonEventKey and PhotonOpParameterKey which contain the fixed byte keys that cannot be re-assigned by applications at will (as these keys are used in the clients and server in their respective context) +- Change: PhotonPeer.PeerState is no longer a byte but of type PhotonPeer.PeerStateValue, which makes checking the state simpler. The PeerStateCallback() for state changes is still called as before. +- Changed: Property PhotonPeer.PeerState. It now converts the low level ConnectionStateValue to a PeerStateValue, which now includes a state InitializingApplication. See reference for PeerStateValue. +- Changed: PeerStateValue enum is now part of the ExitGames.Client.Photon namespace, making it more accessible +- Internal: NConnect in DotNet and Unity to catch security exceptions +- Internal: from using var to explicit type usage in DiffieHellmanCryptoProvider.cs (Mono Develop friendly) +- Internal: made const: ENET_PEER_PACKET_LOSS_SCALE, ENET_PEER_DEFAULT_ROUND_TRIP_TIME and ENET_PEER_PACKET_THROTTLE_INTERVAL +- Internal: PeerBase "PeerStateValue peerState" is now: "ConnectionStateValue peerConnectionState" (holding the low level connection state, nothing more) +- Internal: added PeerBase.ApplicationIsInitialized, which stores if the init command was answered by Photon (reset on connect/disconnect) +- Removed: PhotonDemoServerUrlPort and PhotonDemoServerIpPort of PhotonPeer. All demos now use "localhost:5055" and you should run your own server. +- Added: enum ConnectionProtocol to get rid of the "useTcp" parameter in the PhotonPeer constructor (which was less clear than the explicit enum now in use) +- Added: overload of PhotonPeer constructor, which is still compatible with the "useTcp" bool parameter (to avoid a breaking change for the time being) +- Added: PhotonPeer.UsedProtocol property to find out this peer's protcol +- Added: LitePeer.OpLeave() overload without the gameName parameter. That name is not checked in the Lite application (on the server), so it's not really needed + +*** Version 6.1.0 +- Added: Encryption for Unity and DotNet. Operations (and their responses) can be encrypted after exchanging the public keys with the server +- Added: OpExchangeKeysForEncryption(), DeriveSharedKey() and IsEncryptionAvailable to PhotonPeer (and LitePeer inherits these) +- Added: OpCustom() will throw an ArgumentException if the operation should be encrypted but keys are not yet exchanged (exchange keys first) +- Added: LiteOpCode.ExchangeKeysForEncryption = (byte)95 +- Added: Overloaded PhotonPeer.OpCustom() with new "encrypt" parameter +- Added: property PhotonPeer.IsEncryptionAvailable is true if public-keys are exchanged and the secret is compiled from them +- Added: Encryption demo to Realtime Demo. Press E to exchange keys and R to toggle encrypted sending for the move data (even though events are never encrypted) +- Changed: PeerBase methods: sendOperation()->EnqueueOperation(...,encrypt), updateRoundTripTimeAndVariance()->UpdateRoundTripTimeAndVariance() +- Updated: the Unity client is now a Unity v3.1 project. Make sure to change the server address before you build for iPhone (localhost:5055 won't work on the mobile) +- Removed: the outdated, separate iPhone demo (was: Unity v1.7 for iPhone) +- Updated: PhotonPeer documentation for Service(), DispatchIncomingCommands() and SendOutgoingCommands() +- Added: OpRaiseEvent() overload with parameter TargetActors. Sends optional list of actors that will receive the event (if null, all *other* actors will receive the event, as default) +- Internal: Added source BigInteger.cs, DiffieHellmanCryptoProvider.cs and OakleyGroups.cs +- Internal: PeerBase.CryptoProvider, PeerBase.ExchangeKeysForEncryption() and PeerBase.DeriveSharedKey() +- Internal: EnetPeer.initPhotonPeer() and TPeer.initPhotonPeer() are setting PeerBase.isEncryptionAvailable = false +- Internal: De/Serialization methods (and some variables for it) are moved from NConnect to PeerBase and renamed to: SerializeOperationToMessage() and DeserializeMessageAndCallback() +- Internal: switched project to allow "unsafe" functions (used by BigInteger) +- Internal: renamed PhotonPeer.sendOperation()->EnqueueOperation +- Internal: changed assembly version to 6.1.0 and "client version" in init-byte-block to 6,1,0 +- Internal: moved protocol handling to EnetPeer and TPeer classes (where encryption is added) +- Internal: moved InitBlock to (shared) PeerBase (same for UDP/TCP) +- Internal: serialization is now done by Protocol.SerializeOpParameters(), which excludes the message header. this makes encryption simpler + +*** Version 6.0.0 +- Changed: This library requires Photon v2.2.0 and up! (in other words: the libraries are not compatible with older Photon servers, due to servertime changes) +- Added: Support for arrays in arrays. Any serializable datatype can now be used in nested arrays. Even arrays of Hashtables are possible. +- Added: Realtime Demo optional command line arguments for game config. set all or none: serverAddress, useTcp (true/false), useReliable (true/false), int intervalDispatch, intervalSend (ms), intervalMove (ms) +- Note: Realtime Demo commandline might look like this: start demo-realtime.exe localhost:5055 false true 5 25 100 +- Changed: renamed GetLocalMsTimestamp property to LocalMsTimestampDelegate (it does not have a getter, despite the old name's implication) +- Added: PhotonPeer.LocalTimeInMilliSeconds property to use the timestamp delegate to get the current client milliseconds (by default this is Environment.TickCount) +- Changed: UDP: The default value for PhotonPeer.RoundTripTime (300ms, used before connecting) is now replaced with the turnaround time of connect. This should lead to accurate RTT values much sooner +- Changed: PhotonPeer.ServerTimeInMilliSeconds is no longer updated all the time. Instead it's fetched soon after connect (when initialization won't affect rountrips anymore) and extrapolated. It should be better to be off by a constant value than by a changing value +- Changed: PhotonPeer.ServerTimeInMilliSeconds now returns 0 until the server's timestamp is fetched. Updated the documentation with some internals for this. +- Added: PhotonPeer.FetchServerTimestamp() to send the time fetch command (this is done automatically as well. this method is here for completeness) +- Fixed: roundtrip time calculation is no longer affected by long intervals between Service() or DispatchIncomingCommands() calls (bug of v5.9.0, caused by internal action queues) +- Added: internally for UDP, we use a new command to fetch the timestamp which minimizes the latency for that roundtrip. this one is excluded in roundtrip time measuring +- Changed: internal: ACKs by the server are again directly executed (other commands which are put into the action queue and dispatched) +- Fixed: Peers with TCP as protocol will no longer try to disconnect while not being connected (does not do anything of disconnected or disconnecting) +- Changed: Peers with TCP as protocol will clear the outgoing queue when disconnect() is called (while connected. see fix above) +- Updated: Silverlight Realtime Demo slightly +- Added: PhotonPeer.Listener property to give subclasses access to the IPhotonPeerListener (set in constructor). Can be useful to call Listener.DebugReturn() +- Added: LitePeer-Source.cs to demo-realtime. This is the source of a LitePeer and could be used as sample to create custom operations on the client side + +*** Version 5.9.0 +- Release: of changes in 5.7.6 and 5.7.5 + +*** Version 5.7.6 +- Fixed: a debug output line for TCP connections which did not heed the debug-level. +- Changed: PhotonPeer uses less locking internally and will handle incoming data in the game thread (inside DispatchIncomingCommands() or Service()). +- Changed: Internally, all commands are put into a (locked) queue which is processed within DispatchIncomingCommands(). Your dispatch interval affects local lag but not the PhotonPeer.RoundTripTime value. +- Note: Don't use a peer from multiple threads! It's not thread safe. All callbacks to IPhotonPeerListener methods are happening in your game thread (again: inside DispatchIncomingCommands()). +- Changed: removed locks inside the callbacks (according to above change). +- Changed: DNS resolution is now done in Connect() unless you provide a valid IP address (if IPAddress.Parse(address) is successful, the IP is used directly). +- Fixed: PhotonPeer.Connect() should fail if the IP is unknown or unavailable. Exception: using a localhost might succeed but fail when we try to receive anything. +- Updated: Game.cs now initialized the timing intervals. This avoids issues if the client system is having a negative TickCount. +- Added: ServerAddress property to PhotonPeer, which might help while developing with several servers and peers. +- Changed: This version includes GetLocalMsTimestampDelegate and the PhotonPeer property GetLocalMsTimestamp to set the delegate for local timestamp. + +*** Version 5.7.5 +- Changed: All precompiled demos now connect to localhost! From now on, you need to run Photon before trying any of the demos (as we don't guarantee that udp.exitgames.com is online anyways) +- Changed: OpCustom() now accepts null as parameter Hashtable, which is a shortcut to "no parameters" for simple operations (an empty hashtable is sent though, it does not reduce bandwidth) +- Added: new feature: UDP timeout definition by setting PhotonPeer.DisconnectTimeout (individual per command, set in milliseconds, checked when a command is repeated) +- Renamed: enum ReturnCode to StatusCode. The StatusCode values are only used for status callbacks (not as operation results) +- Changed: parameter type of PeerStatusCallback() from int to StatusCode (to differentiate them from operation ReturnCodes, which are customizable) +- Removed: StatusCode.Ok (as it was actually an Operation ReturnCode) +- Added: new StatusCallback value: StatusCode.SendError. Used for sending error cases: "not connected" and "channel not available" +- Changed: sendOperation() (Udp and Tcp) does not throw an exception while disconnected or for wrong channel (using StatusCode.SendError instead) +- Changed: callback DebugReturn() now has the additional parameter (DebugLevel)level, analog to logging +- Changed: UDP connection is disconnected when a read exception happens (before we tried to read despite this until a timeout ended it) +- Changed: EnetPeer.Disconnect() now ignores calls when peer is disconnected or disconnecting already +- Fixed: TCP code tried to detect socket issues by checking for IOExceptions but now checks SocketException instead +- Changed: internal threading: Callbacks due to incoming packages and commands are now queued and triggered by dispatch (in game loop) +- Changed: dispatch of action-queue as added to DispatchIncomingCommands (in EnetPeer and TPeer) +- Changed: internally, there is no locking for outgoing reliable and unreliable command lists anymore +- Changed: Realtime Demo timer usage to avoid nullref on form-close +- Changed: Realtime Demo propety isReliable is now in the Player class +- Changed: Game.cs and Player.cs for all realtime demos. There is now something like a gameloop (Update()) which must be called regularly and makes (pretty) sure just one thread accesses the peer +- Changed: all realtime demos to use the new Update() method and use more similar Game and Player classes (cleanup for less differences) +- Fixed: RoundtripTimeVariance is now also reset on connect / init, so the resend-timing of reliable udp does not suffer when a peer connects after a disconnect +- Fixed: typo in ExitGames.Client.Photon.StatusCode.QueueIncomingUnreliableWarning (was QueueIncomingUneliableWarning) + +*** Version 5.7.4 RC3 +- Changed: Unity3D lib again has it's own UDP handling (the DotNet one causes browser crashes on web-player exit) + +*** Version 5.7.3 RC3 +- Changed: Unity3D lib is now identical to DotNet lib (Unity iPhone is compatible with DotNet 2.0 now and this got tested) +- Fixed: DNS resolution (did not work for "localhost", which gave two results (IPv4 and IPv6), mixing up things + +*** Version 5.7.2 RC3 +- Changed: Unity3D lib: the receive thread will now receive until no data is available, then sleep 5ms and check again +- Changed: serverTime is now a signed int (as on server) and adds averaged rountripTime/2 when it gets an update +- Changed: ServerTimeInMilliSeconds doc (more concrete, explains how server time works) +- Added: support for serverTime, RountripTime and RoundtripTimeVariance when using TCP (Silverlight does not allow UDP) +- Added: Silverlight supports either URL:Port and IP:Port as server url string + +*** Version 5.7.1 RC2 +- Added: DotNet "Lobby Demo" which uses the "LiteLobby" application of the server SDK to show running games and their player-count +- Changed: the realtime demos to use the more similar Game and Player classes + +*** Version 5.7.0 RC1 +- Added: documentation: project for Silverlight Hashtable and ArrayList substitutes. +- Changed: RealtimeDemo uses same classes Game and Player for Unity3 + Silverlight +- Changed: Silverlight: Hashtable and ArrayList are now a separate project / lib +- Internal: Silverlight: listener interfaces (Photon and Neutron) now conditionally use ExitGames.Client datatypes from lib +- Changed: Photon: connect callback is now deferred to on-init-response (instead of enet-connect) which ensures "no ops before init" +- Changed: Unity Realtime demo: using game and player classes merged over from silverlight and re-wrote sample code to display players +- Internal: photon projects now have a pre-compile setting "Photon" +- Changed: SupportClass Namespace is now compiling into either ExitGames.Client .Photon or .Neutron (to avoid ambiguation) +- Added: LitePeer as Lite Application specific peer (with OpJoin and the rest) +- Changed: demos accordingly +- Changed: case of PhotonPeer methods to first-letter-is-uppercase (as usual in C#) +- Removed: nNet-prefix (Connect and Disconnect are self-explanatory) +- Renamed: PropertyTypes are now LitePropertyTypes (as they belong to the Lite application) +- Changed: Peer state constants with PS_* converted into enum "PeerStateValue" +- Removed: URL_RT_SERVER, URL_RT_SERVER_DEV, IP_RT_SERVER and IP_RT_SERVER_DEV +- Added: PhotonDemoServerUrlPort and PhotonDemoServerIpPort +- Renamed: NPeer to PhotonPeer +- Renamed: PhotonPeerListener to IPhotonListener (class and file) +- Changed: namespace from Com.ExitGames to ExitGames and ExitGames.Client, ExitGames.Client.Photon and ExitGames.Client.Neutron +- Removed: QueueOutgoingUnreliableError, QueueOutgoingAcksError, QueueIncomingReliableError, QueueIncomingUneliableError, QueueSentError (no errors, only warnings) +- Removed: error "report" when TCP incoming queue getts fuller +- Internal: updates Neutron part to run with Protocol.cs de/serialization (added a serializeParametersNeutron() as there are multiple differences to UDP part) +- Changed: projects and scripts to build documentation xml in debug builds +- Renamed: demo-photon-SL to demo-realtime-SL (according to other demo realtime implementations) +- Changed: many classes and properties are now internal. e.g. Protocol, EnetChannel, EnetPeer (and inner classes), TPeer, SuppportClass.ReadInput() +- Updated: AssemblyInfo.cs for photon dotnet and silverlight +- Internal: projects to have precompile-flags also in release builds +- Updated: build scripts for SDK building +- Removed: Compact Framework support + +*** Version 5.6.1 +- Fixed: 0 element arrays caused bugs +- Fixed: double type was cast incorrectly after being read + +*** Version 5.6.0 +- Added: more supported datatypes: float, double and arrays of all basic datatypes (no arrays of hashtable or arrays) +- Internal: changed Photon protocol internally to 1.5. (needs a server update to Photon Server SDK 1.6.1+)! +- Changed: Channels for Photon UDP are now priorized (from low to high) getting the lower channels out first +- Internal: switched de/serialization at several places from manual shifting to a support function, which should provide endian-correctness (Photon Unity PPC compatibility) +- Added: Unity info about "Application.runInBackground = true;" to Unity Appendix in doc +- Changed: Photon return values are put into a NEW hashtable on receive. not just a cleared one which was not reference-safe (no more need to deep-copy the data of events) +- Added: Photon support for "disconnect-reason" which is sent by server in the enet "reserved" byte +- Added: Photon ReturnCode.DisconnectByServerUserLimit and .DisconnectByServerLogic +- Removed: NPeer.IncomingReliableCommands (was more or less useless) +- Added: QueuedIncomingCommands and QueuedOutgoingCommands as metric for how effective send and dispatch is done +- Changed: now throwing exceptions when trying to set init-values at runtime (to be fixed at development-time) +- Added: doc for sequencing and updated channel doc, (too) short chapter on custom operations, topic "opCodes: byte versus short", doc for property-related functions +- Added: overloaded functions for opGetProperties*() for byte-keys +- Fixed: Realtime Demo keypress in input-fields have been used as in-game actions, too +- Changed: Realtime Demo game-name is now that of the native samples ("play" with other platform SDKs) +- Changed: Silverlight SDK has a different port in the constants NPeer.URL_RT_SERVER* and .IP_RT_SERVER* (as Silverlight uses TCP port 4350) + +*** Version 5.4.1 +- Added: missing documentation in Unity3d SDK + +*** Version 5.4.0 +- Change: The timespan until a sent and unacknowledged reliable command is considered lost, is now calculated by + current roundTripTime + 4 * roundTripTimeVariance + The result of this calculation is doubled with every following resend. The maximum number of retries can still be defined by calling SetSentCountAllowance. +- Change: Removed TimeAllowanceInt +- Change: removed surplus debug out, adjusted levels for other, output of command sent-time from hex to decimal +- Added: fragmentation support: bigger data is now placed into multiple packages and reassembled +- Internal: command-buffers are replaced with CommandList and SortedCommandList (major change, but fully internal) +- Fixed: possibility of command buffer overflow. now everything is stored and warnings are used as hint for temporary problems +- Added: property NPeer.IncomingReliableCommands, which returns the count of reliable commands currently queued +- Added: callback on NCommand.CT_DISCONNECT to inform the NPeerListener about a disconnect from server (see above) +- Added: disconnect command will be sent by server in case of timeout, connection-limitations or other issues +- Added: NPeer ReturnCode.DisconnectByServer is called on server-side disconnect (see description) +- Added: call to StopConnection() on disconnect (by server) +- Added: NPeer.PeerID property to get ENet's peerID (useful while debugging) +- Internal: SupportClass.WriteIntToByteArray() to ease writing ints to byte[] +- Internal: added several values to NCommand to store fragments +- Added: support for channels. read more about this in the documentation +- Added: NPeer.ChannelCount which sets the number of channels while not connected (default: 2) +- Changed: opRaiseEvent() and opCustom() now optionally have a channel parameter +- Added: Photon properties functions to NPeer (available with Photon Server SDK v1.5.0) and doc +- Added: LiteEventKey.SetProperties = 92 for broadcasted property set +- Added: LiteOpKey.Broadcast = 13 and .Properties = 12 +- Added: LiteEventKey.TargetActorNr = 10 (actorNr the properties are attached to) and .Properties = 12 (changed properties) + + +*** Version 5.3.11 +- Change: all bytes sent to and from server are treated as unsigned bytes (standard for c#). same for byte-arrays +- Change: updated realtime demo to use int for posx,posy but still sending just a byte-value (the field is 16x16, after all) + +*** Version 5.3.10 +- Change: switched from sbyte-array to byte-array in de/serialization! important: bytes (ev-keys etc) are sbyte. arrays of bytes are unsigned (on client and server) +- Change: NeutronListener functions getShadowReturn() and HasbadwordsReturn() now have byte-array return values. please adjust, even if you don't use those +- Internal: changed SupportClass for Compact Framework +- Internal: getting ticks sitched from expensive "System.DateTime.Now.Ticks / 10000" to cheap "Environment.TickCount" +- Change: Unity lib will now give more debug out if serialisation fails + +*** Version 5.3.9 +- Fixed: result-queue, timeouts and customOps work also fine for Unity build again (were broken due to Neutron Unity webplayer compatibility changes in 5.3.8 for Unity) +- Fixed: if the browser is closed and the unity webplayer immediatly can't use http anymore, Neutron now informs the application via NetworkStatusReturn() + +*** Version 5.3.8 +- Fixed: Neutron Unity now also works fine in webplayer -> Neutron and Photon now both support all platforms of Unity und Unity iPhone +- Fixed: default value for parameter encrypt of NeutronGame::RaiseEvent() now is false like for all other RaiseEvent methods and like on all other platforms, instead of true, as it was before + +*** Version 5.3.7 +- Fixed: .Net UDP issue, where standard MTU settings caused dropped UDP packages +- Internal: refactored ACK queue to arraylist + +*** Version 5.3.6 +- Fixed: NPeer issue with ACKs for repeated commands. this enhances handling of lost packages +- Changed: NPeer.opJoin() no longer needs the SID + +*** Version 5.3.5 +- Known issues: to use Photon on iPhone device, you do need Unity iPhone 1.0.2b1 or higher (current official release is 1.0.1, so please ask for a prerelease or wait until next official release), but of course you can use Photon with Unity iPhone 1.0.1 IDE +- Merged: renamed .NET 1.1 NeutronUnity3DiPhone into NeutronUnity3D to replace the old .NET 2.0 lib of that name, which means, that you can use the same .NET 1.1 based lib for Unity and for Unity iPhone now, since 1.1 cpmpatibility fixes are all done now +- Fixed: photon is fully compatible to .NET 1.1 now +- Internal: optimized UDP package size in Unity3D library (was sending surplus bytes, which were ignored) +- Fixed: NPeer.opCustom() now sends the operation given by parameter +- Changed: IP_RT_SERVER points to new server IP of udp.exitgames.com +- Changed: a new NeutronSession now clears the NetworkLoss state and the sendQueue +- Changed: timeout of a HTTP request to 10 seconds. it triggers + +*** Version 5.3.4 +- Added: prealpha Unity3DiPhone version of Neutron .NET: core lib already functional, but realtime part not usable on device yet +- Internal: there are 4 different versions of Neutron.NET now: + - Full .NET: .NET 2.0 based, with asnyc realtime part + - Compact Framework: .NET 2.0 based, with threaded realtime part + - Unity3D: .NET 2.0 based, with Unity www-class based http part and threaded realtime part + - Unity3DiPhone: .NET 1.1 based, with Unity www-class based http part and threaded realtime part + +*** Version 5.3.3 +- New: ReturnCode.RC_RT_EXCEPTION_CONNECT, which covers the cases where a server is not running +- New: NPeer can now be created with UDP or TCP (by new bool parameter) +- Change: renamed most of the constants for NPeer (in INPeerListener structs) +- Note: TCP does not offer ServerTime or RoundTripTime jet + +*** Version 5.3.2 +- Internal: reverted to threaded model in NConnect (as async UDP is not supported by Unity3D) + +*** Version 5.3.1 +- New: login(), register(), customOperation() and raiseEvent() (all variants) can be encrypted with additional parameter "encrypt" (overloaded function) +- New: encryption uses HTTPs as transfer, by changing the "http:" url to a "https:" url +- New: returnCode for failure of encrypted HTTPs requests: RC_SSL_AUTHENTICATION_FAILED (if certificate is not found, valid or expired) +- Fixed: Realtime Demo using the older Realtime Server + +*** Version 5.3.0 +- New: separated libraries into "Compact Framework" (CF) and "Regular Framework" (no name postfix) +- Change: libraries are now in "libs" folder as debug/release and in libs/CompactFramework debug/release +- Change: libs default URL set to EU/Test. use setServerURL() with Neutron.URL_NEUTRON_* for other Neutron instances +- Internal: lib now uses async UDP communication now with "regular" framework +- Added: properties serverTimeInMilliSeconds, serverTimeAsTimeSpan and serverTimeAsDateTime for getting the current server time +- Removed: serverTimeOffset is now internal only and removed from the API (was only needed to calculate the servertime by yourself, before neutron could do this for you) +- Change: debug out for realtime classes is now layered +- Change: debug level NPeer.DebugOut is now a NPeer.DebugLevel enum and will include all lower levels in output, default: DebugLevel.ERROR +- Fixed: size of realtime demo board +- Change: NPeer constructor now always throws an exception if listener is null +- Change: EventAction() parameter eventCode is now of type sbyte (was int), which corresponds to type of RaiseEvent (and server-side used type) +- Internal: NPeer.opRaiseEvent() now treats eventCode as parameter of operation RaiseEvent (as changed in latest RT server) +- Change: NPeer has its own listener (INPeerListener) and several (better named) structs for the constants used with NPeer / realtime +- Added: LiteOpKey and LiteOpKey.ActorNumber to have a constant for the only OP key of interest +- Change: EventAction() always returns the complete event, which contains a code, the ActorNumber (if any given) and data from raiseEvent (see below) +- Change: in custom events, the data from opRaiseEvent() is in placed as value of key: LiteEventKey.EV_RT_KEY_DATA. to get the data use: Hashtable data = (Hashtable)neutronEvent[LiteEventKey.EV_RT_KEY_DATA]; + +*** Version 5.2.0 +- changed library filename to neutron-lib__.dll with server "test" and "run" (no debug out) and billing "dummy" and "none" +- removed US build of library. please use NeutronSession.SetServerUrl() and the constants: Neutron.URL_NEUTRON_SERVER_*. + +*** Version 5.1.0 +- added realtime classes to DotNet library: ported NPeer (and internal: NCommand and NConnect) classes +- added nPeerReturn() to NeutronListener interface +- added constants for realtime returnCodes (RC_RT_*): RC_RT_CONNECT, RC_RT_DISCONNECT and RC_RT_EXCEPTION +- added constants for realtime eventCodes (EV_RT_*) +- added constants for Neutron servers to Neutron class: URL_NEUTRON_* +- added Reamtime Demo +- updated samples +- added test for UDP to NUnit + +*** Version 5.0.1 +- New: operation Spectate (including new SpectateReturn) to get events from any game (as admin) +- New: SetServerUrl and SetCustomServerUrl now return the URL to debugReturn +- Internal: constant "DEBUG_- InternalS" to be used for intern debugging output + +*** Version 5.0.0 +- New: hasBadwords() as OP and return. Server side check of strings for badwords + +*** Version 5.0.0 RC3 +- Internal: changed constant values: EV_KEY_PROPERTIES = "Data", EV_KEY_REVISION = "Rev" +- New: EV_KEY_CHANNELTYPE for channel-type in property-change events +- New: constants for default channels, CHANNEL_APPLICATION_LONGLIFE, CHANNEL_ACTOR_SHORTLIFE, CHANNEL_ACTOR_LONGLIFE and CHANNEL_APPINSTANCE +- Change: operations that fail due to missing moderation-rights now return RC_MODERATION_DENIED instead of RC_COMMAND_ACCESS_DENIED +- Change: actor-properties can no longer be broadcasted in any way - removed "broadcast" parameter from setActorProperties() +- Change: properties now have a revision which is increased on each change. this way outdated updates might be skipped +- Change: parameters of GetPropertiesReturn(). property-type is replaced by channel. added revision +- Change: EV_PROPERTIES_CHANGE now has a key EV_KEY_OWNERNR if it's a "player property" (the key is missing if it's a game-property) +- Internal: changed setProperties and getProperties to new operation-codes using different parameters (with similar results) +- New: parameter "textMessage" for NeutronGame.invite() adds personal message to invited players (in EV_INV and gameListInvitations()) +- New: key EV_KEY_INFO will be added to EV_INV if "textMessage" was used in NeutronGame.invite() (it's not there otherwise) +- New: gameListInvitations() has new value parameter {t} to get "textMessage" from NeutronGame.invite() +- New: RC_APPINSTANCE_NOT_OPEN is now used for "singleton namebased pools" where a game is full (not able to join / instanciate) +- New: gameCreate() with invitations will fail if the chosen game-name is already taken in a "singleton namebased pool" +- New: RC_APPINSTANCE_ALREADY_EXISTS for the case above + +*** Version 5.0.0 RC2 +- Change: gameCreateReturn() now returns RC_APPINSTANCE_NOT_OPEN (instead of RC_AI_TOO_MANY_ACTORSESSIONS) for full games in "singleton" pools +- Change: obsolete events EV_TURN, EV_TXTMSG and EV_DATA which could be sent by raiseEvent*() and still handled +- Change: switched Neutron URLs to "[..].neutron5.[..]" for test/run libs +- Fix: Polling (getEvents operation) again calls sendGameDataReturn() for all errors (as intended for v4.9.2 already) +- New: constant NeutronListener.EV_KEY_TYPE as part of event EV_BUDDNOTICE + +*** Version 5.0.0 RC1 +- New: RaiseEvent (all functions of this name) now has a "filter" parameter. If filter is true, all String-typed values in an event are badword filtered +- Change: signature of NeutronGame.raiseEvent(), NeutronGame.raiseEventInChannel(), NeutronSession.raiseEventInChannel(), NeutronSession.raiseEventForActor() start with: byte eventCode, Hashtable event, boolean filter +- Change: signature of NeutronSession.raiseEventForActor() is changed to "byte eventCode, Hashtable eventData, boolean filter, String userID, int minutesValid, byte maxTypeCount, byte channel" +- Change: NeutronGame.doModerate() is now isModerator() +- Change: moved GpOperation.SerializeData() and GpOperation.DeserializeData() to Neutron.SerializeData() and Neutron.DeserializeData(). +- New: errorCode RC_INVALID_TARGET and RC_PARAMETER_NOT_SUPPLIED added as constant. + +*** Version 4.9.3 +- New: Errors constants in NeutronListener: RC_FATAL_LOGIC, RC_MATCHMAKING_NOT_COMPLETED, RC_CHANNEL_ACCESS_VIOLATION +- New: for game-creation you can now reserve "spots", which are not filled up by Neutron matchmaking. players can be invited to fill the spots, or they can be deblocked later on +- New: Parameter reservedSpots in NeutronSession.gameCreate() +- New: NeutronGame.setReservedSpots() to modify the number of reserved slots (to make them available to matchmaking again, or block/reserve them) +- New: event EV_RESERVED_SPOTS will update the NeutronGame.reservedSpots value after a call to NeutronGame.setReservedSpots() +- New: NeutronSession.listBannedPlayers() gives you the list of banned players for a known game - only usable by "admin" users +- New: NeutronSession.unbanPlayer() is a modified "kick" operation which allows the respective user to join a certain game again - only usable by "admin" users +- New: the event invitation includes now the game's name (in the new key EV_KEY_NAME) +- New: NeutronSession.gameListPerPool() has now three options to sort the results: by game-name, player-count or "persistent games first" +- Removed: NeutronGame: handoverTurn(), sendData(), sendTextMsg(), getEventHistory() and getEventHistoryReturn(). Obsolete events: EV_TURN, EV_TXTMSG, EV_DATA. Session: getScorePosition()+getScorePositionReturn() +- Update: release_history.txt was updated from v4.0. All changes up to v4.0.4 are added to v4.9.3 + +*** Version 4.9.2 +- New: Players can be admins (by list of logins on server) or moderator (by being the first active player of a game) +- New: Players may request and become moderator for game: NeutronSession.gameCanModerate(boolean), NeutronSession.canModerate, NeutronGame.doModerate() and NeutronGame.moderatorActorNr +- Change: the new value NeutronSession.canModerate will be sent with gameCreate() operations (if set to true) +- New: Event key NeutronListener.EV_KEY_MODERATOR to get moderator's actorNr from events +- Change: EV_QUIT and EV_KICKED now carry the new key EV_KEY_MODERATOR which tells all players who is the current moderator (by actorNr); this is stored into NeutronGame.moderatorActorNr +- New: Players in NeutronGame can have new state PLAYER_KICKED (player-data is updated with EV_KICKED) +- New: NeutronGame.kickPlayer() (for moderators) and NeutronSession.kickPlayer() (for admin's who are not active in the game to shutdown) +- New: NeutronSession.shutdownGame() can be used by admin-players (for others, this operation will fail) +- New: Namebased pools can now be defined as "singleton": only one instance per pool and name will be created; if such a game is full players get an error instead of a new game +- New: Errors constants in NeutronListener: RC_ACTORSESSION_KICKED, RC_ACTORSESSION_BANNED, RC_APPINSTANCE_CLOSED, RC_ACTORSESSION_ALREADY_JOINED +- Change: NeutronGame.raiseEvent() accepts a "targetActorNr" which defines a single player to get the raised event; leave 0 to target "all players in game" (as before) +- New: NeutronGame.quitLocally() to release a NeutronGame instance locally (without having to quit()); used after a player was kicked or game shutdown +- Update: NeutronGame.playerGetCount() is updated to simply count all active or inactive players (excluding quit and kicked ones) +- Internal: NeutronGame constructor reads new parameter: P_MODERATOR +- Change: Polling (getEvents operation) now calls sendGameDataReturn() for all errors (not just RC_ACTORSESSION_EXPIRED and RC_ACTORSESSION_NOT_FOUND); takes care of kicked/banned errors +- Fix: Fatal server errors cause a returnCode of NeutronListener.RC_OP_SERVER again; debug test-server libs print out debug text! (during development fatal errors could happen in case of not matching client/server setups) +- Change: removed (already deprecated) NeutronListener.gameListPerPoolReturn() +- Change / Internal: canModerate is sent as Byte (not bool) as in definition; Code: if ( canModerate ) op.addParameter(Neutron.P_MODERATOR , new Byte((byte)1)); +- Add: NeutronGame.PLAYER_KICKED is now listed in JavaDoc for NeutronGame.playerGetStatus() +- Update: JavaDoc package.html, gameCreateReturn(), gamesListReturn(), EV_DEACTIVATE, kickPlayer(), quitLocally(), RC_ACTORSESSION_KICKED, RC_ACTORSESSION_BANNED, RC_APPINSTANCE_CLOSED, RC_ACTORSESSION_ALREADY_JOINED +- Added: Event EV_STATUS (50) includes a key EV_KEY_ISADMIN if the current player has administrator rights; the value is (byte)1 in that case. The key does not exist in any other case (normal users) +- Update: JavaDoc gameCreateReturn; +- New: Added constant RC_APPINSTANCE_NOT_FOUND = 137 for shutdownGameReturn() +- Fix: serializable datatypes are now completely listed in NeutronSession JavaDoc +- New: Constant for property-change events: EV_PROPERTIES_CHANGE including new keys: EV_KEY_PROPERTY_TYPE, EV_KEY_PROPERTIES, EV_KEY_ISDIFF +- Update: JavaDoc for properties in NeutronSession + +*** Version 4.1.1 +- Fix: gameListPerPool() defaults to 10 games and no offset if the values are less than 1 +- Fix: gamesListReturn() JavaDoc description for "listType" is now: 0 = open games; 1 = invitations; 2 = pool's open games list +- Update: gameListPerPool() sends "{gn}" as values-parameter if it's null +- Update: getPropertiesReturn() gets new parameters: actorNr, userID. These are optional and are available in certain situations only. See JavaDoc +- Update: gameListPerPoolReturn() is now deprecated and merged into gamesListReturn() which in turn got a "type" to identify the list-type +- New: getListBuddyIgnore() got one more value: 't'. This requests the type of relation to users. useful when getting lists of type "both". this is buddies and ignores. +- Change: renamed returned parameters to: count and countOnline. These values are referring to the number in the returned list +- Internal: parameter P_USERID = 85; used in getProperties +- New: made methods nullpointer resistant: getListBuddyIgnore, buddySet, get/set PlayerProperties, get/set ActorProperties, get/set GameProperties; some methods throw exceptions in debug version + +*** Version 4.1.0 +- New: Properties. NeutronSession: setActorProperties(), getActorProperties(). NeutronGame: setLocalPlayerProperties(), getPlayerProperties(), getGameProperties(), setGameProperties() +- New: Buddylist and Ignorelist in NeutronSession: listBuddies(), listIgnored(), getListBuddyIgnore(), buddySet() +- New: Listing of games per pool in NeutronSession: NeutronSession gameListPerPool() +- New: Games with password (only usable for named games) +- Internal: Changed parameter in buddySet from P_STATUS to P_TYPE + +*** Version 4.0.4 +- Change: NeutronGame.handoverTurn() and NeutronGame.sendData() are now getting a Hashtable parameter instead of Object +- New: RC_ACTORSESSION_BUSY (121) constant to help identify common development error! check in gameCreateReturn() + +*** Version 4.0.3 +- New: RC_INVALID_CONNECTIONSTRING (74) constant to help identify a common error! check in loginReturn() +- Update: list of serializable datatypes in NeutronSession JavaDoc +- Fix: Fatal server errors cause a returnCode of NeutronListener.RC_OP_SERVER again; debug test-server libs print out debug text! (during development fatal errors could happen in case of not matching client/server setups) + +*** Version 4.0.2 +- Internal: Neutron.deserializeData() now returns either the P_DATA part of the deserialized data (if available / serialized by serializeData()) or the resulting hashtable itself + +*** Version 4.0.1 +- New: NConnectSE connects to server defined by parameter: ipPort (before: fixed host) +- New: SE version is now completely independent from Java ME classes (were not used, but had to be present) +- Fix: Changed versioning for "ClientLibVersion" in Login/CC +*** Version 4.0.0.0 + +- Removed methods: + - NeutronSession.BuggyGetList - replaced by new GetListBuddyIgnore method; + - NeutronSession.ReSubscribe; + - NeutrinSession.ConfirmBilling; + - NeutronListener.ResubscribeReturn; + +- Added methods: + - NeutronSession.GameCreateNamed with password parameter; + - NeutronSession.GameListPerPool; + - NeutronSession.GetActorProperties; + - NeutronSession.SetActorProperties; + - NeutronSession.GetListBuddyIgnore - replaces removed BuggyGetList; + - NeutronSession.ListBuddies; + - NeutronSession.ListIgnore; + - NeutronSession.BillingInitPayment; + - NeutronSession.BillingProcessPayment; + - NeutronGame.Invite; + - NeutronGame.GetGameProperties; + - NeutronGame.SetGameProperties; + - NeutronGame.GetPlayerProperties; + - NeutronGame.SetLocatPlayerProperties; + - NeutronListener.GameInviteReturn; + - NeutronListener.GetPropertiesReturn; + - NeutronListener.SetPropertiesReturn; + +- Changed argument list: + - NeutronSession.GameCreate - added password parameter; + - NeutronListener.GamesListReturn added listType parameter; + - NeutronListener.BuddyGetListReturn all buddy related info now in passing in one strings array parrameter; + - NeutronListener.BuddySetReturn added type parameter; + - NeutronListener.BillingInitPaymentReturn; + + +- added constants: + - OPC_INVITE + - OPC_TELLAFRIEND + - OPC_LISTGAMES + - OPC_SETPROPERTIES + - OPC_GETPROPERTIES + - P_USERID + - P_RESERVE + - P_RESULT + - P_PROPERTIES + - P_BROADCAST + - P_ISDIFF + - RCB_CHARGING_ERROR + - RCB_POST_CHARGING_ERROR + - RCB_TIMEOUT + - RCB_PRICE_- Changed + - RCB_PRICE_INVALID + - RCB_FATAL_SERVER_ERROR + - RCB_FATAL_LOGIC_ERROR + - RCB_NOT_INCLUDED + - RCB_WMA_UNAVAILABLE + +*** Version 3.0.2.2 +- CLS-specifications largely corrected + +*** Version 3.0.1.1 +- changes in neutron-java-lib integrated + +*** +- Removed: NeutronGame: playerNames, playerIDs, playerLobbies, playerStats +- Change: removed GpOperation.roundtripTime, now using public Neutron.roundtripTime + to be sent in operation headers (GpOperation.serializeParameters(), internal) +- Change: channelRaiseEvent() is now raiseEventInChannel() and gets the eventCode + as seperate parameter value - analog to raiseEventForActor() +- Fix: renamed EV_KEY_M_MIPLAYERS to EV_KEY_M_MINPLAYERS (number of min players of game, before start) +- Fix: values for EV_KEY_M_MINPLAYERS and EV_KEY_M_MAXPLAYERS corrected (wrong case so far) +- Changed: Neutron.millisecondsToWait (current value of polling-interval) is now + set in Neutron.receiveResponse() for login, register and alike diff --git a/Assets/Photon/PhotonLibs/changes-library.txt.meta b/Assets/Photon/PhotonLibs/changes-library.txt.meta new file mode 100644 index 0000000..a6c47ec --- /dev/null +++ b/Assets/Photon/PhotonLibs/changes-library.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d8040d310df77714a90a561261bfb2cb +timeCreated: 1557919981 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/PhotonLibs/netstandard2.0.meta b/Assets/Photon/PhotonLibs/netstandard2.0.meta new file mode 100644 index 0000000..f89b909 --- /dev/null +++ b/Assets/Photon/PhotonLibs/netstandard2.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 553c448718eda6f479d617a657fd78f5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll new file mode 100644 index 0000000..3c8b6c5 Binary files /dev/null and b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll differ diff --git a/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll.meta b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll.meta new file mode 100644 index 0000000..e507b85 --- /dev/null +++ b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.dll.meta @@ -0,0 +1,165 @@ +fileFormatVersion: 2 +guid: 6a558ae793155af4b9b9ab945fc64a0f +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + '': Any + second: + enabled: 0 + settings: + Exclude Android: 0 + Exclude Editor: 0 + Exclude GameCoreScarlett: 0 + Exclude GameCoreXboxOne: 0 + Exclude Linux: 0 + Exclude Linux64: 0 + Exclude LinuxUniversal: 0 + Exclude Lumin: 0 + Exclude OSXUniversal: 0 + Exclude PS4: 0 + Exclude PS5: 0 + Exclude Switch: 0 + Exclude WebGL: 0 + Exclude Win: 0 + Exclude Win64: 0 + Exclude WindowsStoreApps: 0 + Exclude XboxOne: 0 + Exclude iOS: 0 + Exclude tvOS: 0 + - first: + Android: Android + second: + enabled: 1 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 1 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 1 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: LinuxUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 1 + settings: + CPU: AnyCPU + - first: + WebGL: WebGL + second: + enabled: 1 + settings: {} + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 1 + settings: + CPU: AnyCPU + DontProcess: false + PlaceholderPath: + SDK: AnySDK + ScriptingBackend: Il2Cpp + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CompileFlags: + FrameworkDependencies: + - first: + PS4: PS4 + second: + enabled: 1 + settings: {} + - first: + PS5: PS5 + second: + enabled: 1 + settings: {} + - first: + Nintendo Switch: Switch + second: + enabled: 1 + settings: {} + - first: + XboxOne: XboxOne + second: + enabled: 1 + settings: {} + - first: + GameCoreScarlett: GameCoreScarlett + second: + enabled: 1 + settings: {} + - first: + GameCoreXboxOne: GameCoreXboxOne + second: + enabled: 1 + settings: {} + - first: + Lumin: Lumin + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml new file mode 100644 index 0000000..f321902 --- /dev/null +++ b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml @@ -0,0 +1,2766 @@ + + + + Photon3Unity3D + + + + + Initializes a new instance of the class. + + + + + Gets the public key that can be used by another DiffieHellmanCryptoProvider object + to generate a shared secret agreement. + + + + + Derives the shared key is generated from the secret agreement between two parties, + given a byte array that contains the second party's public key. + + + The second party's public key. + + + + Interface for Photon's DiffieHellman/Payload Encryption. + + + + Provides classical Diffie-Hellman Modular Exponentiation Groups defined by the + OAKLEY Key Determination Protocol (RFC 2412). + + + + + Gets the genrator (N) used by the the well known groups 1,2 and 5. + + + + + Gets the 768 bit prime for the well known group 1. + + + + + Gets the 1024 bit prime for the well known group 2. + + + + + Gets the 1536 bit prime for the well known group 5. + + + + A slice of memory that should be pooled and reused. Wraps a byte-array. + + This is a serializable datatype for the .Net clients. It will serialize and transfer as byte[]. + If PhotonPeer.UseByteArraySlicePoolForEvents is enabled, byte-arrays in (incoming) events will be deserialized as + ByteArraySlice. + + Adjust your OnEvent code accordingly. + + + + The buffer for the slice. + + + The position where the content starts in Buffer. + + + The length of the data in the Buffer. + + + + Internal constructor - these instances will be part of the pooling system. + + The pool to return to. + The index to return to (in the related returnPool). + + + + Create a new ByteArraySlice. The buffer supplied will be used. Usage is similar to ArraySegment. + + Not part of pooling. + + + + Creates a ByteArraySlice, which is not pooled. It has no Buffer. + + Not part of pooling. + + + + If this item was fetched from a ByteArraySlicePool, this will return it. + + + True if this was a pooled item and it successfully was returned. + If it does not belong to a pool nothing will happen, and false will be returned. + + + + Resets Count and Offset to 0 each. + + + Tiered pool for ByteArraySlices. Reduces the allocations once enough slices are available. + + + + Requests for buffers smaller than 2^minStackIndex will use 2^minStackIndex. This value avoids allocations of smaller rarely used buffers. + Set this to a lower value if you will never need sizes larger than byte[2^minStackIndex] + + + + Count of allocations this pool did. + + + Creates a new pool. + + + + Get a ByteArraySlice from pool. This overload handles user supplied byte[] and byte count and can be used as a non-boxing alternative to ArraySegment<byte>. + + + This effectively pools the ByteArraySlice instances but not their data. + ByteArraySlice.Release() will return the slice itself to the pool but delete the reference to the buffer supplied here. + + + + + Get byte[] wrapper from pool. This overload accepts a bytecount and will return a wrapper with a byte[] that size or greater. + + + + Pops a slice from the stack or creates a new slice for that stack. + The stack to use. Lock that stack before calling PopOrCreate for thread safety. + + A slice. + + + + Releasing a ByteArraySlice, will put it back into the pool, if it was acquired from one. + + The ByteArraySlice to return to the pool. + The stackIndex for this slice. + True if this slice was returned to some pool. False if not (null or stackIndex < 0. + + + + Clears all pool items with byte array sizes between lower and upper inclusively. + + + Use this if you sent some unusually large RaiseEvents and believe the buffers of that size + will not be needed again, and you would like to free up the buffer memory. + + + + + Replacement for Dictionary<K,V> which does not allocate memory during usage. + + Key type. + Value type. + + + + This is a substitute for the Hashtable class, missing in: Win8RT and Windows Phone. It uses a Dictionary<object,object> as base. + + + Please be aware that this class might act differently than the Hashtable equivalent. + As far as Photon is concerned, the substitution is sufficiently precise. + + + + + Translates the byte key into the pre-boxed byte before doing the lookup. + + + + + + + Creates a shallow copy of the Hashtable. + + + A shallow copy of a collection copies only the elements of the collection, whether they are + reference types or value types, but it does not copy the objects that the references refer + to. The references in the new collection point to the same objects that the references in + the original collection point to. + + Shallow copy of the Hashtable. + + + + Used as container for unknown types the client could not deserialize. + + + + + The type code which was read for this type. + + + + + The size/length value that was read for this type. + + May be larger than Data.Length, if the Size exceeded the remaining message content. + + + + Container for the data that arrived. + + If the Size exceeded the remaining message length, only the remaining data is read. This may be null, if the size was somehow less than 1. + + + Interface for DatagramEncryptor implementations. + + + Initialize the encryptor. + + + + Encryption/decryption algorithm implementation + + + + + Packet authentication algorithm impelmenation + + + + Number for reliable unsequenced commands (separate from "standard" reliable sequenced). Used to avoid duplicates. + + + The highest number of reliable unsequenced commands that arrived (and all commands before). + + + Any reliable unsequenced number that's been received, which is higher than the current highest in complete sequence (reliableUnsequencedNumbersCompletelyReceived). + + + To store the highest acknowledged sequence number (and get some impression what the server already received and stored). + + + Checks and queues incoming reliable unsequenced commands ("send" or "fragment"), if they haven't been received yet. + The command to check and queue. + True if the command is new and got queued (or could be executed/dispatched). + + + Quick Resends are suspended if the sent queue is this size or larger. + + + One list for all channels keeps sent commands (for re-sending). + + + One pool of ACK byte arrays ( 20 bytes each) for all channels to keep acknowledgements. + + + Gets enabled by "request" from server (not by client). + + + Initial PeerId as used in Connect command. If EnableServerTracing is false. + + + Initial PeerId to enable Photon Tracing, as used in Connect command. See: EnableServerTracing. + + + + Checks the incoming queue and Dispatches received data if possible. + + If a Dispatch happened or not, which shows if more Dispatches might be needed. + + + Gets the target size for fragments. + + Caches the result for a specific MTU value. + Fragment length is different, when datagram encryption is used (so this caches two values in fact). + + + + + + gathers acks until udp-packet is full and sends it! + + + + Queue of received commands. ReceiveIncomingCommands will queue commands, except ACKs which Execute immediately. + + + + gathers commands from all (out)queues until udp-packet is full and sends it! + + + + + Checks connected state and channel before operation is serialized and enqueued for sending. + + if operation could be enqueued + + + reliable-udp-level function to send some byte[] to the server via un/reliable command + only called when a custom operation should be send + the invocation ID for this operation (the payload) + + + reads incoming udp-packages to create and queue incoming commands* + + + Queues incoming commands in the correct order as either unreliable, reliable or unsequenced. + If queued or not. + + + removes commands which are acknowledged + + + + Enumeration of situations that change the peers internal status. + Used in calls to OnStatusChanged to inform your application of various situations that might happen. + + + Most of these codes are referenced somewhere else in the documentation when they are relevant to methods. + + + + the PhotonPeer is connected.
See {@link PhotonListener#OnStatusChanged}*
+
+ + the PhotonPeer just disconnected.
See {@link PhotonListener#OnStatusChanged}*
+
+ + the PhotonPeer encountered an exception and will disconnect, too.
See {@link PhotonListener#OnStatusChanged}*
+
+ + Exception while opening the incoming connection to the server. Followed by Disconnect. + The server could be down / not running or the client has no network or a misconfigured DNS.
See {@link PhotonListener#OnStatusChanged}*
+
+ + Used on platforms that throw a security exception on connect. Unity3d does this, e.g., if a webplayer build could not fetch a policy-file from a remote server. + + + Sending command failed. Either not connected, or the requested channel is bigger than the number of initialized channels. + + + Exception, if a server cannot be connected. Followed by Disconnect. + Most likely, the server is not responding. Ask user to try again later. + + + Disconnection due to a timeout (client did no longer receive ACKs from server). Followed by Disconnect. + + + Timeout disconnect by server. The server didn't receive necessary ACKs in time. Followed by Disconnect. + + + Disconnect by server due to concurrent user limit reached (received a disconnect command). + + + (1043) Disconnect by server due to server's logic. Followed by Disconnect. + + + Disconnect by server due to unspecified reason. Followed by Disconnect. + + + (1048) Value for OnStatusChanged()-call, when the encryption-setup for secure communication finished successfully. + + + (1049) Value for OnStatusChanged()-call, when the encryption-setup failed for some reason. Check debug logs. + + + + Callback interface for the Photon client side. Must be provided to a new PhotonPeer in its constructor. + + + These methods are used by your PhotonPeer instance to keep your app updated. Read each method's + description and check out the samples to see how to use them. + + + + + Provides textual descriptions for various error conditions and noteworthy situations. + In cases where the application needs to react, a call to OnStatusChanged is used. + OnStatusChanged gives "feedback" to the game, DebugReturn provies human readable messages + on the background. + + + All debug output of the library will be reported through this method. Print it or put it in a + buffer to use it on-screen. Use PhotonPeer.DebugOut to select how verbose the output is. + + DebugLevel (severity) of the message. + Debug text. Print to System.Console or screen. + + + + Callback method which gives you (async) responses for called operations. + + + Similar to method-calling, operations can have a result. + Because operation-calls are non-blocking and executed on the server, responses are provided + after a roundtrip as call to this method. + + Example: Trying to create a room usually succeeds but can fail if the room's name is already + in use (room names are their IDs). + + This method is used as general callback for all operations. Each response corresponds to a certain + "type" of operation by its OperationCode. + + + + When you join a room, the server will assign a consecutive number to each client: the + "actorNr" or "player number". This is sent back in the operation result. + + Fetch your actorNr of a Join response like this: + int actorNr = (int)operationResponse[(byte)OperationCode.ActorNr]; + + The response to an operation\-call. + + + + OnStatusChanged is called to let the game know when asynchronous actions finished or when errors happen. + + + Not all of the many StatusCode values will apply to your game. Example: If you don't use encryption, + the respective status changes are never made. + + The values are all part of the StatusCode enumeration and described value-by-value. + + A code to identify the situation. + + + + Called whenever an event from the Photon Server is dispatched. + + + Events are used for communication between clients and allow the server to update clients anytime. + The creation of an event is often triggered by an operation (called by this client or an other). + + Each event carries a Code plus optional content in its Parameters. + Your application should identify which content to expect by the event's Code. + + Events can be defined and modified server-side. + + If you use the LoadBalancing api as basis, several events like EvJoin and EvLeave are pre-defined. + The LoadBalancing api provides the EventCode and ParameterCode classes for pre-defined events. + + Photon also allows you to come up with custom events on the fly, purely client-side. + To do so, use OpRaiseEvent. + + Events are incoming messages and as such buffered in the peer. + Calling PhotonPeer.DispatchIncomingCommands will call IPhotonPeerListener.OnEvent, to hand over received events. + + PhotonPeer.ReuseEventInstance is an option to optimize memory usage by reusing one EventData instance. + + The event currently being dispatched. + + + The protocol for this socket, defined in constructor. + + + Address, as defined via a Connect() call. Including protocol, port and or path. + This is set in the constructor and in Connect() again. Typically the address does not change after the IPhotonSocket is instantiated. + + + Contains only the server's hostname (stripped protocol, port and or path). Set in IPhotonSocket.Connect(). + + + Contains the IP address of the previously resolved ServerAddress (or empty, if GetIpAddress wasn't used). + + + Contains only the server's port address (as string). Set in IphotonSocket.Connect(). + + + Where available, this exposes if the server's address was resolved into an IPv6 address or not. + + + + Provides the protocol string, of the current PhotonPeer.SerializationProtocolType to be used for WebSocket connections. + + + Any WebSocket wrapper could access this to get the desired binary protocol for the connection. + Some WebSocket implementations use a static value of the same name and need to be updated. + + The value is not cached and each call will create the needed string on the fly. + + + + + Separates the given address into address (host name or IP) and port. Port must be included after colon! + + + This method expects any address to include a port. The final ':' in addressAndPort has to separate it. + IPv6 addresses have multiple colons and must use brackets to separate address from port. + + Examples: + ns.exitgames.com:5058 + http://[2001:db8:1f70::999:de8:7648:6e8]:100/ + [2001:db8:1f70::999:de8:7648:6e8]:100 + See: + http://serverfault.com/questions/205793/how-can-one-distinguish-the-host-and-the-port-in-an-ipv6-url + + + + Wraps a DNS call to provide an array of addresses, sorted to have the IPv6 ones first. + + This skips a DNS lookup, if the hostname is an IPv4 address. Then only this address is used as is. + The DNS lookup may take a while, so it is recommended to do this in a thread. Also, it may fail entirely. + + + IPAddress array for hostname, sorted to put any IPv6 addresses first.
+ If the DNS lookup fails, HandleException(StatusCode.ExceptionOnConnect) gets called and null returned. + Then the socket should not attempt to connect. +
+
+ + + Returns null or the IPAddress representing the address, doing Dns resolution if needed. + + Only returns IPv4 or IPv6 adresses, no others. + The string address of a server (hostname or IP). + IPAddress for the string address or null, if the address is neither IPv4, IPv6 or some hostname that could be resolved. + + + Variants of the Photon specific serialization protocol used for operations, responses, events and data. + + + Version 1.6 (outdated). + + + Version 1.8. + + + + + + + Serialize creates a byte-array from the given object and returns it. + + The object to serialize + The serialized byte-array + + + + Deserialize returns an object reassembled from the given StreamBuffer. + + The buffer to be Deserialized + The Deserialized object + + + + Deserialize returns an object reassembled from the given byte-array. + + The byte-array to be Deserialized + The Deserialized object + + + + Interface for (UDP) traffic capturing. + + + + Indicates if the PhotonPeer should call Record or not. + + + Implement to record network traffic. Called by PhotonPeer for each UDP message sent and received. + + The buffer will not contain Ethernet Header, IP, UDP level data. Only the payload received by the client. + + It is advised to not use NetworkSimulation when recording traffic. + The recording is done on the timing of actual receive- and send-calls and internal simulation would offset the timing. + + Buffer to be sent or received. Check length value for actual content length. + Length of the network data. + Indicates incoming (true) or outgoing (false) traffic. + The local peerId for the connection. Defaults to 0xFFFF until assigned by the Server. + The currently used IPhotonSocket of this Peer. Enables you to track the connection endpoint. + + + Internal class for "commands" - the package in which operations are sent. + + + Size of the Payload, which may be null. + + + Checks commandFlags & FV_UNRELIABLE_UNSEQUENCED. + + + Checks commandFlags & FV_RELIABLE. + + + + ACKs should never be created as NCommand. use CreateACK to wrtie the serialized ACK right away... + + + + + + + + + this variant does only create outgoing commands and increments . incoming ones are created from a DataInputStream + + + this variant does only create outgoing commands and increments . incoming ones are created from a DataInputStream + + + reads the command values (commandHeader and command-values) from incoming bytestream and populates the incoming command* + + + + A simulation item is an action that can be queued to simulate network lag. + + + + With this, the actual delay can be measured, compared to the intended lag. + + + Timestamp after which this item must be executed. + + + Action to execute when the lag-time passed. + + + Starts a new Stopwatch + + + + A set of network simulation settings, enabled (and disabled) by PhotonPeer.IsSimulationEnabled. + + + For performance reasons, the lag and jitter settings can't be produced exactly. + In some cases, the resulting lag will be up to 20ms bigger than the lag settings. + Even if all settings are 0, simulation will be used. Set PhotonPeer.IsSimulationEnabled + to false to disable it if no longer needed. + + All lag, jitter and loss is additional to the current, real network conditions. + If the network is slow in reality, this will add even more lag. + The jitter values will affect the lag positive and negative, so the lag settings + describe the medium lag even with jitter. The jitter influence is: [-jitter..+jitter]. + Packets "lost" due to OutgoingLossPercentage count for BytesOut and LostPackagesOut. + Packets "lost" due to IncomingLossPercentage count for BytesIn and LostPackagesIn. + + + + internal + + + internal + + + internal + + + internal + + + internal + + + internal + + + internal + + + This setting overrides all other settings and turns simulation on/off. Default: false. + + + Outgoing packages delay in ms. Default: 100. + + + Randomizes OutgoingLag by [-OutgoingJitter..+OutgoingJitter]. Default: 0. + + + Percentage of outgoing packets that should be lost. Between 0..100. Default: 1. TCP ignores this setting. + + + Incoming packages delay in ms. Default: 100. + + + Randomizes IncomingLag by [-IncomingJitter..+IncomingJitter]. Default: 0. + + + Percentage of incoming packets that should be lost. Between 0..100. Default: 1. TCP ignores this setting. + + + Counts how many outgoing packages actually got lost. TCP connections ignore loss and this stays 0. + + + Counts how many incoming packages actually got lost. TCP connections ignore loss and this stays 0. + + + Provides an overview of the current values in form of a string. + String summary. + + + + The pool this wrapper should return to when released/disposed. + + + + + Gets value and if it belongs to the static pool, returns the wrapper to pool. + + + + + + Boxes the value and returns boxed object. Releases the wrapper. + + + + + + Removes this WrapperStruct from pooling. + + + + Returns a String which represents the value of this instance. + String which represents the value of this instance. + + + Returns a String which represents the type (in brackets and value of this instance. + String which represents the type (in brackets) and value of this instance. + + + + staticPool is used for implicit casting. This is not threadsafe, so casting between T and StructWrapper should only be done on the Unity main thread. + + + + + Replacement for object.GetType() that first checks to see if object is a WrappedStruct. + If so returns the StructWrapper T type, otherwise just returns object.GetType(). + + + + + + + Wrap a struct in a pooled StructWrapper. + + + + + Wrap a struct in a pooled StructWrapper. Pulls wrapper from the static pool. Wrapper is returned to pool when Unwrapped. + Slighty faster version of Wrap() that is hard wired to pull from the static pool. Use the persistant bool argument to make a permanent unpooled wrapper. + + + + + Tests if object is either a cast T, or a wrapped T + + + + + Remove all wrappers in hashtable from pooling, so they can remain cached and used later. + + + + + + Unwraps any WrapperStructs, boxes their value, releases hashtable entry with the boxed value. Releases the wrappers. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is returned to the wrapper pool if applicable, so it is not considered safe to Unwrap multiple times, as the wrapper may be recycled. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is will not be returned to its pool until it is Unwrapped, or the pool is cleared. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is returned to the wrapper pool if applicable, so it is not considered safe to Unwrap multiple times, as the wrapper may be recycled. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is returned to the wrapper pool if applicable, so it is not considered safe to Unwrap multiple times, as the wrapper may be recycled. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is returned to the wrapper pool if applicable, so it is not considered safe to Unwrap multiple times, as the wrapper may be recycled. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is returned to the wrapper pool if applicable, so it is not considered safe to Unwrap multiple times, as the wrapper may be recycled. + + + + + If object is a StructWrapper, the value will be extracted. If not, the object will be cast to T. + Wrapper is will not be returned to its pool until it is Unwrapped, or the pool is cleared. + + + + + + + + + + + Will get the object using the key. If the key is invalid, will return null. + + + + + + Dictionary content as string. + If true, type-info is also included. + Full content of dictionary as string. + + + Param code. Used in internal op: InitEncryption. + + + Encryption-Mode code. Used in internal op: InitEncryption. + + + Param code. Used in internal op: InitEncryption. + + + Code of internal op: InitEncryption. + + + TODO: Code of internal op: Ping (used in PUN binary websockets). + + + Result code for any (internal) operation. + + + + This is the replacement for the const values used in eNet like: PS_DISCONNECTED, PS_CONNECTED, etc. + + + + No connection is available. Use connect. + + + Establishing a connection already. The app should wait for a status callback. + + + + The low level connection with Photon is established. On connect, the library will automatically + send an Init package to select the application it connects to (see also PhotonPeer.Connect()). + When the Init is done, IPhotonPeerListener.OnStatusChanged() is called with connect. + + Please note that calling operations is only possible after the OnStatusChanged() with StatusCode.Connect. + + + Connection going to be ended. Wait for status callback. + + + Acknowledging a disconnect from Photon. Wait for status callback. + + + Connection not properly disconnected. + + + The server's address, as set by a Connect() call, including any protocol, ports and or path. + If rHTTP is used, this can be set directly. + + + + This is the (low level) connection state of the peer. It's internal and based on eNet's states. + + Applications can read the "high level" state as PhotonPeer.PeerState, which uses a different enum. + + + Byte count of last sent operation (set during serialization). + + + Byte count of last dispatched message (set during dispatch/deserialization). + + + The command that's currently being dispatched. + + + This ID is assigned by the Realtime Server upon connection. + The application does not have to care about this, but it is useful in debugging. + + + + The serverTimeOffset is serverTimestamp - localTime. Used to approximate the serverTimestamp with help of localTime + + + + + Count of all bytes going out (including headers) + + + + + Count of all bytes coming in (including headers) + + + + Set via Connect(..., customObject) and sent in Init-Request. + + + Sent on connect in an Init Request. + + + Temporary cache of AppId. Used in Connect() to keep the AppId until we send the Init-Request (after the network-level (and Enet) connect). + + + Set to timeInt, whenever SendOutgoingCommands actually checks outgoing queues to send them. Must be connected. + + + Maximum Transfer Unit to be used for UDP+TCP + + + If IPhotonSocket.Connected is true, this value shows if the server's address resolved as IPv6 address. + + You must check the socket's IsConnected state. Otherwise, this value is not initialized. + Sent to server in Init-Request. + + + + + Writes and "Init Request", which initializes the connection / application used server-side. + + Uses this.ServerAddress, this.AppId, this.PhotonToken and CustomInitData and some more values. + Bytes of the init request. + + + Called when the server's Init Response arrived. + + + Serializes an operation into our binary messages (magic number, msg-type byte and message). Optionally encrypts. + This method is mostly the same in EnetPeer, TPeer and HttpPeerBase. Also, for raw messages, we have another variant. + + + Serializes an operation into our binary messages (magic number, msg-type byte and message). Optionally encrypts. + This method is mostly the same in EnetPeer, TPeer and HttpPeerBase. Also, for raw messages, we have another variant. + + + Returns the UDP Payload starting with Magic Number for binary protocol + + + + Checks outgoing queues for commands to send and puts them on their way. + This creates one package per go in UDP. + + If commands are not sent, cause they didn't fit into the package that's sent. + + + + Checks the incoming queue and Dispatches received data if possible. + + If a Dispatch happened or not, which shows if more Dispatches might be needed. + + + + Internally uses an operation to exchange encryption keys with the server. + + If the op could be sent. + + + + Gets the currently used settings for the built-in network simulation. + Please check the description of NetworkSimulationSet for more details. + + + + + Core of the Network Simulation, which is available in Debug builds. + Called by a timer in intervals. + + + + EnetPeer will set this value, so trafficstats can use it. TCP has 0 bytes per package extra + + + See PhotonPeer value. + + + See PhotonPeer value. + + + See PhotonPeer value. + + + See PhotonPeer value. + + + + Value range for a Peer's connection and initialization state, as returned by the PeerState property. + + + While this is not the same as the StatusCode of IPhotonPeerListener.OnStatusChanged(), it directly relates to it. + In most cases, it makes more sense to build a game's state on top of the OnStatusChanged() as you get changes. + + + + The peer is disconnected and can't call Operations. Call Connect(). + + + The peer is establishing the connection: opening a socket, exchanging packages with Photon. + + + The connection is established and now sends the application name to Photon. + You set the "application name" by calling PhotonPeer.Connect(). + + + The peer is connected and initialized (selected an application). You can now use operations. + + + The peer is disconnecting. It sent a disconnect to the server, which will acknowledge closing the connection. + + + + These are the options that can be used as underlying transport protocol. + + + + Use UDP to connect to Photon, which allows you to send operations reliable or unreliable on demand. + + + Use TCP to connect to Photon. + + + A TCP-based protocol commonly supported by browsers.For WebGL games mostly. Note: No WebSocket IPhotonSocket implementation is in this Assembly. + This protocol is only available in Unity exports to WebGL. + + + A TCP-based, encrypted protocol commonly supported by browsers. For WebGL games mostly. Note: No WebSocket IPhotonSocket implementation is in this Assembly. + This protocol is only available in Unity exports to WebGL. + + + + Level / amount of DebugReturn callbacks. Each debug level includes output for lower ones: OFF, ERROR, WARNING, INFO, ALL. + + + + No debug out. + + + Only error descriptions. + + + Warnings and errors. + + + Information about internal workflows, warnings and errors. + + + Most complete workflow description (but lots of debug output), info, warnings and errors. + + + Build target framework supported by this dll. + + + + Instances of the PhotonPeer class are used to connect to a Photon server and communicate with it. + + + A PhotonPeer instance allows communication with the Photon Server, which in turn distributes messages + to other PhotonPeer clients. + An application can use more than one PhotonPeer instance, which are treated as separate users on the + server. Each should have its own listener instance, to separate the operations, callbacks and events. + + + + + No effect anymore. Removed without replacement. + + + This value was used to get/set the initial capacities of command-lists. + These grow on demand but knowing their capacity is of very limited use. + Also, various command-lists grow their capacity independent from one another. + + + + + No effect anymore. Removed without replacement. + + + This was used to skip some received (and buffered) unreliable commands, to avoid situations + where the peer has aggregated a lot of (old) messages. + + + + + The WarningSize was used test all message queues for congestion. + + + + + Gets a local timestamp in milliseconds by calling SupportClass.GetTickCount(). + See LocalMsTimestampDelegate. + + + + Where dynamic linking is available, this library will attempt to load a native Photon "Encryptor" plugin library for "Datagram Encryption". + Fallback to a managed implementation. This value is always true. + + + Obsolete and ignored. Size of CommandLog. Default is 0, no logging. + + + Obsolete and ignored. Converts the CommandLog into a readable table-like string with summary. + + + False if this library build contains C# Socket code. If true, you must set some type as SocketImplementation before connecting. + + + True if the library was compiled with DEBUG setting. + + + Version of the Native Encryptor API compiled into this assembly. Defines which PhotonEncryptorPlugin needs to be used. + + + Target framework this dll was built for. + + + Global toggle to avoid callbacks from native plugins. Defaults to false, meaning: "callbacks enabled". + Callbacks from native code will fail on some platforms, which is why you can disable them. + + + Can be used to remove/hide the AppId from websocket connect paths. + + + A simplified identifier for client SDKs. Photon's APIs might modify this (as a dll can be used in more than one product). Helps debugging. + + + For the Init-request, we shift the ClientId by one and the last bit signals a "debug" (0) or "release" build (1). + + + Version of this library as string. + + + Version of this library as string. + + + A Native Socket implementation is no longer part of this DLL but delivered in a separate add-on. This value always returns false. + + + Native Payload Encryption is no longer part of this DLL but delivered in a separate add-on. This value always returns false. + + + Native Datagram Encryption is no longer part of this DLL but delivered in a separate add-on. This value always returns false. + + + Enables selection of a (Photon-)serialization protocol. Used in Connect methods. + Defaults to SerializationProtocol.GpBinaryV16; + + + Optional definition of IPhotonSocket type per ConnectionProtocol. + + Several platforms have special Socket implementations and slightly different APIs. + Customizing the SocketImplementationConfig helps to accomodate this. + By default, UDP and TCP have socket implementations assigned. + + If a native socket plugin is available set the SocketNativeSource class as Type definition here. + + You only need to set the SocketImplementationConfig once, after creating a PhotonPeer + and before connecting. If you switch the TransportProtocol, the correct implementation is being used. + + + + + Can be used to read the IPhotonSocket implementation at runtime (before connecting). + + + Use the SocketImplementationConfig to define which IPhotonSocket is used per ConnectionProtocol. + + + + + Sets the level (and amount) of debug output provided by the library. + + + This affects the callbacks to IPhotonPeerListener.DebugReturn. + Default Level: Error. + + + + + Gets the IPhotonPeerListener of this instance (set in constructor). + Can be used in derived classes for Listener.DebugReturn(). + + + + + Called when the client received a Disconnect Message from the server. Signals an error and provides a message to debug the case. + + + + + Option to make the PhotonPeer reuse a single EventData instance for all incoming events. + + + This reduces memory garbage. + If enabled, the event provided via OnEvent(EventData photonEvent) is invalid once the callback finished. + That event's content will get modified. Typically this is not a problem as events are rarely cached. + + Changing this value acquires the same lock that DispatchIncomingCommands() uses. + + + + + Enables a deserialization optimization for incoming events. Defaults to false. + + + When enabled, byte-arrays in incoming Photon events are deserialized into pooled ByteArraySlice instances (wrappers for byte[]). + This improves the memory footprint for receiving byte-arrays in events. + + When used, you have to release the (pooled) ByteArraySlice instances. + + Adjust your handling of EventData accordingly: + + The ByteArraySlice.Buffer will usually be bigger than the send/received byte-array. + Check the ByteArraySlice.Count and read only the actually received bytes. + The Buffer is reused and not cleared. The Offset will be 0 for incoming events. + + Important: + While the peer will acquire the ByteArraySlice and passes it to OnEvent, the game code has to call ByteArraySlice.Release() + when the slice is no longer needed. + + Send either byte[], ArraySegment or use the ByteArraySlicePool to acquire ByteArraySlices to send. + + + + + Instance of a ByteArraySlicePool. UseByteArraySlicePoolForEvents defines if this PhotonPeer is using the pool for deserialization of byte[] in Photon events. + + ByteArraySlice is a serializable datatype of the Photon .Net client library. + It helps avoid allocations by being pooled and (optionally) used in incoming Photon events (see: UseByteArraySlicePoolForEvents). + + You can also use the pool to acquire ByteArraySlice instances for serialization. + RaiseEvent will auto-release all ByteArraySlice instances passed in. + + + + + This debug setting enables a new send-ordering for commands. Defaults to true and commands are sent in the order they are created. Set to false to use Enet ordering. + + + + Skips resending (individual) commands if their reliable sequence number is this amount larger than the highest acknowledged sequence number. + + This puts focus on resending commands that are older and are needed on the receiver side to dispatch commands. + It queues more on the client side than on the server. + Affects only reliable UDP (based on enet). + + + + Skips sending reliable and unreliable commands if their reliable sequence number is this amount larger than the highest acknowledged sequence number. + + This puts focus on resending commands that are older and are needed on the receiver side to dispatch commands. + It queues more on the client side than on the server. + Affects only reliable UDP (based on enet). + + + + + Gets count of all bytes coming in (including headers, excluding UDP/TCP overhead) + + + + + Gets count of all bytes going out (including headers, excluding UDP/TCP overhead) + + + + + Gets the size of the dispatched event or operation-result in bytes. + This value is set before OnEvent() or OnOperationResponse() is called (within DispatchIncomingCommands()). + + + Get this value directly in OnEvent() or OnOperationResponse(). Example: + void OnEvent(...) { + int eventSizeInBytes = this.peer.ByteCountCurrentDispatch; + //... + + void OnOperationResponse(...) { + int resultSizeInBytes = this.peer.ByteCountCurrentDispatch; + //... + + + + Returns the debug string of the event or operation-response currently being dispatched or string. Empty if none. + In a release build of the lib, this will always be empty. + + + + Gets the size of the last serialized operation call in bytes. + The value includes all headers for this single operation but excludes those of UDP, Enet Package Headers and TCP. + + + Get this value immediately after calling an operation. + Example: + + this.loadbalancingClient.OpJoinRoom("myroom"); + int opjoinByteCount = this.loadbalancingClient.ByteCountLastOperation; + + + + If set, the TrafficRecorder will be used to capture all traffic. + + If null or not Enabled, the recorder is not being used. + Release builds of this library will never record traffic for performance reasons. + + See ITrafficRecorder docs. + + + + + Debugging option to tell the Photon Server to log all datagrams. + + + + + Up to 4 resend attempts for a reliable command can be done in quick succession (after RTT+4*Variance). + + + By default 0. Any later resend attempt will then double the time before the next resend. + Max value = 4; + Make sure to adjust SentCountAllowance to a slightly higher value, as more repeats will get done. + + + + + This is the (low level) state of the connection to the server of a PhotonPeer. Managed internally and read-only. + + + Don't mix this up with the StatusCode provided in IPhotonListener.OnStatusChanged(). + Applications should use the StatusCode of OnStatusChanged() to track their state, as + it also covers the higher level initialization between a client and Photon. + + + + + This peer's ID as assigned by the server or 0 if not using UDP. Will be 0xFFFF before the client connects. + + Used for debugging only. This value is not useful in everyday Photon usage. + + + + Count of all currently received but not-yet-Dispatched reliable commands + (events and operation results) from all channels. + + + + + Count of all commands currently queued as outgoing, including all channels and reliable, unreliable. + + + + + Sets a new (temporary) size of the MessageBufferPool to reuse memory where possible. + + + The MessageBufferPool is a Queue<StreamBuffer> for performance reasons. + This methods dequeues from the MessageBufferPool to get the Count equal to countOfBuffers, + then it calls MessageBufferPool.TrimExcess(). + + New size of the pool. Clears the pool if <= 0. + + + + Gets / sets the number of channels available in UDP connections with Photon. + Photon Channels are only supported for UDP. + The default ChannelCount is 2. Channel IDs start with 0 and 255 is a internal channel. + + + + + Enables the client so send the "encrypted" flag on secure connections. Incompatible with Server SDK 4.x. + + + + + While not connected, this controls if the next connection(s) should use a per-package CRC checksum. + + + While turned on, the client and server will add a CRC checksum to every sent package. + The checksum enables both sides to detect and ignore packages that were corrupted during transfer. + Corrupted packages have the same impact as lost packages: They require a re-send, adding a delay + and could lead to timeouts. + + Building the checksum has a low processing overhead but increases integrity of sent and received data. + Packages discarded due to failed CRC cecks are counted in PhotonPeer.PacketLossByCrc. + + + + + Count of packages dropped due to failed CRC checks for this connection. + + + + + + Count of packages dropped due to wrong challenge for this connection. + + + + + Gets the count of sent but not yet acknowledged commands (for UDP connections). + + + + + Count of commands that got repeated (due to local repeat-timing before an ACK was received). + + + + + Number of send retries before a peer is considered lost/disconnected. Default: 7. + + + The initial timeout countdown of a command is calculated by the current roundTripTime + 4 * roundTripTimeVariance. + Please note that the timeout span until a command will be resent is not constant, but based on + the roundtrip time at the initial sending, which will be doubled with every failed retry. + + DisconnectTimeout and SentCountAllowance are competing settings: either might trigger a disconnect on the + client first, depending on the values and Roundtrip Time. + + + + + Caps the initial timing for repeats of reliable commands. In milliseconds. Default: 400ms. + + + Unless acknowledged, reliable commands are repeated initially after: current roundTripTime + 4 * roundTripTimeVariance. + + As this value can be very high when there was exceptional lag, InitialResendTimeMax makes sure that commands + get repeated several times before they may trigger a timeout. + + + + + Sets the time between pings being sent automatically. They measure the roundtrip time and keep connections from closing. Default: 1000. + + + For Photon's reliable UDP connections, pings are skipped if any reliable command was sent during the specified TimePingInterval. + Any reliable command is used to update the RoundTripTime and RoundTripTimeVariance. + + When using TCP and WebSockets, the ping is of interest to measure the roundtrip and to keep a connection open, should nothing else + With those two protocols, the ping is used to update the RoundTripTime and RoundTripTimeVariance. + + + + + Time in milliseconds before any sent reliable command triggers a timeout disconnect, unless acknowledged by the receiver. Default: 10000. + + + DisconnectTimeout is not an exact value for a timeout. The exact timing of the timeout depends on the frequency + of Service() calls and the roundtrip time. Commands sent with long roundtrip-times and variance are checked less + often for re-sending. + + DisconnectTimeout and SentCountAllowance are competing settings: either might trigger a disconnect on the + client first, depending on the values and Roundtrip Time. + + Default: 10000 ms. Maximum setting: 65535. + Setting a negative value will apply the default timeout. + + + + + Approximated Environment.TickCount value of server (while connected). + + + UDP: The server's timestamp is automatically fetched after connecting (once). This is done + internally by a command which is acknowledged immediately by the server. + TCP: The server's timestamp fetched with each ping but set only after connecting (once). + + The approximation will be off by +/- 10ms in most cases. Per peer/client and connection, the + offset will be constant (unless FetchServerTimestamp() is used). A constant offset should be + better to adjust for. Unfortunately there is no way to find out how much the local value + differs from the original. + + The approximation adds RoundtripTime / 2 and uses this.LocalTimeInMilliSeconds to calculate + in-between values (this property returns a new value per tick). + + The value sent by Photon equals Environment.TickCount in the logic layer. + + + 0 until connected. + While connected, the value is an approximation of the server's current timestamp. + + + + + This setter for the (local-) timestamp delegate replaces the default Environment.TickCount with any equal function. + + + + The internally used per PhotonPeer time value. + + Returns the integer part of a Stopwatch ElapsedMilliseconds value. + If the PhotonPeer runs continuously the ClientTime will increment from zero to Int32..::.MaxValue + for approximately 24.9 days, then jump to Int32..::.MinValue (a negative number), then increment + back to zero during the next 24.9 days. + + It is recommended to use this int only for delta times, to avoid handling the overflow. + + + + The last ConnectionTime value, when some ACKs were sent out by this client. + Only applicable to UDP connections. + + + The last ConnectionTime value, when SendOutgoingCommands actually checked outgoing queues to send them. Must be connected. + Available for UDP and TCP connections. + + + Measures the maximum milliseconds spent in PhotonSocket.Send(). + + + Time until a reliable command is acknowledged by the server. + + The value measures network latency and for UDP it includes the server's ACK-delay (setting in config). + In TCP, there is no ACK-delay, so the value is slightly lower (if you use default settings for Photon). + + RoundTripTime is updated constantly. Every reliable command will contribute a fraction to this value. + + This is also the approximate time until a raised event reaches another client or until an operation + result is available. + + + + + Changes of the roundtriptime as variance value. Gives a hint about how much the time is changing. + + + + The last measured roundtrip time for this connection. + + + + Timestamp of the last time anything (!) was received from the server (including low level Ping, ACKs, events and operation-returns). + + + This is not the time when something was dispatched. If you enable NetworkSimulation, this value is affected as well. + + + + + The server address which was used in PhotonPeer.Connect() or null (before Connect() was called). + + + + Contains the IP address of the previously resolved ServerAddress (or empty, if address wasn't resolved with the internal methods). + + + The protocol this peer is currently connected/connecting with (or 0). + + + This is the transport protocol to be used for next connect (see remarks). + The TransportProtocol can be changed anytime but it will not change the + currently active connection. Instead, TransportProtocol will be applied on next Connect. + + + + + Gets or sets the network simulation "enabled" setting. + Changing this value also locks this peer's sending and when setting false, + the internally used queues are executed (so setting to false can take some cycles). + + + + + Gets the settings for built-in Network Simulation for this peer instance + while IsSimulationEnabled will enable or disable them. + Once obtained, the settings can be modified by changing the properties. + + + + + Defines the initial size of an internally used StreamBuffer for Tcp. + The StreamBuffer is used to aggregate operation into (less) send calls, + which uses less resoures. + + + The size is not restricing the buffer and does not affect when outgoing data is actually sent. + + + + + The Maximum Trasfer Unit (MTU) defines the (network-level) packet-content size that is + guaranteed to arrive at the server in one piece. The Photon Protocol uses this + size to split larger data into packets and for receive-buffers of packets. + + + This value affects the Packet-content. The resulting UDP packages will have additional + headers that also count against the package size (so it's bigger than this limit in the end) + Setting this value while being connected is not allowed and will throw an Exception. + Minimum is 576. Huge values won't speed up connections in most cases! + + + + + This property is set internally, when OpExchangeKeysForEncryption successfully finished. + While it's true, encryption can be used for operations. + + + + + While true, the peer will not send any other commands except ACKs (used in UDP connections). + + + + Defines if Key Exchange for Encryption is done asynchronously in another thread. + + + Indicates if sequence numbers should be randomized. + + + Initialization array, used to modify the sequence numbers of channels. + + + If GCM is used for DatagramEncryption. + If true, the randomization-value gets added to the current value, else (CBC/old style) the randomization-value replaces the current value. + + + + Gets the byte-count of incoming "low level" messages, which are either Enet Commands or Tcp Messages. + These include all headers, except those of the underlying internet protocol Udp or Tcp. + + + + + Gets the byte-count of outgoing "low level" messages, which are either Enet Commands or Tcp Messages. + These include all headers, except those of the underlying internet protocol Udp or Tcp. + + + + + Gets a statistic of incoming and outgoing traffic, split by operation, operation-result and event. + + + Operations are outgoing traffic, results and events are incoming. + Includes the per-command header sizes (Udp: Enet Command Header or Tcp: Message Header). + + + + + Returns the count of milliseconds the stats are enabled for tracking. + + + + + Enables or disables collection of statistics in TrafficStatsIncoming, TrafficStatsOutgoing and TrafficstatsGameLevel. + + + Setting this to true, also starts the stopwatch to measure the timespan the stats are collected. + Enables the traffic statistics of a peer: TrafficStatsIncoming, TrafficStatsOutgoing and TrafficstatsGameLevel (nothing else). + Default value: false (disabled). + + + + + Creates new instances of TrafficStats and starts a new timer for those. + + + + + Creates new TrafficStats values and the related Stopwatch instance. To be called when the peer is created / reset. + + + + + Returns a string of the most interesting connection statistics. + When you have issues on the client side, these might contain hints about the issue's cause. + + If true, Incoming and Outgoing low-level stats are included in the string. + Stats as string. + + + Implements the message-protocol, based on the underlying network protocol (udp, tcp, http). + + + Setter for the Payload Encryptor type. Used for next connection. + + If null, the PhotonPeer will create a DiffieHellmanCryptoProvider, which is the default. + This is only needed in rare cases, where using native payload encryption makes sense. + + Get in touch about this, if you got questions: developer@photonengine.com + + + + PayloadEncryption Secret. Message payloads get encrypted with it individually and on demand. + + + Setter for the Datagram Encryptor type. Used at next connect. + + If null, the PhotonPeer will create a default datagram encryptor instance. + + + + The datagram encryptor used for the current connection. Applied internally in InitDatagramEncryption. + + + Count of unreliable commands being discarded in case this client already dispatched a command that was newer (higher sequence number). + + + Set per dispatch in DispatchIncomingCommands to: commandUnreliableSequenceNumber - channel.incomingUnreliableSequenceNumber. Indicates how big the (sequence)gap is, compared to the last dispatched unreliable command. + + + Creates a new PhotonPeer with specified transport protocol (without a IPhotonPeerListener). + Make sure to set the Listener, before using the peer. + + + + Creates a new PhotonPeer instance to communicate with Photon and selects the transport protocol. We recommend UDP. + + a IPhotonPeerListener implementation + Protocol to use to connect to Photon. + + + + Starts connecting to the given Photon server. Non-blocking. + + + Connecting to the Photon server is done asynchronous. + Unless an error happens right away (and this returns false), wait for the call of IPhotonPeerListener.OnStatusChanged. + + + Address of a Photon server as IP:port or hostname. WebSocket connections must contain a scheme (ws:// or wss://). + + + The ID of the app to use. Typically this is a guid (for the Photon Cloud). Max 32 characters. + + + Optional custom data to be used by server during peer creation. + If used for authentication, the server is able to reject a client without creating a peer. + Must be a serializable data type of Photon. + + Custom data to send to the server in the Init request. Might be used to identify a client / user. + + True if a connection attempt will be made. False if some error could be detected early-on. + + + + + Starts connecting to the given Photon server. Non-blocking. + + + Connecting to the Photon server is done asynchronous. + Unless an error happens right away (and this returns false), wait for the call of IPhotonPeerListener.OnStatusChanged. + + + Address of a Photon server as IP:port or hostname. WebSocket connections must contain a scheme (ws:// or wss://). + + + Optional address of a proxy server. Only used by WebSocket connections. Set null to use none. + + + The ID of the app to use. Typically this is a guid (for the Photon Cloud). Max 32 characters. + + + Optional Photon token data to be used by server during peer creation. + If used for authentication, the server is able to reject a client without creating a peer. + Must be of type string or byte[] (as provided by server). + + Custom data to send to the server in the Init request. Might be used to identify a client / user. + + True if a connection attempt will be made. False if some error could be detected early-on. + + + + + This method initiates a mutual disconnect between this client and the server. + + + Calling this method does not immediately close a connection. Disconnect lets the server + know that this client is no longer listening. For the server, this is a much faster way + to detect that the client is gone but it requires the client to send a few final messages. + + On completion, OnStatusChanged is called with the StatusCode.Disconnect. + + If the client is disconnected already or the connection thread is stopped, then there is no callback. + + The default server logic will leave any joined game and trigger the respective event. + + + + + This method immediately closes a connection (pure client side) and ends related listening Threads. + + + Unlike Disconnect, this method will simply stop to listen to the server. Udp connections will timeout. + If the connections was open, this will trigger a callback to OnStatusChanged with code StatusCode.Disconnect. + + + + + This will fetch the server's timestamp and update the approximation for property ServerTimeInMilliseconds. + + + The server time approximation will NOT become more accurate by repeated calls. Accuracy currently depends + on a single roundtrip which is done as fast as possible. + + The command used for this is immediately acknowledged by the server. This makes sure the roundtrip time is + low and the timestamp + rountriptime / 2 is close to the original value. + + + + + This method creates a public key for this client and exchanges it with the server. + + + Encryption is not instantly available but calls OnStatusChanged when it finishes. + Check for StatusCode EncryptionEstablished and EncryptionFailedToEstablish. + + Calling this method sets IsEncryptionAvailable to false. + This method must be called before the "encrypt" parameter of OpCustom can be used. + + If operation could be enqueued for sending + + + + Initializes Datagram Encryption. Optionally, the EncryptorType is being used, if set. + + Secret used to cipher udp packets. + Secret used for authentication of udp packets. + Sets if enet Sequence Numbers will be randomized or not. Preferably should be true. + Sets if the chaining mode should be CBC (false, default) or GCM (true). GCM mode is only available with a native encryption plugin. + + + + Photon's Payload Encryption secret may be set by a response from the server. + + The secret in form of a byte[]. + + + + This method excutes DispatchIncomingCommands and SendOutgoingCommands in your application Thread-context. + + + The Photon client libraries are designed to fit easily into a game or application. The application + is in control of the context (thread) in which incoming events and responses are executed and has + full control of the creation of UDP/TCP packages. + + Sending packages and dispatching received messages are two separate tasks. Service combines them + into one method at the cost of control. It calls DispatchIncomingCommands and SendOutgoingCommands. + + Call this method regularly (2..20 times a second). + + This will Dispatch ANY remaining buffered responses and events AND will send queued outgoing commands. + Fewer calls might be more effective if a device cannot send many packets per second, as multiple + operations might be combined into one package. + + + You could replace Service by: + + while (DispatchIncomingCommands()); //Dispatch until everything is Dispatched... + SendOutgoingCommands(); //Send a UDP/TCP package with outgoing messages + + + + + + + Creates and sends a UDP/TCP package with outgoing commands (operations and acknowledgements). Also called by Service(). + + + As the Photon library does not create any UDP/TCP packages by itself. Instead, the application + fully controls how many packages are sent and when. A tradeoff, an application will + lose connection, if it is no longer calling SendOutgoingCommands or Service. + + If multiple operations and ACKs are waiting to be sent, they will be aggregated into one + package. The package fills in this order: + ACKs for received commands + A "Ping" - only if no reliable data was sent for a while + Starting with the lowest Channel-Nr: + Reliable Commands in channel + Unreliable Commands in channel + + This gives a higher priority to lower channels. + + A longer interval between sends will lower the overhead per sent operation but + increase the internal delay (which adds "lag"). + + Call this 2..20 times per second (depending on your target platform). + + The if commands are not yet sent. Udp limits it's package size, Tcp doesnt. + + + + Dispatching received messages (commands), causes callbacks for events, responses and state changes within a IPhotonPeerListener. + + + DispatchIncomingCommands only executes a single received + command per call. If a command was dispatched, the return value is true and the method + should be called again. + + This method is called by Service() until currently available commands are dispatched. + In general, this method should be called until it returns false. In a few cases, it might + make sense to pause dispatching (if a certain state is reached and the app needs to load + data, before it should handle new events). + + The callbacks to the peer's IPhotonPeerListener are executed in the same thread that is + calling DispatchIncomingCommands. This makes things easier in a game loop: Event execution + won't clash with painting objects or the game logic. + + + + + Prepares your operation (code and parameters) to be sent to the Photon Server with specified SendOptions. + + + This method serializes and enqueues the operation right away while the actual sending happens later. + To be able to aggregate operations/messages, the Photon client sends packages only when you call SendOutgoingCommands(). + + The sendOptions specify how the operation gets sent exactly. + Keep in mind that some transport protocols don't support unreliable or unsequenced transport. + In that case, the sendOptions might be ignored. + + The operationCode must be known by the server's logic or won't be processed. + In almost all cases, sending an operation will result in a OperationResponse (see: IPhotonPeerListener.OnOperationResponse). + + Operations are handled by their byte\-typed code. The codes are defined in the Realtime API (a.k.a. LoadBalancing API). + Containing parameters as key\-value pair. The key is byte\-typed, while the value is any serializable datatype. + Wraps up DeliveryMode (reliability), Encryption and Channel values for sending. + If operation could be enqueued for sending. + + + + Registers new types/classes for de/serialization and the fitting methods to call for this type. + + + SerializeMethod and DeserializeMethod are complementary: Feed the product of serializeMethod to + the constructor, to get a comparable instance of the object. + + After registering a Type, it can be used in events and operations and will be serialized like + built-in types. + + Type (class) to register. + A byte-code used as shortcut during transfer of this Type. + Method delegate to create a byte[] from a customType instance. + Method delegate to create instances of customType's from byte[]. + If the Type was registered successfully. + + + + Container for an Operation request, which is a code and parameters. + + + On the lowest level, Photon only allows byte-typed keys for operation parameters. + The values of each such parameter can be any serializable datatype: byte, int, hashtable and many more. + + + + Byte-typed code for an operation - the short identifier for the server's method to call. + + + The parameters of the operation - each identified by a byte-typed code in Photon. + + + + Contains the server's response for an operation called by this peer. + The indexer of this class actually provides access to the Parameters Dictionary. + + + The OperationCode defines the type of operation called on Photon and in turn also the Parameters that + are set in the request. Those are provided as Dictionary with byte-keys. + There are pre-defined constants for various codes defined in the LoadBalancing application. + Check: OperationCode, ParameterCode, etc. + + An operation's request is summarized by the ReturnCode: a short typed code for "Ok" or + some different result. The code's meaning is specific per operation. An optional DebugMessage can be + provided to simplify debugging. + + Each call of an operation gets an ID, called the "invocID". This can be matched to the IDs + returned with any operation calls. This way, an application could track if a certain OpRaiseEvent + call was successful. + + + + The code for the operation called initially (by this peer). + Use enums or constants to be able to handle those codes, like OperationCode does. + + + A code that "summarizes" the operation's success or failure. Specific per operation. 0 usually means "ok". + + + An optional string sent by the server to provide readable feedback in error-cases. Might be null. + + + A Dictionary of values returned by an operation, using byte-typed keys per value. + + + + Alternative access to the Parameters, which wraps up a TryGetValue() call on the Parameters Dictionary. + + The byte-code of a returned value. + The value returned by the server, or null if the key does not exist in Parameters. + + + ToString() override. + Relatively short output of OpCode and returnCode. + + + Extensive output of operation results. + To be used in debug situations only, as it returns a string for each value. + + + A Photon Event consists of a Code value and a Parameters Dictionary with the event's content (if any). + + The indexer of this class provides access to the values in Parameters. + It wraps the null check for Parameters and uses TryGetValue() for the provided key. + + Photon servers use events to send information which is not triggered by a client's operation requests (those get responses). + The Realtime API allows you to send custom events with any Code and content via OpRaiseEvent. + + + + The event code identifies the type of event. + + + The Parameters of an event is a Dictionary<byte, object>. + + + + Access to the Parameters of a Photon-defined event. Custom Events only use Code, Sender and CustomData. + + The key byte-code of a Photon event value. + The Parameters value, or null if the key does not exist in Parameters. + + + + Defines the event key containing the Sender of the event. + + + Defaults to Sender key of Realtime API events (RaiseEvent): 254. + Can be set to Chat API's ChatParameterCode.Sender: 5. + + + + + Accesses the Sender of the event via the indexer and SenderKey. The result is cached. + + + Accesses this event's Parameters[CustomDataKey], which may be null. + In that case, this returns 0 (identifying the server as sender). + + + + + Defines the event key containing the Custom Data of the event. + + + Defaults to Data key of Realtime API events (RaiseEvent): 245. + Can be set to any other value on demand. + + + + + Accesses the Custom Data of the event via the indexer and CustomDataKey. The result is cached. + + + Accesses this event's Parameters[CustomDataKey], which may be null. + + + + ToString() override. + Short output of "Event" and it's Code. + + + Extensive output of the event content. + To be used in debug situations only, as it returns a string for each value. + + + + Type of serialization methods to add custom type support. + Use PhotonPeer.ReisterType() to register new types with serialization and deserialization methods. + + The method will get objects passed that were registered with it in RegisterType(). + Return a byte[] that resembles the object passed in. The framework will surround it with length and type info, so don't include it. + + + Serialization method delegate. StreamBuffer based custom serialization methods must use this form. + + + + Type of deserialization methods to add custom type support. + Use PhotonPeer.RegisterType() to register new types with serialization and deserialization methods. + + The framwork passes in the data it got by the associated SerializeMethod. The type code and length are stripped and applied before a DeserializeMethod is called. + Return a object of the type that was associated with this method through RegisterType(). + + + Deserialization method delegate. StreamBuffer based custom deserialization methods must use this form. + + + + Provides tools for the Exit Games Protocol + + + + + Serialize creates a byte-array from the given object and returns it. + + The object to serialize + The serialized byte-array + + + + Deserialize returns an object reassembled from the given byte-array. + + The byte-array to be Deserialized + The Deserialized object + + + + Serializes a short typed value into a byte-array (target) starting at the also given targetOffset. + The altered offset is known to the caller, because it is given via a referenced parameter. + + The short value to be serialized + The byte-array to serialize the short to + The offset in the byte-array + + + + Serializes an int typed value into a byte-array (target) starting at the also given targetOffset. + The altered offset is known to the caller, because it is given via a referenced parameter. + + The int value to be serialized + The byte-array to serialize the short to + The offset in the byte-array + + + + Serializes an float typed value into a byte-array (target) starting at the also given targetOffset. + The altered offset is known to the caller, because it is given via a referenced parameter. + + The float value to be serialized + The byte-array to serialize the short to + The offset in the byte-array + + + + Deserialize fills the given int typed value with the given byte-array (source) starting at the also given offset. + The result is placed in a variable (value). There is no need to return a value because the parameter value is given by reference. + The altered offset is this way also known to the caller. + + The int value to deserialize into + The byte-array to deserialize from + The offset in the byte-array + + + + Deserialize fills the given short typed value with the given byte-array (source) starting at the also given offset. + The result is placed in a variable (value). There is no need to return a value because the parameter value is given by reference. + The altered offset is this way also known to the caller. + + The short value to deserialized into + The byte-array to deserialize from + The offset in the byte-array + + + + Deserialize fills the given float typed value with the given byte-array (source) starting at the also given offset. + The result is placed in a variable (value). There is no need to return a value because the parameter value is given by reference. + The altered offset is this way also known to the caller. + + The float value to deserialize + The byte-array to deserialize from + The offset in the byte-array + + + + Exit Games GpBinaryV16 protocol implementation + + + + + The gp type. + + + + + Unkown type. + + + + + An array of objects. + + + This type is new in version 1.5. + + + + + A boolean Value. + + + + + A byte value. + + + + + An array of bytes. + + + + + An array of objects. + + + + + A 16-bit integer value. + + + + + A 32-bit floating-point value. + + + This type is new in version 1.5. + + + + + A dictionary + + + This type is new in version 1.6. + + + + + A 64-bit floating-point value. + + + This type is new in version 1.5. + + + + + A Hashtable. + + + + + A 32-bit integer value. + + + + + An array of 32-bit integer values. + + + + + A 64-bit integer value. + + + + + A string value. + + + + + An array of string values. + + + + + A custom type. 0x63 + + + + + Null value don't have types. + + + + + Calls the correct serialization method for the passed object. + + + + + DeserializeInteger returns an Integer typed value from the given stream. + + + + Exception type for de/serialization issues. Used in Protocol 1.8. + + + Constructor for the exception. + + + Unkown. GpType: 0. + + + Boolean. GpType: 2. See: BooleanFalse, BooleanTrue. + + + Byte. GpType: 3. + + + Short. GpType: 4. + + + 32-bit floating-point value. GpType: 5. + + + 64-bit floating-point value. GpType: 6. + + + String. GpType: 7. + + + Null value don't have types. GpType: 8. + + + CompressedInt. GpType: 9. + + + CompressedLong. GpType: 10. + + + Int1. GpType: 11. + + + Int1_. GpType: 12. + + + Int2. GpType: 13. + + + Int2_. GpType: 14. + + + L1. GpType: 15. + + + L1_. GpType: 16. + + + L2. GpType: 17. + + + L2_. GpType: 18. + + + Custom Type. GpType: 19. + + + Custom Type Slim. GpType: 128 (0x80) and up. + + + Dictionary. GpType: 20. + + + Hashtable. GpType: 21. + + + ObjectArray. GpType: 23. + + + OperationRequest. GpType: 24. + + + OperationResponse. GpType: 25. + + + EventData. GpType: 26. + + + Boolean False. GpType: 27. + + + Boolean True. GpType: 28. + + + ShortZero. GpType: 29. + + + IntZero. GpType: 30. + + + LongZero. GpType: 3. + + + FloatZero. GpType: 32. + + + DoubleZero. GpType: 33. + + + ByteZero. GpType: 34. + + + Array for nested Arrays. GpType: 64 (0x40). Element count and type follows. + + + + Writes integers as compressed. Either directly as zigzag-encoded or (when a type is written for this value) it can use an optimized sub-type. + + + + Enum of the three options for reliability and sequencing in Photon's reliable-UDP. + + + The operation/message gets sent just once without acknowledgement or repeat. The sequence (order) of messages is guaranteed. + + + The operation/message asks for an acknowledgment. It's resent until an ACK arrived. The sequence (order) of messages is guaranteed. + + + The operation/message gets sent once (unreliable) and might arrive out of order. Best for your own sequencing (e.g. for streams). + + + The operation/message asks for an acknowledgment. It's resent until an ACK arrived and might arrive out of order. Best for your own sequencing (e.g. for streams). + + + Wraps up DeliveryMode, Encryption and Channel values for sending operations and messages. + + + Default SendOptions instance for reliable sending. + + + Default SendOptions instance for unreliable sending. + + + Chose the DeliveryMode for this operation/message. Defaults to Unreliable. + + + If true the operation/message gets encrypted before it's sent. Defaults to false. + Before encryption can be used, it must be established. Check PhotonPeer.IsEncryptionAvailable is true. + + + The Enet channel to send in. Defaults to 0. + Channels in Photon relate to "message channels". Each channel is a sequence of messages. + + + Sets the DeliveryMode either to true: Reliable or false: Unreliable, overriding any current value. + Use this to conveniently select reliable/unreliable delivery. + + + Encapsulates the network i/o functionality for the realtime library. + + + used by PhotonPeer* + + + Endless loop, run in Receive Thread. + + + Internal class to encapsulate the network i/o functionality for the realtime libary. + + + used by PhotonPeer* + + + Encapsulates the network i/o functionality for the realtime library. + + + used by PhotonPeer* + + + Endless loop, run in Receive Thread. + + + Internal class to encapsulate the network i/o functionality for the realtime libary. + + + used by PhotonPeer* + + + + Allocates a new byte[] that is the exact used length. Use GetBuffer for nonalloc operations. + + + + + Allocates a new byte[] that is the exact used length. Use GetBuffer for nonalloc operations. + + + + + The bytes between Position and Length are copied to the beginning of the buffer. Length decreased by Position. Position set to 0. + + + + + Brings StreamBuffer to the state as after writing of 'length' bytes. Returned buffer and offset can be used to actually fill "written" segment with data. + + + + + Remaining bytes in this StreamBuffer. Returns 0 if len - pos is less than 0. + + + + + Sets stream length. If current position is greater than specified value, it's set to the value. + + + SetLength(0) resets the stream to initial state but preserves underlying byte[] buffer. + + + + + Guarantees that the buffer is at least neededSize bytes. + + + + + Contains several (more or less) useful static methods, mostly used for debugging. + + + + + Gets the local machine's "milliseconds since start" value (precision is described in remarks). + + + This method uses Environment.TickCount (cheap but with only 16ms precision). + PhotonPeer.LocalMsTimestampDelegate is available to set the delegate (unless already connected). + + Fraction of the current time in Milliseconds (this is not a proper datetime timestamp). + + + + Creates a background thread that calls the passed function in intervals, as long as that returns true. + + + With StopBackgroundCalls, you can stop threads started with this method. + The resulting ThreadAbortException is caught and discarded. + + The function to call. Must return true, if it should be called again. Returning false ends the thread. + Milliseconds to sleep between calls of myThread. Default: 100ms. + An optional name for the task to help debugging. Null or empty won't set the thread.Name. + + + + Calls Abort on the thread with the given id (= index of the thread list) + + + The resulting ThreadAbortException is caught and discarded. + + The unique ID of the thread. + True if the thread is canceled and false otherwise, e.g. if the thread with the given ID does not exist. + + + + Calls Abort on all threads that were started via StartBackgroundCalls. + + + The resulting ThreadAbortException is caught and discarded. + + True if any thread got aborted. + + + + Writes the exception's stack trace to the received stream. + + Exception to obtain information from. + Output sream used to write to. + + + + Writes the exception's stack trace to the received stream. Writes to: System.Diagnostics.Debug. + + Exception to obtain information from. + + + + This method returns a string, representing the content of the given IDictionary. + Returns "null" if parameter is null. + + IDictionary to return as string. + + + + + Converts a byte-array to string (useful as debugging output). + Uses BitConverter.ToString(list) internally after a null-check of list. + + Byte-array to convert to string. + + List of bytes as string. + + + + + Class to wrap static access to the random.Next() call in a thread safe manner. + + + + + An Attribute named "Preserve" tells Unity to not strip the code. + + + + TCP "Package" header: 7 bytes + + + TCP "Message" header: 2 bytes + + + TCP header combined: 9 bytes + + + Defines if the (TCP) socket implementation needs to do "framing". + The WebSocket protocol (e.g.) includes framing, so when that is used, we set DoFraming to false. + + + + Checks the incoming queue and Dispatches received data if possible. Returns if a Dispatch happened or + not, which shows if more Dispatches might be needed. + + + + + gathers commands from all (out)queues until udp-packet is full and sends it! + + + + Sends a ping in intervals to keep connection alive (server will timeout connection if nothing is sent). + Always false in this case (local queues are ignored. true would be: "call again to send remaining data"). + + + enqueues serialized operations to be sent as tcp stream / package + + + Sends a ping and modifies this.lastPingResult to avoid another ping for a while. + + + reads incoming tcp-packages to create and queue incoming commands* + + + + Only in use as long as PhotonPeer.TrafficStatsEnabled = true; + + + + Gets sum of outgoing operations in bytes. + + + Gets count of outgoing operations. + + + Gets sum of byte-cost of incoming operation-results. + + + Gets count of incoming operation-results. + + + Gets sum of byte-cost of incoming events. + + + Gets count of incoming events. + + + + Gets longest time it took to complete a call to OnOperationResponse (in your code). + If such a callback takes long, it will lower the network performance and might lead to timeouts. + + + + Gets OperationCode that causes the LongestOpResponseCallback. See that description. + + + + Gets longest time a call to OnEvent (in your code) took. + If such a callback takes long, it will lower the network performance and might lead to timeouts. + + + + Gets EventCode that caused the LongestEventCallback. See that description. + + + + Gets longest time between subsequent calls to DispatchIncomgingCommands in milliseconds. + Note: This is not a crucial timing for the networking. Long gaps just add "local lag" to events that are available already. + + + + + Gets longest time between subsequent calls to SendOutgoingCommands in milliseconds. + Note: This is a crucial value for network stability. Without calling SendOutgoingCommands, + nothing will be sent to the server, who might time out this client. + + + + + Gets number of calls of DispatchIncomingCommands. + + + + + Gets number of calls of DispatchIncomingCommands. + + + + + Gets number of calls of SendOutgoingCommands. + + + + Gets sum of byte-cost of all "logic level" messages. + + + Gets sum of counted "logic level" messages. + + + Gets sum of byte-cost of all incoming "logic level" messages. + + + Gets sum of counted incoming "logic level" messages. + + + Gets sum of byte-cost of all outgoing "logic level" messages (= OperationByteCount). + + + Gets sum of counted outgoing "logic level" messages (= OperationCount). + + + + Resets the values that can be maxed out, like LongestDeltaBetweenDispatching. See remarks. + + + Set to 0: LongestDeltaBetweenDispatching, LongestDeltaBetweenSending, LongestEventCallback, LongestEventCallbackCode, LongestOpResponseCallback, LongestOpResponseCallbackOpCode. + Also resets internal values: timeOfLastDispatchCall and timeOfLastSendCall (so intervals are tracked correctly). + + + + Gets the byte-size of per-package headers. + + + + Counts commands created/received by this client, ignoring repeats (out command count can be higher due to repeats). + + + + Gets count of bytes as traffic, excluding UDP/TCP headers (42 bytes / x bytes). + + + Timestamp of the last incoming ACK that has been read (every PhotonPeer.TimePingInterval milliseconds this client sends a PING which must be ACKd). + + + Timestamp of last incoming reliable command (every second we expect a PING). + +
+
diff --git a/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml.meta b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml.meta new file mode 100644 index 0000000..b58d8aa --- /dev/null +++ b/Assets/Photon/PhotonLibs/netstandard2.0/Photon3Unity3D.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7015e500cd5b71244af56448dfb59804 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json index 7207356..a037ed7 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -4,6 +4,7 @@ "com.unity.ide.visualstudio": "2.0.14", "com.unity.ide.vscode": "1.2.5", "com.unity.inputsystem": "1.3.0", + "com.unity.nuget.mono-cecil": "1.10.2", "com.unity.render-pipelines.universal": "12.1.5", "com.unity.test-framework": "1.1.31", "com.unity.textmeshpro": "3.0.6", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 6745891..1774ff9 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -57,6 +57,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.nuget.mono-cecil": { + "version": "1.10.2", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.render-pipelines.core": { "version": "12.1.5", "depth": 1, diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 2f4712b..0be9294 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -764,7 +764,8 @@ PlayerSettings: webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLDecompressionFallback: 0 - scriptingDefineSymbols: {} + scriptingDefineSymbols: + Standalone: FUSION_WEAVER additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {}