mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
Initial Push
This commit is contained in:
parent
98061c0f59
commit
854de5644d
8
Assets/Mirage.Profiler.meta
Normal file
8
Assets/Mirage.Profiler.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: faf041ce44aaa4df69ab8e633c2b21ec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
366
Assets/Mirage.Profiler/CHANGELOG.md
Executable file
366
Assets/Mirage.Profiler/CHANGELOG.md
Executable file
@ -0,0 +1,366 @@
|
||||
# [1.1.0-mirror.11](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.10...v1.1.0-mirror.11) (2023-08-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding define to support older versions of mirror ([86c29cd](https://github.com/James-Frowen/Mirage.Profiler/commit/86c29cd70ca57913d7ec5678cb126abd3737beac))
|
||||
|
||||
# [1.1.0-mirror.10](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.9...v1.1.0-mirror.10) (2022-09-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding support for counter in built player ([506e904](https://github.com/James-Frowen/Mirage.Profiler/commit/506e9048571ba3e75ec9561ca1144db5685245c5))
|
||||
|
||||
# [1.1.0-mirror.9](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.8...v1.1.0-mirror.9) (2022-09-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* names for mirror rpcs ([#5](https://github.com/James-Frowen/Mirage.Profiler/issues/5)) ([6bd5fa5](https://github.com/James-Frowen/Mirage.Profiler/commit/6bd5fa5b24084fc8a7a0f043e1c5f083ce1a00e2))
|
||||
|
||||
# [1.1.0-mirror.8](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.7...v1.1.0-mirror.8) (2022-09-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixes null ref when sort header is null ([9848b81](https://github.com/James-Frowen/Mirage.Profiler/commit/9848b818713228f3b3784fe66fda58b55de9d095)), closes [#4](https://github.com/James-Frowen/Mirage.Profiler/issues/4)
|
||||
* fixing header being squashed if vertical scroll is full ([89e651e](https://github.com/James-Frowen/Mirage.Profiler/commit/89e651e26e3c55415648e6fa6f1002c32132ddfb))
|
||||
* fixing null ref when group message is false ([6d3dfbf](https://github.com/James-Frowen/Mirage.Profiler/commit/6d3dfbfe365196529ba040ad533773e7f5b9aa03)), closes [#6](https://github.com/James-Frowen/Mirage.Profiler/issues/6)
|
||||
* stopping exception if rpc name does not contain dot ([#3](https://github.com/James-Frowen/Mirage.Profiler/issues/3)) ([4f7a1bd](https://github.com/James-Frowen/Mirage.Profiler/commit/4f7a1bd40333449dfb82969d7ef76b7c62801167))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding group message to save data ([12c3313](https://github.com/James-Frowen/Mirage.Profiler/commit/12c3313e6b557d6afb25844783287a38c22c6878))
|
||||
|
||||
# [1.1.0-mirror.7](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.6...v1.1.0-mirror.7) (2022-09-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* only add NetworkDiagnostics events for first static instance ([#2](https://github.com/James-Frowen/Mirage.Profiler/issues/2)) ([d827c4d](https://github.com/James-Frowen/Mirage.Profiler/commit/d827c4d4296915ac237d10540b7daf4ece5442fd))
|
||||
|
||||
# [1.1.0-mirror.6](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.5...v1.1.0-mirror.6) (2022-08-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removing mirage from paackage.json ([0b0032b](https://github.com/James-Frowen/Mirage.Profiler/commit/0b0032b8b449214c20b1836ecb21e70b3696eebb))
|
||||
|
||||
# [1.1.0-mirror.5](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.4...v1.1.0-mirror.5) (2022-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* movng files into package ([b1d50bf](https://github.com/James-Frowen/Mirage.Profiler/commit/b1d50bf7137083e0c071277f6880b67455ef1761))
|
||||
|
||||
# [1.1.0-mirror.4](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.3...v1.1.0-mirror.4) (2022-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing version not being added by CI ([08d48cf](https://github.com/James-Frowen/Mirage.Profiler/commit/08d48cfae980798cda5e8334f0b225c3cc640dcc))
|
||||
|
||||
# [1.1.0-mirror.3](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.2...v1.1.0-mirror.3) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding missing meta file ([318e209](https://github.com/James-Frowen/Mirage.Profiler/commit/318e20989a7690ea886ab442e15141cdea96261b))
|
||||
|
||||
# [1.1.0-mirror.2](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.1.0-mirror.1...v1.1.0-mirror.2) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding runtime folder to fix package layout ([e606a0f](https://github.com/James-Frowen/Mirage.Profiler/commit/e606a0fa356c8a837dff5809dfa0fa85f0267d6f))
|
||||
|
||||
# [1.1.0-mirror.1](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0...v1.1.0-mirror.1) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing error when rpc name is empty ([2502325](https://github.com/James-Frowen/Mirage.Profiler/commit/25023256734cd730cc95b06d1ebad8f2677324e7))
|
||||
* fixing module names for mirror ([94d77b1](https://github.com/James-Frowen/Mirage.Profiler/commit/94d77b129964559a1f8e68b49d9b345768c81532))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* example scene for mirror ([c707be8](https://github.com/James-Frowen/Mirage.Profiler/commit/c707be8e9d2d5c71cf609a8f11d1174d4cd0c1af))
|
||||
* Mirror Support ([1675f77](https://github.com/James-Frowen/Mirage.Profiler/commit/1675f776264c10421802028f1a30b422fdb81215))
|
||||
|
||||
# 1.0.0 (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding note about maybe missing NetworkProfilerBehaviour ([91b2420](https://github.com/James-Frowen/Mirage.Profiler/commit/91b242030cf64cc7a80bd0b5e8f3e38317e6a246))
|
||||
* changing order to be int.max ([4c40e61](https://github.com/James-Frowen/Mirage.Profiler/commit/4c40e61bc23884dff13de35db84dd8ed4abc0724))
|
||||
* clearing byte count at end of frame ([f2270a8](https://github.com/James-Frowen/Mirage.Profiler/commit/f2270a8960543a8bfd70fbc2a8fc628e788a1a43))
|
||||
* disabling debug message by default ([7f16136](https://github.com/James-Frowen/Mirage.Profiler/commit/7f161365b0f54d32e34773b3eb5645046b12bed4))
|
||||
* enabling example bullet on server ([ea15bf6](https://github.com/James-Frowen/Mirage.Profiler/commit/ea15bf6301b736a328f51edadf5f127cdf704729))
|
||||
* fixing client spawn in example ([5ff2c0a](https://github.com/James-Frowen/Mirage.Profiler/commit/5ff2c0ae7ed0a3c89baff1aed7316761d7342338))
|
||||
* fixing crash when closing unity ([cf2513a](https://github.com/James-Frowen/Mirage.Profiler/commit/cf2513ac150fc67533d6c41e9e44ec894d81ca0e))
|
||||
* fixing define to hide debug toggle ([8d22bf7](https://github.com/James-Frowen/Mirage.Profiler/commit/8d22bf7cd6ccf32b0af0866235587c136d09072b))
|
||||
* fixing example auto connect ([6b5163e](https://github.com/James-Frowen/Mirage.Profiler/commit/6b5163e9577e0b8304d78a5ec87df7650f63fe9d))
|
||||
* fixing example where field was not being used ([4417cdc](https://github.com/James-Frowen/Mirage.Profiler/commit/4417cdcf0504723ba25e8e342c1c91757fd11743))
|
||||
* fixing frame index for saved messages ([49d9e70](https://github.com/James-Frowen/Mirage.Profiler/commit/49d9e705b7db99e05b3089eed502553369752522))
|
||||
* fixing groups not un-expanding ([196feae](https://github.com/James-Frowen/Mirage.Profiler/commit/196feae6e7e49f54478f575c7c3668160b01e0e3))
|
||||
* fixing index out of range on first frame ([036f104](https://github.com/James-Frowen/Mirage.Profiler/commit/036f10487e469c4cf39fe6b431c2f22499157b6d))
|
||||
* fixing player count sample being skipped ([740b4f8](https://github.com/James-Frowen/Mirage.Profiler/commit/740b4f8816dfd783e7465856f0380c7fc8b9b2f2))
|
||||
* fixing profier behaviour ([53f0614](https://github.com/James-Frowen/Mirage.Profiler/commit/53f06143befb02caa8c415a05fb10d53bf484906))
|
||||
* fixing ProfilerCounter ([81344fe](https://github.com/James-Frowen/Mirage.Profiler/commit/81344fe871224a773d014b9bb76f028ff4aba21d))
|
||||
* fixing sort arrow not updated ([3ced312](https://github.com/James-Frowen/Mirage.Profiler/commit/3ced312f20f5c24657bd635fc93ced5b40ffca02))
|
||||
* fixing unity skipping profiler frames becuase unity is buggy ([f452ac4](https://github.com/James-Frowen/Mirage.Profiler/commit/f452ac4a1eb45bf4713d1769895ec431c24550ba))
|
||||
* init array with non-null elements ([b3c3276](https://github.com/James-Frowen/Mirage.Profiler/commit/b3c32761b0e6a62ff4180fde276131709d1956d6))
|
||||
* making example clients auto reconnect ([cbf27b7](https://github.com/James-Frowen/Mirage.Profiler/commit/cbf27b7abdb0ee955b77c9d7f977241b1ad3e7bc))
|
||||
* making header scroll Horizontally ([5eac0f7](https://github.com/James-Frowen/Mirage.Profiler/commit/5eac0f71a8569c580bc03587d63b9fea418be326))
|
||||
* making message info fields serializable ([81fb20d](https://github.com/James-Frowen/Mirage.Profiler/commit/81fb20da066d271fb72c56a763ccd0fe106cbc1a))
|
||||
* naming debug name shorter ([36f2d6a](https://github.com/James-Frowen/Mirage.Profiler/commit/36f2d6a5c76e6391ba16d44d8f09d44625be9d11))
|
||||
* removing debug logs ([0bb90ef](https://github.com/James-Frowen/Mirage.Profiler/commit/0bb90ef9dc674e5596ef79994259f96fd378f2d7))
|
||||
* removing prefix from summary labels ([3841a9e](https://github.com/James-Frowen/Mirage.Profiler/commit/3841a9e3c65ee7432a4d2020ed03a4f7557f14d6))
|
||||
* removing verbose log ([0cfd3bf](https://github.com/James-Frowen/Mirage.Profiler/commit/0cfd3bf4d9364871da49dcbf9a084179e67f4a20))
|
||||
* scroll bar for table ([af7c83b](https://github.com/James-Frowen/Mirage.Profiler/commit/af7c83b13fb13aff81f7fc1648eb48597849525b))
|
||||
* setting max connections in example to 1000 ([94d06ea](https://github.com/James-Frowen/Mirage.Profiler/commit/94d06ea9723cadf1214b0a92ece59f2788389305))
|
||||
* stoping sort mode going back to none ([7745700](https://github.com/James-Frowen/Mirage.Profiler/commit/77457001775bc6f6889c7c8f4f86bacdadbd90e1))
|
||||
* updating mirage for fix ([e367c4a](https://github.com/James-Frowen/Mirage.Profiler/commit/e367c4a3c60e065effc80d4896998d5ef6c310c9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding basic column sort ([39d4519](https://github.com/James-Frowen/Mirage.Profiler/commit/39d4519ce8a2fe086189bf12cdb34ce3e1820fe2))
|
||||
* adding object name column ([239e069](https://github.com/James-Frowen/Mirage.Profiler/commit/239e069dd0aecc080413325eb98edf7ccc3b1eb1))
|
||||
* adding option to group messages by type ([2e5fc27](https://github.com/James-Frowen/Mirage.Profiler/commit/2e5fc276bacc93f45c444a3b637d0d716bc1db1b))
|
||||
* adding option to run example outside of editor ([5a75b65](https://github.com/James-Frowen/Mirage.Profiler/commit/5a75b65b0f3f3075d21c2cdd0d821655bf2a47e3))
|
||||
* adding rpc name to message info ([d90d842](https://github.com/James-Frowen/Mirage.Profiler/commit/d90d842bc57defe10b7af8b1b2b6c2bd70566df1))
|
||||
* adding rpc name to table ([63722e2](https://github.com/James-Frowen/Mirage.Profiler/commit/63722e29458a522d10afa96d6a75831a5d084495))
|
||||
* adding shooting to example ([6a86354](https://github.com/James-Frowen/Mirage.Profiler/commit/6a86354895f2effe3689b4638f195d8536107a19))
|
||||
* adding sorting for groups ([2dec9a5](https://github.com/James-Frowen/Mirage.Profiler/commit/2dec9a5bfef76c34dff68a4aa904590c0924dc86))
|
||||
* expandable groups for message types ([001de86](https://github.com/James-Frowen/Mirage.Profiler/commit/001de86db446769d12880b35e64147bfda84f755))
|
||||
* first prerelease ([7fbd8ce](https://github.com/James-Frowen/Mirage.Profiler/commit/7fbd8ceb9223321abc514a3178d7703fe92f9497))
|
||||
* first release ([55b2b39](https://github.com/James-Frowen/Mirage.Profiler/commit/55b2b395e10da952101ae101f2cf2ab1acefe664))
|
||||
* only showing rpc short name, full name in tooltip ([9f71ae7](https://github.com/James-Frowen/Mirage.Profiler/commit/9f71ae78a546b0c8543d4295e8c2944a3ec458bd))
|
||||
* putting table header above scrollview ([a7d52ff](https://github.com/James-Frowen/Mirage.Profiler/commit/a7d52fff785b0f625a8af5abd920cc06d0a38053))
|
||||
* returning saved message instead of ones from counter ([6e894fa](https://github.com/James-Frowen/Mirage.Profiler/commit/6e894faec6701346f738925057cf360554e74860))
|
||||
* saving group expanded in save data ([70e4da9](https://github.com/James-Frowen/Mirage.Profiler/commit/70e4da92c1d8e35518eaa4656ff87d5276843a62))
|
||||
* saving message for frames ([131257a](https://github.com/James-Frowen/Mirage.Profiler/commit/131257aa7c603da085e10c8d5103a6776c7a8a38))
|
||||
* using sort from save data ([6bded42](https://github.com/James-Frowen/Mirage.Profiler/commit/6bded42b36bc8f7767cce355de220157c630093f))
|
||||
* working save data for message table ([0ab9197](https://github.com/James-Frowen/Mirage.Profiler/commit/0ab9197870fd581f68a1b405f3572c7b04c9f39e))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* replacing FindObjectOfType with reference given in setup ([71e6712](https://github.com/James-Frowen/Mirage.Profiler/commit/71e671247dd5394d806ca1017b91aa2faa68eea8))
|
||||
|
||||
# [1.0.0-beta.24](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.23...v1.0.0-beta.24) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* making header scroll Horizontally ([7ba9d26](https://github.com/James-Frowen/Mirage.Profiler/commit/7ba9d2667afcf32269c979f3df4769e3e86a3d6c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding rpc name to message info ([52a7bb4](https://github.com/James-Frowen/Mirage.Profiler/commit/52a7bb4927e009eec5839e30c94bee1345e4a4b9))
|
||||
* adding rpc name to table ([fd1e762](https://github.com/James-Frowen/Mirage.Profiler/commit/fd1e762a22e9ce5ec10f92105613f8f11f5aad6d))
|
||||
* only showing rpc short name, full name in tooltip ([e346a6f](https://github.com/James-Frowen/Mirage.Profiler/commit/e346a6f7d403113057fd067e89cb470e594fd4c3))
|
||||
|
||||
# [1.0.0-beta.23](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.22...v1.0.0-beta.23) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing sort arrow not updated ([bf8cd9a](https://github.com/James-Frowen/Mirage.Profiler/commit/bf8cd9adeec12ca2898918e40c611e040e58013e))
|
||||
|
||||
# [1.0.0-beta.22](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.21...v1.0.0-beta.22) (2022-08-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* saving group expanded in save data ([557538c](https://github.com/James-Frowen/Mirage.Profiler/commit/557538c6cf9d06b456def4e34c0b8add6683bd7b))
|
||||
|
||||
# [1.0.0-beta.21](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2022-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing index out of range on first frame ([b1a4a6b](https://github.com/James-Frowen/Mirage.Profiler/commit/b1a4a6b7ca21154d32758fcb577ccd8c22c55d7e))
|
||||
* fixing player count sample being skipped ([04d7341](https://github.com/James-Frowen/Mirage.Profiler/commit/04d7341d0e3cbf7a91208a0b5f0df567644d7030))
|
||||
* setting max connections in example to 1000 ([94dc143](https://github.com/James-Frowen/Mirage.Profiler/commit/94dc14329c3c1799f85c210f11ece1879bb6fa47))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding object name column ([d3de7e3](https://github.com/James-Frowen/Mirage.Profiler/commit/d3de7e334530068cc7776fb000afbaaf21dd30b4))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* replacing FindObjectOfType with reference given in setup ([df9141d](https://github.com/James-Frowen/Mirage.Profiler/commit/df9141df18139a1020222c60402c4fedd3259a38))
|
||||
|
||||
# [1.0.0-beta.20](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2022-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing example where field was not being used ([1e6f605](https://github.com/James-Frowen/Mirage.Profiler/commit/1e6f605e8cd9953daf6f9482c72dabe069c7b300))
|
||||
|
||||
# [1.0.0-beta.19](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2022-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removing verbose log ([fe0541a](https://github.com/James-Frowen/Mirage.Profiler/commit/fe0541a7859eadd5ce390c6449dd83694c058d08))
|
||||
|
||||
# [1.0.0-beta.18](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2022-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing crash when closing unity ([eacc82c](https://github.com/James-Frowen/Mirage.Profiler/commit/eacc82ca37b4bce169ed03ae2336474af1936760))
|
||||
* fixing unity skipping profiler frames becuase unity is buggy ([4e23dff](https://github.com/James-Frowen/Mirage.Profiler/commit/4e23dffc07ed2d7f48dd9a0d4257806c93ba7c3f))
|
||||
|
||||
# [1.0.0-beta.17](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2022-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removing debug logs ([3c0d019](https://github.com/James-Frowen/Mirage.Profiler/commit/3c0d01905c1c3b659a4e5453c79d63a3ba506bfe))
|
||||
|
||||
# [1.0.0-beta.16](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2022-08-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* clearing byte count at end of frame ([d185ad4](https://github.com/James-Frowen/Mirage.Profiler/commit/d185ad43c9d716aa44bbb715b3eed89b16d19706))
|
||||
* enabling example bullet on server ([bc58f72](https://github.com/James-Frowen/Mirage.Profiler/commit/bc58f72ff2946b1f4a96821d36e3dfc88fed0f82))
|
||||
* fixing client spawn in example ([348e98b](https://github.com/James-Frowen/Mirage.Profiler/commit/348e98b4de4c36be9c118cfa6242827126422cd9))
|
||||
* fixing example auto connect ([65968de](https://github.com/James-Frowen/Mirage.Profiler/commit/65968ded4aa7ea2fd0c04739cb0e272709ff7d67))
|
||||
* fixing frame index for saved messages ([1204a1f](https://github.com/James-Frowen/Mirage.Profiler/commit/1204a1f4f32d497a260a4720937edde67d6cd9ed))
|
||||
* fixing profier behaviour ([bc91dc7](https://github.com/James-Frowen/Mirage.Profiler/commit/bc91dc78b71d9893adb48dac991433631fb7a34f))
|
||||
* fixing ProfilerCounter ([9a7f62a](https://github.com/James-Frowen/Mirage.Profiler/commit/9a7f62a4d4ce2ff42bfb1cd0ad0b712ac7b46ac7))
|
||||
* init array with non-null elements ([f69c268](https://github.com/James-Frowen/Mirage.Profiler/commit/f69c2684de2b242fe337b051d274d84b48577ebc))
|
||||
* making example clients auto reconnect ([8ca5eee](https://github.com/James-Frowen/Mirage.Profiler/commit/8ca5eeeb102e54e12a33490c878fd35156cb6ff0))
|
||||
* making message info fields serializable ([4cbc905](https://github.com/James-Frowen/Mirage.Profiler/commit/4cbc905e513a62ee17ddf660d524363464e080c8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding option to run example outside of editor ([5de8073](https://github.com/James-Frowen/Mirage.Profiler/commit/5de80731e41513564ccf20ba67fb29eb4e01adf1))
|
||||
* adding shooting to example ([543374a](https://github.com/James-Frowen/Mirage.Profiler/commit/543374a94c0a65362fcabaf0ca190d49163e372d))
|
||||
* returning saved message instead of ones from counter ([541ec66](https://github.com/James-Frowen/Mirage.Profiler/commit/541ec663cda04fb2b10c73822a6e28bd221ac6b7))
|
||||
* saving message for frames ([519f256](https://github.com/James-Frowen/Mirage.Profiler/commit/519f25695ad0ca7b113a65de14c7feb93c42d4f0))
|
||||
* working save data for message table ([ffaaef9](https://github.com/James-Frowen/Mirage.Profiler/commit/ffaaef97e4ee05751be419e642f61b923857486f))
|
||||
|
||||
# [1.0.0-beta.15](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2022-08-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* stoping sort mode going back to none ([5c85668](https://github.com/James-Frowen/Mirage.Profiler/commit/5c856680631869769e41206c72fb024e90e74cd9))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* using sort from save data ([cdc65ad](https://github.com/James-Frowen/Mirage.Profiler/commit/cdc65ad3d72791e74d51d76df700eb3993a4576d))
|
||||
|
||||
# [1.0.0-beta.14](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2022-08-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* adding note about maybe missing NetworkProfilerBehaviour ([5120029](https://github.com/James-Frowen/Mirage.Profiler/commit/5120029c3d30f5a141d0a86e28501dc52a6d7a7d))
|
||||
|
||||
# [1.0.0-beta.13](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2022-08-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* changing order to be int.max ([31cbd2f](https://github.com/James-Frowen/Mirage.Profiler/commit/31cbd2ff5b9b9a476d9c9562a19c7d3aec894eb1))
|
||||
|
||||
# [1.0.0-beta.12](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2022-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing groups not un-expanding ([17c27c2](https://github.com/James-Frowen/Mirage.Profiler/commit/17c27c24a22f40a9e240e9f0a68933ad531bd192))
|
||||
|
||||
# [1.0.0-beta.11](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2022-07-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* putting table header above scrollview ([6d52a00](https://github.com/James-Frowen/Mirage.Profiler/commit/6d52a004e8859d8930063b4610f24512d3016113))
|
||||
|
||||
# [1.0.0-beta.10](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.9...v1.0.0-beta.10) (2022-07-28)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding sorting for groups ([bd655e8](https://github.com/James-Frowen/Mirage.Profiler/commit/bd655e89338f1f03b32f600288be6dd06edc6a8f))
|
||||
|
||||
# [1.0.0-beta.9](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2022-07-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding basic column sort ([1ce56a8](https://github.com/James-Frowen/Mirage.Profiler/commit/1ce56a878444597bf4946bf573264c9ebd156581))
|
||||
|
||||
# [1.0.0-beta.8](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.7...v1.0.0-beta.8) (2022-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* disabling debug message by default ([ad1e082](https://github.com/James-Frowen/Mirage.Profiler/commit/ad1e08243d1f4e443468b32682eb44ede8c3ffa4))
|
||||
* removing prefix from summary labels ([6836ff5](https://github.com/James-Frowen/Mirage.Profiler/commit/6836ff547c7f2340baca8a68064c7556ef0ccfe6))
|
||||
|
||||
# [1.0.0-beta.7](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.6...v1.0.0-beta.7) (2022-07-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* expandable groups for message types ([20f949c](https://github.com/James-Frowen/Mirage.Profiler/commit/20f949ceee10951cf4215451afbbea33d3f0d63f))
|
||||
|
||||
# [1.0.0-beta.6](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2022-07-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fixing define to hide debug toggle ([5798c5c](https://github.com/James-Frowen/Mirage.Profiler/commit/5798c5c0f13c5614c8ce507736820ac6bfbfa04a))
|
||||
|
||||
# [1.0.0-beta.5](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2022-07-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding option to group messages by type ([9392ebb](https://github.com/James-Frowen/Mirage.Profiler/commit/9392ebb6d324db1cc4fb09f28909acde371ba198))
|
||||
|
||||
# [1.0.0-beta.4](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2022-07-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* naming debug name shorter ([1a2ad87](https://github.com/James-Frowen/Mirage.Profiler/commit/1a2ad8765b1cfbd9a4033da6de7a18e1c38d3632))
|
||||
|
||||
# [1.0.0-beta.3](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2022-07-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* scroll bar for table ([d7b697f](https://github.com/James-Frowen/Mirage.Profiler/commit/d7b697f1556300a22773f4f9e04718c7d228203e))
|
||||
|
||||
# [1.0.0-beta.2](https://github.com/James-Frowen/Mirage.Profiler/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2022-07-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* updating mirage for fix ([69ded5e](https://github.com/James-Frowen/Mirage.Profiler/commit/69ded5e7ac44130226362da61bddde2f275d0b98))
|
||||
|
||||
# 1.0.0-beta.1 (2022-07-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* first prerelease ([e253142](https://github.com/James-Frowen/Mirage.Profiler/commit/e25314230a8e322e351912ac9f960f6f29821be5))
|
7
Assets/Mirage.Profiler/CHANGELOG.md.meta
Executable file
7
Assets/Mirage.Profiler/CHANGELOG.md.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b2c5211880da6c4f83e37f1222441e1
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
8
Assets/Mirage.Profiler/Editor.meta
Executable file
8
Assets/Mirage.Profiler/Editor.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9eb58b4bf960c1439443c3160ce8371
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
3
Assets/Mirage.Profiler/Editor/AssemblyInfo.cs
Executable file
3
Assets/Mirage.Profiler/Editor/AssemblyInfo.cs
Executable file
@ -0,0 +1,3 @@
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion("1.1.0.11")]
|
11
Assets/Mirage.Profiler/Editor/AssemblyInfo.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/AssemblyInfo.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6efabd9b23eecd0459622d955c6a2b4e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
7
Assets/Mirage.Profiler/Editor/ICountRecorderProvider.cs
Executable file
7
Assets/Mirage.Profiler/Editor/ICountRecorderProvider.cs
Executable file
@ -0,0 +1,7 @@
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
internal interface ICountRecorderProvider
|
||||
{
|
||||
CountRecorder GetCountRecorder();
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/ICountRecorderProvider.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/ICountRecorderProvider.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7ff307d04db0b64690d8c9692e40d8c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
8
Assets/Mirage.Profiler/Editor/Messages.meta
Executable file
8
Assets/Mirage.Profiler/Editor/Messages.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 667ec5fd9bee70844a2b458410782b0a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
89
Assets/Mirage.Profiler/Editor/Messages/Columns.cs
Executable file
89
Assets/Mirage.Profiler/Editor/Messages/Columns.cs
Executable file
@ -0,0 +1,89 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal sealed class Columns : IEnumerable<ColumnInfo>
|
||||
{
|
||||
private const int EXPAND_WIDTH = 25;
|
||||
private const int FULL_NAME_WIDTH = 300;
|
||||
private const int NAME_WIDTH = 150;
|
||||
private const int OTHER_WIDTH = 100;
|
||||
|
||||
private readonly ColumnInfo[] _columns;
|
||||
|
||||
public readonly ColumnInfo Expand;
|
||||
public readonly ColumnInfo FullName;
|
||||
public readonly ColumnInfo TotalBytes;
|
||||
public readonly ColumnInfo Count;
|
||||
public readonly ColumnInfo BytesPerMessage;
|
||||
public readonly ColumnInfo NetId;
|
||||
public readonly ColumnInfo ObjectName;
|
||||
public readonly ColumnInfo RpcName;
|
||||
|
||||
public Columns()
|
||||
{
|
||||
Expand = new ColumnInfo("+", EXPAND_WIDTH, x => "");
|
||||
|
||||
FullName = new ColumnInfo("Message", FULL_NAME_WIDTH, x => x.Name);
|
||||
FullName.AddSort(m => m.Name, m => m.Name);
|
||||
|
||||
TotalBytes = new ColumnInfo("Total Bytes", OTHER_WIDTH, x => x.TotalBytes.ToString());
|
||||
TotalBytes.AddSort(m => m.TotalBytes, m => m.TotalBytes);
|
||||
|
||||
Count = new ColumnInfo("Count", OTHER_WIDTH, x => x.Count.ToString());
|
||||
Count.AddSort(m => m.TotalCount, m => m.Count);
|
||||
|
||||
BytesPerMessage = new ColumnInfo("Bytes", OTHER_WIDTH, x => x.Bytes.ToString());
|
||||
BytesPerMessage.AddSort(null, m => m.Bytes);
|
||||
|
||||
NetId = new ColumnInfo("Net id", OTHER_WIDTH, x => x.NetId.HasValue ? x.NetId.ToString() : "");
|
||||
NetId.AddSort(null, m => m.NetId.GetValueOrDefault());
|
||||
|
||||
ObjectName = new ColumnInfo("GameObject Name", NAME_WIDTH, x => x.ObjectName);
|
||||
ObjectName.AddSort(null, m => m.ObjectName);
|
||||
|
||||
RpcName = new ColumnInfo("RPC Name (hover for full name)", FULL_NAME_WIDTH, x => RpcShortName(x.RpcName));
|
||||
RpcName.AddSort(null, m => m.RpcName);
|
||||
// full name in tooltip
|
||||
RpcName.AddToolTip(m => m.RpcName);
|
||||
|
||||
_columns = new ColumnInfo[] {
|
||||
|
||||
Expand,
|
||||
FullName,
|
||||
TotalBytes,
|
||||
Count,
|
||||
BytesPerMessage,
|
||||
NetId,
|
||||
ObjectName,
|
||||
RpcName,
|
||||
};
|
||||
}
|
||||
|
||||
private string RpcShortName(string fullName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fullName))
|
||||
return string.Empty;
|
||||
|
||||
const char separator = '.';
|
||||
|
||||
if (fullName.Contains(separator))
|
||||
{
|
||||
var split = fullName.Split(separator);
|
||||
var count = split.Length;
|
||||
if (count >= 2)
|
||||
{
|
||||
return $"{split[count - 2]}.{split[count - 1]}";
|
||||
}
|
||||
}
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
|
||||
public IEnumerator<ColumnInfo> GetEnumerator() => ((IEnumerable<ColumnInfo>)_columns).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => _columns.GetEnumerator();
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/Columns.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/Columns.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36dff191a3209c444844bbdc74f60cd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
16
Assets/Mirage.Profiler/Editor/Messages/CounterNames.cs
Executable file
16
Assets/Mirage.Profiler/Editor/Messages/CounterNames.cs
Executable file
@ -0,0 +1,16 @@
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal struct CounterNames
|
||||
{
|
||||
public readonly string Count;
|
||||
public readonly string Bytes;
|
||||
public readonly string PerSecond;
|
||||
|
||||
public CounterNames(string count, string bytes, string perSecond)
|
||||
{
|
||||
Count = count;
|
||||
Bytes = bytes;
|
||||
PerSecond = perSecond;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/CounterNames.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/CounterNames.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2422d6a68fe533e4c9da13ae30767fcf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
10
Assets/Mirage.Profiler/Editor/Messages/DrawnMessage.cs
Executable file
10
Assets/Mirage.Profiler/Editor/Messages/DrawnMessage.cs
Executable file
@ -0,0 +1,10 @@
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal class DrawnMessage
|
||||
{
|
||||
public MessageInfo Info;
|
||||
public Row Row;
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/DrawnMessage.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/DrawnMessage.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: df1ae3a59995b1a43b6b76e16d2b8f3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
123
Assets/Mirage.Profiler/Editor/Messages/Group.cs
Executable file
123
Assets/Mirage.Profiler/Editor/Messages/Group.cs
Executable file
@ -0,0 +1,123 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal class Group
|
||||
{
|
||||
public readonly List<Row> Rows = new List<Row>();
|
||||
public readonly string Name;
|
||||
public readonly List<DrawnMessage> Messages = new List<DrawnMessage>();
|
||||
|
||||
private readonly Table _table;
|
||||
private readonly Columns _columns;
|
||||
|
||||
public Row Head;
|
||||
|
||||
public int TotalBytes { get; private set; }
|
||||
public int TotalCount { get; private set; }
|
||||
public int Order { get; private set; }
|
||||
|
||||
public bool Expanded { get; private set; }
|
||||
|
||||
public Group(string name, Table table, Columns columns)
|
||||
{
|
||||
Name = name;
|
||||
_table = table;
|
||||
_columns = columns;
|
||||
// start at max, then take min each time message is added
|
||||
Order = int.MaxValue;
|
||||
}
|
||||
|
||||
public void AddMessage(MessageInfo msg)
|
||||
{
|
||||
Messages.Add(new DrawnMessage { Info = msg });
|
||||
TotalBytes += msg.TotalBytes;
|
||||
TotalCount += msg.Count;
|
||||
Order = Math.Min(Order, msg.Order);
|
||||
}
|
||||
|
||||
public void ToggleExpand()
|
||||
{
|
||||
Expand(!Expanded);
|
||||
}
|
||||
|
||||
public void Expand(bool expanded)
|
||||
{
|
||||
Expanded = expanded;
|
||||
// create rows if needed
|
||||
LazyCreateRows();
|
||||
foreach (var row in Rows)
|
||||
{
|
||||
row.VisualElement.style.display = expanded ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
|
||||
// head can be null if ungrouped
|
||||
Head?.SetText(_columns.Expand, Expanded ? "-" : "+");
|
||||
}
|
||||
|
||||
public void LazyCreateRows()
|
||||
{
|
||||
// not visible, do nothing till row is expanded
|
||||
if (!Expanded)
|
||||
return;
|
||||
// already created
|
||||
if (Rows.Count > 0)
|
||||
return;
|
||||
|
||||
DrawMessages();
|
||||
}
|
||||
|
||||
|
||||
/// <param name="messages">Messages to add to table</param>
|
||||
/// <param name="createdRows">list to add rows to once created, Can be null</param>
|
||||
private void DrawMessages()
|
||||
{
|
||||
var previous = Head;
|
||||
var backgroundColor = GetBackgroundColor();
|
||||
|
||||
foreach (var drawn in Messages)
|
||||
{
|
||||
var row = _table.AddRow(previous);
|
||||
Rows.Add(row);
|
||||
|
||||
// set previous to be new row, so that message are added in order after previous
|
||||
previous = row;
|
||||
|
||||
drawn.Row = row;
|
||||
var info = drawn.Info;
|
||||
DrawMessage(row, info);
|
||||
|
||||
// set color of labels not whole row, otherwise color will be outside of table as well
|
||||
foreach (var ele in row.GetChildren())
|
||||
ele.style.backgroundColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMessage(Row row, MessageInfo info)
|
||||
{
|
||||
foreach (var column in _columns)
|
||||
{
|
||||
row.SetText(column, column.TextGetter.Invoke(info));
|
||||
if (column.HasToolTip)
|
||||
{
|
||||
var label = row.GetLabel(column);
|
||||
label.tooltip = column.ToolTipGetter.Invoke(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Color GetBackgroundColor()
|
||||
{
|
||||
// pick color that is lighter/darker than default editor background
|
||||
// todo check if there is a way to get the real color, or do we have to use `isProSkin`?
|
||||
return EditorGUIUtility.isProSkin
|
||||
? (Color)new Color32(56, 56, 56, 255) / 0.8f
|
||||
: (Color)new Color32(194, 194, 194, 255) * .8f;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/Group.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/Group.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d73a0b136989084c8caed7ae1349219
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
94
Assets/Mirage.Profiler/Editor/Messages/GroupSorter.cs
Executable file
94
Assets/Mirage.Profiler/Editor/Messages/GroupSorter.cs
Executable file
@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal struct GroupSorter
|
||||
{
|
||||
private readonly Dictionary<string, Group> _grouped;
|
||||
private readonly Func<Group, Group, int> _sortGroupFunc;
|
||||
private readonly Func<MessageInfo, MessageInfo, int> _sortMessageFunc;
|
||||
|
||||
private readonly SortMode _sortMode;
|
||||
|
||||
public GroupSorter(Dictionary<string, Group> grouped, ColumnInfo sortHeader, SortMode sortMode)
|
||||
{
|
||||
_grouped = grouped;
|
||||
_sortMode = sortMode;
|
||||
|
||||
// if header or sort is null, use default
|
||||
_sortGroupFunc = sortHeader?.SortGroup ?? DefaultGroupSort;
|
||||
_sortMessageFunc = sortHeader?.SortMessages ?? DefaultMessageSort;
|
||||
}
|
||||
|
||||
public void Sort()
|
||||
{
|
||||
var groups = new List<Group>(_grouped.Values);
|
||||
|
||||
// sort all groups and their messages
|
||||
groups.Sort(CompareGroupSortMode);
|
||||
foreach (var group in groups)
|
||||
{
|
||||
group.Messages.Sort(CompareDrawnSortMode);
|
||||
}
|
||||
|
||||
// apply sort to table
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// use BringToFront so that each new element is placed after the last one, bring them all to their correct position
|
||||
|
||||
// head might be null if messages are ungrouped
|
||||
group.Head?.VisualElement.BringToFront();
|
||||
foreach (var msg in group.Messages)
|
||||
{
|
||||
// row might be null before it is drawn for first time
|
||||
msg.Row?.VisualElement.BringToFront();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int CompareGroupSortMode(Group x, Group y)
|
||||
{
|
||||
var sort = _sortGroupFunc.Invoke(x, y);
|
||||
return CheckSortMode(sort);
|
||||
}
|
||||
|
||||
private int CompareDrawnSortMode(DrawnMessage x, DrawnMessage y)
|
||||
{
|
||||
var sort = _sortMessageFunc.Invoke(x.Info, y.Info);
|
||||
return CheckSortMode(sort);
|
||||
}
|
||||
|
||||
private int CheckSortMode(int sort)
|
||||
{
|
||||
// flip order if Descending
|
||||
if (_sortMode == SortMode.Descending)
|
||||
return -sort;
|
||||
|
||||
return sort;
|
||||
}
|
||||
|
||||
public static int Compare<T>(Group x, Group y, Func<Group, T> func) where T : IComparable<T>
|
||||
{
|
||||
var xValue = func.Invoke(x);
|
||||
var yValue = func.Invoke(y);
|
||||
return xValue.CompareTo(yValue);
|
||||
}
|
||||
public static int Compare<T>(MessageInfo x, MessageInfo y, Func<MessageInfo, T> func) where T : IComparable<T>
|
||||
{
|
||||
var xValue = func.Invoke(x);
|
||||
var yValue = func.Invoke(y);
|
||||
return xValue.CompareTo(yValue);
|
||||
}
|
||||
|
||||
public static int DefaultGroupSort(Group x, Group y)
|
||||
{
|
||||
return x.Order.CompareTo(y.Order);
|
||||
}
|
||||
public static int DefaultMessageSort(MessageInfo x, MessageInfo y)
|
||||
{
|
||||
return x.Order.CompareTo(y.Order);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/GroupSorter.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/GroupSorter.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b13bef8f87b23684bae6fc2453c090b0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
141
Assets/Mirage.Profiler/Editor/Messages/MessageView.cs
Executable file
141
Assets/Mirage.Profiler/Editor/Messages/MessageView.cs
Executable file
@ -0,0 +1,141 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal class MessageView
|
||||
{
|
||||
private readonly Columns _columns;
|
||||
private readonly Table _table;
|
||||
|
||||
/// <summary>
|
||||
/// cache list used to gather messages
|
||||
/// </summary>
|
||||
private readonly List<MessageInfo> _messages = new List<MessageInfo>();
|
||||
private readonly Dictionary<string, Group> _grouped = new Dictionary<string, Group>();
|
||||
|
||||
public event Action<Group, bool> OnGroupExpanded;
|
||||
|
||||
public MessageView(Columns columns, TableSorter sorter, VisualElement parent)
|
||||
{
|
||||
_columns = columns;
|
||||
_table = new Table(columns, sorter);
|
||||
parent.Add(_table.VisualElement);
|
||||
}
|
||||
|
||||
public void Draw(IEnumerable<Frame> frames, bool groupMessages)
|
||||
{
|
||||
CollectMessages(frames);
|
||||
GroupMessages(groupMessages);
|
||||
DrawGroups(groupMessages);
|
||||
|
||||
var expandColumn = _columns.Expand;
|
||||
var defaultWidth = expandColumn.Width;
|
||||
var width = groupMessages ? defaultWidth : 0;
|
||||
_table.ChangeWidth(expandColumn, width, true);
|
||||
}
|
||||
|
||||
private void CollectMessages(IEnumerable<Frame> frames)
|
||||
{
|
||||
_messages.Clear();
|
||||
foreach (var frame in frames)
|
||||
{
|
||||
_messages.AddRange(frame.Messages);
|
||||
}
|
||||
}
|
||||
|
||||
private void GroupMessages(bool asGroups)
|
||||
{
|
||||
_grouped.Clear();
|
||||
|
||||
foreach (var message in _messages)
|
||||
{
|
||||
string name;
|
||||
if (asGroups)
|
||||
name = message.Name;
|
||||
else
|
||||
name = "all_messages";
|
||||
|
||||
if (!_grouped.TryGetValue(name, out var group))
|
||||
{
|
||||
group = new Group(name, _table, _columns);
|
||||
_grouped[name] = group;
|
||||
}
|
||||
|
||||
group.AddMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroups(bool withHeader)
|
||||
{
|
||||
foreach (var group in _grouped.Values)
|
||||
{
|
||||
if (withHeader)
|
||||
{
|
||||
DrawGroupHeader(group);
|
||||
}
|
||||
else
|
||||
{
|
||||
group.Expand(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawGroupHeader(Group group)
|
||||
{
|
||||
// draw header
|
||||
var head = _table.AddRow();
|
||||
head.SetText(_columns.Expand, group.Expanded ? "-" : "+");
|
||||
head.SetText(_columns.FullName, group.Name);
|
||||
head.SetText(_columns.TotalBytes, group.TotalBytes);
|
||||
head.SetText(_columns.Count, group.TotalCount);
|
||||
group.Head = head;
|
||||
|
||||
var expand = head.GetLabel(_columns.Expand);
|
||||
expand.AddManipulator(new Clickable((evt) =>
|
||||
{
|
||||
group.ToggleExpand();
|
||||
OnGroupExpanded?.Invoke(group, group.Expanded);
|
||||
}));
|
||||
|
||||
// will lazy create message if expanded
|
||||
group.Expand(group.Expanded);
|
||||
}
|
||||
|
||||
public void Sort(ColumnInfo sortHeader, SortMode sortMode)
|
||||
{
|
||||
var sorter = new GroupSorter(_grouped, sortHeader, sortMode);
|
||||
sorter.Sort();
|
||||
|
||||
if (sortHeader != null)
|
||||
{
|
||||
// also set table names,
|
||||
_table.SetSortHeader(sortHeader, sortMode);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_table.Clear();
|
||||
}
|
||||
|
||||
public VisualElement AddEmptyRow()
|
||||
{
|
||||
var row = _table.AddEmptyRow();
|
||||
return row.VisualElement;
|
||||
}
|
||||
|
||||
public void ExpandMany(List<string> expanded)
|
||||
{
|
||||
foreach (var group in _grouped.Values)
|
||||
{
|
||||
if (expanded.Contains(group.Name))
|
||||
{
|
||||
group.Expand(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/MessageView.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/MessageView.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f417eeb8a30cca4eb1a9301a73dbf10
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
284
Assets/Mirage.Profiler/Editor/Messages/MessageViewController.cs
Executable file
284
Assets/Mirage.Profiler/Editor/Messages/MessageViewController.cs
Executable file
@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
using Unity.Profiling;
|
||||
using Unity.Profiling.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
internal sealed class MessageViewController : ProfilerModuleViewController
|
||||
{
|
||||
private readonly CounterNames _names;
|
||||
private readonly Columns _columns = new Columns();
|
||||
private Label _countLabel;
|
||||
private Label _bytesLabel;
|
||||
private Label _perSecondLabel;
|
||||
private VisualElement _toggleBox;
|
||||
private Toggle _debugToggle;
|
||||
private Toggle _groupMsgToggle;
|
||||
private MessageView _messageView;
|
||||
private readonly SavedData _savedData;
|
||||
|
||||
public MessageViewController(ProfilerWindow profilerWindow, CounterNames names, SavedData savedData)
|
||||
: base(profilerWindow)
|
||||
{
|
||||
_names = names;
|
||||
_savedData = savedData;
|
||||
}
|
||||
|
||||
protected override VisualElement CreateView()
|
||||
{
|
||||
// unity doesn't catch errors here so we have to wrap in try/catch
|
||||
try
|
||||
{
|
||||
return CreateViewInternal();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogException(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
// Unsubscribe from the Profiler window event that we previously subscribed to.
|
||||
ProfilerWindow.SelectedFrameIndexChanged -= FrameIndexChanged;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private VisualElement CreateViewInternal()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
root.style.flexDirection = new StyleEnum<FlexDirection>(FlexDirection.Row);
|
||||
root.style.height = Length.Percent(100);
|
||||
root.style.overflow = Overflow.Hidden;
|
||||
|
||||
var summary = new VisualElement();
|
||||
_countLabel = AddLabelWithPadding(summary);
|
||||
_bytesLabel = AddLabelWithPadding(summary);
|
||||
_perSecondLabel = AddLabelWithPadding(summary);
|
||||
_perSecondLabel.tooltip = Names.PER_SECOND_TOOLTIP;
|
||||
root.Add(summary);
|
||||
summary.style.height = Length.Percent(100);
|
||||
summary.style.width = 180;
|
||||
summary.style.minWidth = 180;
|
||||
summary.style.maxWidth = 180;
|
||||
summary.style.borderRightColor = Color.white * .4f;//dark grey
|
||||
summary.style.borderRightWidth = 3;
|
||||
|
||||
_toggleBox = new VisualElement();
|
||||
_toggleBox.style.position = Position.Absolute;
|
||||
_toggleBox.style.bottom = 5;
|
||||
_toggleBox.style.left = 5;
|
||||
_toggleBox.style.unityTextAlign = TextAnchor.LowerLeft;
|
||||
summary.Add(_toggleBox);
|
||||
|
||||
_groupMsgToggle = new Toggle
|
||||
{
|
||||
text = "Group Messages",
|
||||
tooltip = "Groups Message by type",
|
||||
value = _savedData.GroupMessages,
|
||||
};
|
||||
_groupMsgToggle.RegisterValueChangedCallback(GroupToggled);
|
||||
_toggleBox.Add(_groupMsgToggle);
|
||||
|
||||
// todo allow selection of multiple frames
|
||||
//var frameSlider = new MinMaxSlider();
|
||||
//frameSlider.highLimit = 300;
|
||||
//frameSlider.lowLimit = 1;
|
||||
//frameSlider.value = Vector2.one;
|
||||
//frameSlider.RegisterValueChangedCallback(_ => Debug.Log(frameSlider.value));
|
||||
//_toggleBox.Add(frameSlider);
|
||||
|
||||
_debugToggle = new Toggle
|
||||
{
|
||||
text = "Show Fake Messages",
|
||||
tooltip = "Adds fakes message to table to debug layout of table",
|
||||
value = false
|
||||
};
|
||||
_debugToggle.RegisterValueChangedCallback(_ => ReloadData());
|
||||
_toggleBox.Add(_debugToggle);
|
||||
#if MIRAGE_PROFILER_DEBUG
|
||||
_debugToggle.style.display = DisplayStyle.Flex;
|
||||
#else
|
||||
_debugToggle.style.display = DisplayStyle.None;
|
||||
#endif
|
||||
|
||||
|
||||
var sorter = new TableSorter(this);
|
||||
_messageView = new MessageView(_columns, sorter, root);
|
||||
_messageView.OnGroupExpanded += OnGroupExpanded;
|
||||
|
||||
// Populate the label with the current data for the selected frame.
|
||||
ReloadData();
|
||||
|
||||
// Be notified when the selected frame index in the Profiler Window changes, so we can update the label.
|
||||
ProfilerWindow.SelectedFrameIndexChanged += FrameIndexChanged;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void GroupToggled(ChangeEvent<bool> evt)
|
||||
{
|
||||
_savedData.GroupMessages = evt.newValue;
|
||||
ReloadData();
|
||||
}
|
||||
|
||||
private void OnGroupExpanded(Group group, bool expanded)
|
||||
{
|
||||
if (expanded)
|
||||
_savedData.Expanded.Add(group.Name);
|
||||
else
|
||||
_savedData.Expanded.Remove(group.Name);
|
||||
}
|
||||
|
||||
private void FrameIndexChanged(long selectedFrameIndex) => ReloadData();
|
||||
|
||||
private static Label AddLabelWithPadding(VisualElement parent)
|
||||
{
|
||||
var label = new Label() { style = { paddingTop = 8, paddingLeft = 8 } };
|
||||
parent.Add(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
internal void Sort(SortHeader header)
|
||||
{
|
||||
_savedData.SetSortHeader(header);
|
||||
SortFromSaveData();
|
||||
}
|
||||
|
||||
private void SortFromSaveData()
|
||||
{
|
||||
var (sortHeader, sortMode) = _savedData.GetSortHeader(_columns);
|
||||
_messageView.Sort(sortHeader, sortMode);
|
||||
}
|
||||
|
||||
|
||||
private void ReloadData()
|
||||
{
|
||||
SetSummary(_countLabel, _names.Count);
|
||||
SetSummary(_bytesLabel, _names.Bytes);
|
||||
SetSummary(_perSecondLabel, _names.PerSecond);
|
||||
|
||||
ReloadMessages();
|
||||
}
|
||||
|
||||
private void SetSummary(Label label, string counterName)
|
||||
{
|
||||
var frame = (int)ProfilerWindow.selectedFrameIndex;
|
||||
var category = ProfilerCategory.Network.Name;
|
||||
var value = ProfilerDriver.GetFormattedCounterValue(frame, category, counterName);
|
||||
|
||||
// replace prefix
|
||||
var display = counterName.Replace("Received", "").Replace("Sent", "").Trim();
|
||||
label.text = $"{display}: {value}";
|
||||
}
|
||||
|
||||
private void ReloadMessages()
|
||||
{
|
||||
const int EditorID = -1;
|
||||
|
||||
_messageView.Clear();
|
||||
|
||||
var frameIndex = (int)ProfilerWindow.selectedFrameIndex;
|
||||
// Debug.Log($"ReloadMessages [selected {(int)ProfilerWindow.selectedFrameIndex}]");
|
||||
|
||||
|
||||
if (ProfilerDriver.connectedProfiler != EditorID)
|
||||
{
|
||||
AddErrorLabel("Can't read message from player");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetMessages(frameIndex, out var messages))
|
||||
{
|
||||
AddErrorLabel("Can not load messages! (Message list only visible in play mode)\nIMPORTANT: make sure NetworkProfilerBehaviour is setup in starting scene");
|
||||
return;
|
||||
}
|
||||
|
||||
if (messages.Count == 0)
|
||||
{
|
||||
AddInfoLabel("No Messages");
|
||||
return;
|
||||
}
|
||||
|
||||
var frame = new Frame[1] {
|
||||
new Frame{ Messages = messages },
|
||||
};
|
||||
_messageView.Draw(frame, _groupMsgToggle.value);
|
||||
_messageView.ExpandMany(_savedData.Expanded);
|
||||
SortFromSaveData();
|
||||
}
|
||||
|
||||
private bool TryGetMessages(int frameIndex, out List<MessageInfo> messages)
|
||||
{
|
||||
#if MIRAGE_PROFILER_DEBUG
|
||||
if (_debugToggle.value)
|
||||
{
|
||||
messages = GenerateDebugMessages();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (frameIndex == -1)
|
||||
{
|
||||
messages = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
messages = _savedData.Frames.GetFrame(frameIndex).Messages;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MIRAGE_PROFILER_DEBUG
|
||||
private static List<MessageInfo> GenerateDebugMessages()
|
||||
{
|
||||
var messages = new List<MessageInfo>();
|
||||
var order = 0;
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
messages.Add(NewInfo(order++, new RpcMessage { netId = (uint)i }, 20 + i, 5));
|
||||
messages.Add(NewInfo(order++, new SpawnMessage { netId = (uint)i }, 80 + i, 1));
|
||||
messages.Add(NewInfo(order++, new SpawnMessage { netId = (uint)i }, 60 + i, 4));
|
||||
messages.Add(NewInfo(order++, new NetworkPingMessage { }, 4, 1));
|
||||
|
||||
static MessageInfo NewInfo(int order, object msg, int bytes, int count)
|
||||
{
|
||||
#if MIRAGE_DIAGNOSTIC_INSTANCE
|
||||
return new MessageInfo(new NetworkDiagnostics.MessageInfo(null, msg, bytes, count), provider, order);
|
||||
#else
|
||||
return new MessageInfo(new NetworkDiagnostics.MessageInfo(msg, bytes, count), new NetworkInfoProvider(null), order);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
private void AddErrorLabel(string message)
|
||||
{
|
||||
var parent = _messageView.AddEmptyRow();
|
||||
var ele = AddLabelWithPadding(parent);
|
||||
ele.style.color = Color.red;
|
||||
ele.text = message;
|
||||
}
|
||||
|
||||
private void AddInfoLabel(string message)
|
||||
{
|
||||
var parent = _messageView.AddEmptyRow();
|
||||
var ele = AddLabelWithPadding(parent);
|
||||
ele.text = message;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/MessageViewController.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/MessageViewController.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b210a69331a49414ca5f6436163f61d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
27
Assets/Mirage.Profiler/Editor/Messages/TableSorter.cs
Executable file
27
Assets/Mirage.Profiler/Editor/Messages/TableSorter.cs
Executable file
@ -0,0 +1,27 @@
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.Messages
|
||||
{
|
||||
|
||||
internal class TableSorter : ITableSorter
|
||||
{
|
||||
private readonly MessageViewController _controller;
|
||||
|
||||
public TableSorter(MessageViewController controller)
|
||||
{
|
||||
_controller = controller;
|
||||
}
|
||||
|
||||
public void Sort(Table table, SortHeader header)
|
||||
{
|
||||
if (table.ContainsEmptyRows)
|
||||
{
|
||||
Debug.LogWarning("Can't sort when there are empty rows");
|
||||
return;
|
||||
}
|
||||
|
||||
_controller.Sort(header);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Messages/TableSorter.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Messages/TableSorter.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 658b759dbe63e9c44a14d785a15c862e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
20
Assets/Mirage.Profiler/Editor/Mirage.Profiler.Editor.asmdef
Executable file
20
Assets/Mirage.Profiler/Editor/Mirage.Profiler.Editor.asmdef
Executable file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "Mirage.Profiler.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:b46779583a009f04ba9f5f31d0e7e6ac",
|
||||
"GUID:6ff9f1c8e2ed4034baf80c43db7a3b6a",
|
||||
"GUID:30817c1a0e6d646d99c048fc403f5979"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
7
Assets/Mirage.Profiler/Editor/Mirage.Profiler.Editor.asmdef.meta
Executable file
7
Assets/Mirage.Profiler/Editor/Mirage.Profiler.Editor.asmdef.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff97fe2e55c0fd44c91fb1242f9bff3e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
12
Assets/Mirage.Profiler/Editor/ModuleNames.cs
Executable file
12
Assets/Mirage.Profiler/Editor/ModuleNames.cs
Executable file
@ -0,0 +1,12 @@
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
internal static class ModuleNames
|
||||
{
|
||||
// change this based on netcode lib
|
||||
private const string PREFIX = "Mirror";
|
||||
|
||||
public const string SERVER = PREFIX + " Server";
|
||||
public const string SENT = PREFIX + " Sent";
|
||||
public const string RECEIVED = PREFIX + " Received";
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/ModuleNames.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/ModuleNames.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a65943c66af2cb4cb0433f5f3bd73b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
35
Assets/Mirage.Profiler/Editor/ReceivedModule.cs
Executable file
35
Assets/Mirage.Profiler/Editor/ReceivedModule.cs
Executable file
@ -0,0 +1,35 @@
|
||||
using Mirage.NetworkProfiler.ModuleGUI.Messages;
|
||||
using Unity.Profiling.Editor;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
[System.Serializable]
|
||||
[ProfilerModuleMetadata(ModuleNames.RECEIVED)]
|
||||
public class ReceivedModule : ProfilerModule, ICountRecorderProvider
|
||||
{
|
||||
private static readonly ProfilerCounterDescriptor[] counters = new ProfilerCounterDescriptor[]
|
||||
{
|
||||
new ProfilerCounterDescriptor(Names.RECEIVED_COUNT, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.RECEIVED_BYTES, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.RECEIVED_PER_SECOND, Counters.Category),
|
||||
};
|
||||
|
||||
public ReceivedModule() : base(counters) { }
|
||||
|
||||
public override ProfilerModuleViewController CreateDetailsViewController()
|
||||
{
|
||||
var names = new CounterNames(
|
||||
Names.RECEIVED_COUNT,
|
||||
Names.RECEIVED_BYTES,
|
||||
Names.RECEIVED_PER_SECOND
|
||||
);
|
||||
|
||||
return new MessageViewController(ProfilerWindow, names, SaveDataLoader.ReceiveData);
|
||||
}
|
||||
|
||||
CountRecorder ICountRecorderProvider.GetCountRecorder()
|
||||
{
|
||||
return NetworkProfilerRecorder._receivedCounter;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/ReceivedModule.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/ReceivedModule.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 677abdcefedd3144b9784f7a09a647da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
231
Assets/Mirage.Profiler/Editor/SavedData.cs
Executable file
231
Assets/Mirage.Profiler/Editor/SavedData.cs
Executable file
@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.Messages;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.UITable;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
[Serializable]
|
||||
internal class SavedData
|
||||
{
|
||||
/// <summary>
|
||||
/// Message from each frame so they can survive domain reload
|
||||
/// </summary>
|
||||
public Frames Frames;
|
||||
|
||||
/// <summary>
|
||||
/// Active sort header
|
||||
/// </summary>
|
||||
public string SortHeader;
|
||||
|
||||
public SortMode SortMode;
|
||||
|
||||
/// <summary>
|
||||
/// Which Message groups are expanded
|
||||
/// </summary>
|
||||
public List<string> Expanded;
|
||||
|
||||
public bool GroupMessages = true;
|
||||
|
||||
public SavedData()
|
||||
{
|
||||
Frames = new Frames();
|
||||
|
||||
Expanded = new List<string>();
|
||||
}
|
||||
|
||||
public (ColumnInfo, SortMode) GetSortHeader(Columns columns)
|
||||
{
|
||||
foreach (var c in columns)
|
||||
{
|
||||
if (SortHeader == c.Header)
|
||||
{
|
||||
return (c, SortMode);
|
||||
}
|
||||
}
|
||||
|
||||
return (null, SortMode.None);
|
||||
}
|
||||
|
||||
public void SetSortHeader(SortHeader header)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
SortHeader = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
SortHeader = header.Info.Header;
|
||||
SortMode = header.SortMode;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var frame in Frames)
|
||||
{
|
||||
frame.Bytes = 0;
|
||||
frame.Messages.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SaveDataLoader
|
||||
{
|
||||
private SavedData _receiveData;
|
||||
private SavedData _sentData;
|
||||
private static SaveDataLoader instance;
|
||||
private static bool isQuitting;
|
||||
|
||||
// private so only we can create one
|
||||
private SaveDataLoader()
|
||||
{
|
||||
NetworkProfilerRecorder.AfterSample += AfterSample;
|
||||
EditorApplication.quitting += Quitting;
|
||||
}
|
||||
|
||||
private void Quitting()
|
||||
{
|
||||
Console.WriteLine("[Mirage.Profiler] quitting");
|
||||
// save and clear references when quitting,
|
||||
// this is needed because finialize is called after unity dll unloads so causes crash
|
||||
SaveBoth();
|
||||
_receiveData = null;
|
||||
_sentData = null;
|
||||
isQuitting = true;
|
||||
}
|
||||
|
||||
~SaveDataLoader()
|
||||
{
|
||||
NetworkProfilerRecorder.AfterSample -= AfterSample;
|
||||
|
||||
// dont save after quitting, unity might unload their dll and cause crash
|
||||
if (isQuitting)
|
||||
return;
|
||||
|
||||
SaveBoth();
|
||||
}
|
||||
|
||||
private void SaveBoth()
|
||||
{
|
||||
if (_receiveData != null)
|
||||
Save(GetFullPath("Receive"), _receiveData);
|
||||
|
||||
if (_sentData != null)
|
||||
Save(GetFullPath("Sent"), _sentData);
|
||||
}
|
||||
|
||||
private static void AfterSample(int tick)
|
||||
{
|
||||
SetFrame(tick, ReceiveData, NetworkProfilerRecorder._receivedCounter);
|
||||
SetFrame(tick, SentData, NetworkProfilerRecorder._sentCounter);
|
||||
}
|
||||
|
||||
private static void SetFrame(int tick, SavedData data, CountRecorder counter)
|
||||
{
|
||||
var saveFrame = data.Frames.GetFrame(tick);
|
||||
|
||||
// clear old data
|
||||
saveFrame.Bytes = 0;
|
||||
saveFrame.Messages.Clear();
|
||||
|
||||
if (counter == null)
|
||||
return;
|
||||
|
||||
var counterFrame = counter._frames.GetFrame(tick);
|
||||
|
||||
saveFrame.Bytes = counterFrame.Bytes;
|
||||
saveFrame.Messages.AddRange(counterFrame.Messages);
|
||||
}
|
||||
|
||||
public static SavedData ReceiveData
|
||||
{
|
||||
get
|
||||
{
|
||||
// dont load on quit, it might cause crash if unity dll unloads while savedata is save/loading
|
||||
if (isQuitting)
|
||||
return null;
|
||||
|
||||
if (instance == null)
|
||||
instance = new SaveDataLoader();
|
||||
|
||||
if (instance._receiveData == null)
|
||||
{
|
||||
instance._receiveData = Load(GetFullPath("Receive"));
|
||||
}
|
||||
return instance._receiveData;
|
||||
}
|
||||
}
|
||||
|
||||
public static SavedData SentData
|
||||
{
|
||||
get
|
||||
{
|
||||
// dont load on quit, it might cause crash if unity dll unloads while savedata is save/loading
|
||||
if (isQuitting)
|
||||
return null;
|
||||
|
||||
if (instance == null)
|
||||
instance = new SaveDataLoader();
|
||||
|
||||
if (instance._sentData == null)
|
||||
{
|
||||
instance._sentData = Load(GetFullPath("Sent"));
|
||||
}
|
||||
return instance._sentData;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFullPath(string name)
|
||||
{
|
||||
var userSettingsFolder = Path.GetFullPath("UserSettings");
|
||||
if (string.IsNullOrEmpty(name))
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
|
||||
return Path.Join(userSettingsFolder, "Mirage.Profiler", $"{name}.json");
|
||||
}
|
||||
|
||||
public static void Save(string path, SavedData data)
|
||||
{
|
||||
Console.WriteLine($"[Mirage.Profiler] Save {path}");
|
||||
CheckDir(path);
|
||||
|
||||
var text = JsonUtility.ToJson(data);
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
public static SavedData Load(string path)
|
||||
{
|
||||
Console.WriteLine($"[Mirage.Profiler] Load {path}");
|
||||
CheckDir(path);
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var text = File.ReadAllText(path);
|
||||
var data = JsonUtility.FromJson<SavedData>(text);
|
||||
Validate(data);
|
||||
return data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SavedData();
|
||||
}
|
||||
}
|
||||
|
||||
private static void Validate(SavedData data)
|
||||
{
|
||||
data.Frames.ValidateSize();
|
||||
}
|
||||
|
||||
private static void CheckDir(string path)
|
||||
{
|
||||
// check dir exists
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/SavedData.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/SavedData.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e3608e18fbb66346bc8d15f628ad4f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
35
Assets/Mirage.Profiler/Editor/SentModule.cs
Executable file
35
Assets/Mirage.Profiler/Editor/SentModule.cs
Executable file
@ -0,0 +1,35 @@
|
||||
using Mirage.NetworkProfiler.ModuleGUI.Messages;
|
||||
using Unity.Profiling.Editor;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
[System.Serializable]
|
||||
[ProfilerModuleMetadata(ModuleNames.SENT)]
|
||||
public class SentModule : ProfilerModule, ICountRecorderProvider
|
||||
{
|
||||
private static readonly ProfilerCounterDescriptor[] counters = new ProfilerCounterDescriptor[]
|
||||
{
|
||||
new ProfilerCounterDescriptor(Names.SENT_COUNT, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.SENT_BYTES, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.SENT_PER_SECOND, Counters.Category),
|
||||
};
|
||||
|
||||
public SentModule() : base(counters) { }
|
||||
|
||||
public override ProfilerModuleViewController CreateDetailsViewController()
|
||||
{
|
||||
var names = new CounterNames(
|
||||
Names.SENT_COUNT,
|
||||
Names.SENT_BYTES,
|
||||
Names.SENT_PER_SECOND
|
||||
);
|
||||
|
||||
return new MessageViewController(ProfilerWindow, names, SaveDataLoader.SentData);
|
||||
}
|
||||
|
||||
CountRecorder ICountRecorderProvider.GetCountRecorder()
|
||||
{
|
||||
return NetworkProfilerRecorder._sentCounter;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/SentModule.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/SentModule.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5447799e8b54b3044b4fac717c450b46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
103
Assets/Mirage.Profiler/Editor/ServerModule.cs
Executable file
103
Assets/Mirage.Profiler/Editor/ServerModule.cs
Executable file
@ -0,0 +1,103 @@
|
||||
using Unity.Profiling;
|
||||
using Unity.Profiling.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI
|
||||
{
|
||||
[System.Serializable]
|
||||
[ProfilerModuleMetadata(ModuleNames.SERVER)]
|
||||
public class ServerModule : ProfilerModule
|
||||
{
|
||||
private static readonly ProfilerCounterDescriptor[] counters = new ProfilerCounterDescriptor[]
|
||||
{
|
||||
new ProfilerCounterDescriptor(Names.PLAYER_COUNT, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.CHARACTER_COUNT, Counters.Category),
|
||||
new ProfilerCounterDescriptor(Names.OBJECT_COUNT, Counters.Category),
|
||||
};
|
||||
|
||||
public ServerModule() : base(counters) { }
|
||||
|
||||
public override ProfilerModuleViewController CreateDetailsViewController()
|
||||
{
|
||||
return new ServerViewController(ProfilerWindow);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class BaseViewController : ProfilerModuleViewController
|
||||
{
|
||||
public BaseViewController(ProfilerWindow profilerWindow) : base(profilerWindow) { }
|
||||
|
||||
protected static Label AddLabelWithPadding(VisualElement parent)
|
||||
{
|
||||
var label = new Label() { style = { paddingTop = 8, paddingLeft = 8 } };
|
||||
parent.Add(label);
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
protected void SetText(Label label, string name)
|
||||
{
|
||||
var frame = (int)ProfilerWindow.selectedFrameIndex;
|
||||
var category = ProfilerCategory.Network.Name;
|
||||
var value = ProfilerDriver.GetFormattedCounterValue(frame, category, name);
|
||||
|
||||
label.text = $"{name}: {value}";
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ServerViewController : BaseViewController
|
||||
{
|
||||
private Label PlayerLabel;
|
||||
private Label CharacterLabel;
|
||||
private Label ObjectLabel;
|
||||
|
||||
public ServerViewController(ProfilerWindow profilerWindow) : base(profilerWindow) { }
|
||||
|
||||
protected override VisualElement CreateView()
|
||||
{
|
||||
var root = new VisualElement();
|
||||
|
||||
PlayerLabel = AddLabelWithPadding(root);
|
||||
CharacterLabel = AddLabelWithPadding(root);
|
||||
ObjectLabel = AddLabelWithPadding(root);
|
||||
|
||||
PlayerLabel.tooltip = Names.PLAYER_COUNT_TOOLTIP;
|
||||
CharacterLabel.tooltip = Names.CHARACTER_COUNT_TOOLTIP;
|
||||
ObjectLabel.tooltip = Names.OBJECT_COUNT_TOOLTIP;
|
||||
|
||||
// Populate the label with the current data for the selected frame.
|
||||
ReloadData();
|
||||
|
||||
// Be notified when the selected frame index in the Profiler Window changes, so we can update the label.
|
||||
ProfilerWindow.SelectedFrameIndexChanged += OnSelectedFrameIndexChanged;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void OnSelectedFrameIndexChanged(long selectedFrameIndex)
|
||||
{
|
||||
// Update the label with the current data for the newly selected frame.
|
||||
ReloadData();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
// Unsubscribe from the Profiler window event that we previously subscribed to.
|
||||
ProfilerWindow.SelectedFrameIndexChanged -= OnSelectedFrameIndexChanged;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void ReloadData()
|
||||
{
|
||||
SetText(PlayerLabel, Names.PLAYER_COUNT);
|
||||
SetText(CharacterLabel, Names.CHARACTER_COUNT);
|
||||
SetText(ObjectLabel, Names.OBJECT_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/ServerModule.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/ServerModule.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b8113ff8f56cc644bb23d2da87f23eab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
8
Assets/Mirage.Profiler/Editor/Table.meta
Executable file
8
Assets/Mirage.Profiler/Editor/Table.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc264da8a8fc533418223d921f2f00c8
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
66
Assets/Mirage.Profiler/Editor/Table/ColumnInfo.cs
Executable file
66
Assets/Mirage.Profiler/Editor/Table/ColumnInfo.cs
Executable file
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using Mirage.NetworkProfiler.ModuleGUI.Messages;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class ColumnInfo
|
||||
{
|
||||
public string Header { get; private set; }
|
||||
public int Width { get; private set; }
|
||||
|
||||
public bool AllowSort { get; private set; }
|
||||
public Func<Group, Group, int> SortGroup { get; private set; }
|
||||
public Func<MessageInfo, MessageInfo, int> SortMessages { get; private set; }
|
||||
|
||||
public Func<MessageInfo, string> TextGetter { get; private set; }
|
||||
|
||||
public bool HasToolTip { get; private set; }
|
||||
public Func<MessageInfo, string> ToolTipGetter { get; private set; }
|
||||
|
||||
public ColumnInfo(string header, int width, Func<MessageInfo, string> textGetter)
|
||||
{
|
||||
Header = header;
|
||||
Width = width;
|
||||
TextGetter = textGetter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables sorting for column. If sort functions are null they will use default sort from <see cref="GroupSorter"/>
|
||||
/// </summary>
|
||||
/// <param name="sortGroup"></param>
|
||||
/// <param name="sortMessages"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public void AddSort(Func<Group, Group, int> sortGroup, Func<MessageInfo, MessageInfo, int> sortMessages)
|
||||
{
|
||||
AllowSort = true;
|
||||
SortGroup = sortGroup ?? GroupSorter.DefaultGroupSort;
|
||||
SortMessages = sortMessages ?? GroupSorter.DefaultMessageSort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables sorting for column. If sort functions are null they will use default sort from <see cref="GroupSorter"/>
|
||||
/// <para>Uses member getting to sort via that member</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="groupGetter"></param>
|
||||
/// <param name="messageGetter"></param>
|
||||
public void AddSort<T>(Func<Group, T> groupGetter, Func<MessageInfo, T> messageGetter) where T : IComparable<T>
|
||||
{
|
||||
Func<Group, Group, int> sortGroup = groupGetter != null
|
||||
? (x, y) => GroupSorter.Compare(x, y, groupGetter)
|
||||
: null;
|
||||
|
||||
Func<MessageInfo, MessageInfo, int> sortMessages = messageGetter != null
|
||||
? (x, y) => GroupSorter.Compare(x, y, messageGetter)
|
||||
: null;
|
||||
|
||||
AddSort(sortGroup, sortMessages);
|
||||
}
|
||||
|
||||
public void AddToolTip(Func<MessageInfo, string> getter)
|
||||
{
|
||||
HasToolTip = true;
|
||||
ToolTipGetter = getter;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/ColumnInfo.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/ColumnInfo.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b017b2728f784842bc8f62460ab9fbe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
22
Assets/Mirage.Profiler/Editor/Table/EmptyRow.cs
Executable file
22
Assets/Mirage.Profiler/Editor/Table/EmptyRow.cs
Executable file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class EmptyRow : Row
|
||||
{
|
||||
public EmptyRow(Table table, Row previous = null) : base(table, previous) { }
|
||||
|
||||
public override Label GetLabel(ColumnInfo column)
|
||||
{
|
||||
throw new NotSupportedException("Empty row does not have any columns");
|
||||
}
|
||||
|
||||
public override IEnumerable<VisualElement> GetChildren()
|
||||
{
|
||||
return Enumerable.Empty<VisualElement>();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/EmptyRow.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/EmptyRow.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8129d33c5f799524fa10e84110131e69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
7
Assets/Mirage.Profiler/Editor/Table/ITableSorter.cs
Executable file
7
Assets/Mirage.Profiler/Editor/Table/ITableSorter.cs
Executable file
@ -0,0 +1,7 @@
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal interface ITableSorter
|
||||
{
|
||||
void Sort(Table table, SortHeader header);
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/ITableSorter.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/ITableSorter.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05c177a4f382419409a2f55de20e046c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
53
Assets/Mirage.Profiler/Editor/Table/LabelRow.cs
Executable file
53
Assets/Mirage.Profiler/Editor/Table/LabelRow.cs
Executable file
@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class LabelRow : Row
|
||||
{
|
||||
private readonly Dictionary<ColumnInfo, Label> _elements = new Dictionary<ColumnInfo, Label>();
|
||||
|
||||
public LabelRow(Table table, Row previous = null) : base(table, previous)
|
||||
{
|
||||
foreach (var header in table.HeaderInfo)
|
||||
{
|
||||
var label = CreateLabel(header);
|
||||
VisualElement.Add(label);
|
||||
_elements[header] = label;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Label CreateLabel(ColumnInfo column)
|
||||
{
|
||||
var label = new Label();
|
||||
SetLabelStyle(column, label);
|
||||
return label;
|
||||
}
|
||||
|
||||
protected static void SetLabelStyle(ColumnInfo column, Label label)
|
||||
{
|
||||
var style = label.style;
|
||||
style.width = column.Width;
|
||||
|
||||
style.paddingLeft = 5;
|
||||
style.paddingRight = 5;
|
||||
style.paddingTop = 5;
|
||||
style.paddingBottom = 5;
|
||||
style.borderRightColor = Color.white * .4f;
|
||||
style.borderBottomColor = Color.white * .4f;
|
||||
style.borderBottomWidth = 1;
|
||||
style.borderRightWidth = 2;
|
||||
}
|
||||
|
||||
public override Label GetLabel(ColumnInfo column)
|
||||
{
|
||||
return _elements[column];
|
||||
}
|
||||
|
||||
public override IEnumerable<VisualElement> GetChildren()
|
||||
{
|
||||
return _elements.Values;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/LabelRow.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/LabelRow.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e463bd968d1bb0e4cbae5efd09ed24fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
45
Assets/Mirage.Profiler/Editor/Table/Row.cs
Executable file
45
Assets/Mirage.Profiler/Editor/Table/Row.cs
Executable file
@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal abstract class Row
|
||||
{
|
||||
public Table Table { get; }
|
||||
public VisualElement VisualElement { get; }
|
||||
|
||||
public Row(Table table, Row previous = null)
|
||||
{
|
||||
Table = table;
|
||||
|
||||
VisualElement = new VisualElement();
|
||||
VisualElement.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
var parent = table.ScrollView;
|
||||
if (previous != null)
|
||||
{
|
||||
var index = parent.IndexOf(previous.VisualElement);
|
||||
// insert after previous
|
||||
parent.Insert(index + 1, VisualElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
// just add at end
|
||||
parent.Add(VisualElement);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract Label GetLabel(ColumnInfo column);
|
||||
public abstract IEnumerable<VisualElement> GetChildren();
|
||||
|
||||
public void SetText(ColumnInfo column, object obj)
|
||||
{
|
||||
SetText(column, obj.ToString());
|
||||
}
|
||||
public void SetText(ColumnInfo column, string text)
|
||||
{
|
||||
var label = GetLabel(column);
|
||||
label.text = text;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/Row.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/Row.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac8d50f233b70db42a649ca6b7d69010
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
63
Assets/Mirage.Profiler/Editor/Table/SortHeader.cs
Executable file
63
Assets/Mirage.Profiler/Editor/Table/SortHeader.cs
Executable file
@ -0,0 +1,63 @@
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class SortHeader : Label
|
||||
{
|
||||
public const string ARROW_UP = "\u2191";
|
||||
public const string ARROW_DOWN = "\u2193";
|
||||
|
||||
public SortMode SortMode;
|
||||
|
||||
private readonly SortHeaderRow _row;
|
||||
|
||||
private string _nameWithoutSort;
|
||||
public string NameWithoutSort
|
||||
{
|
||||
get
|
||||
{
|
||||
// this get should be called before any modifications are made
|
||||
// so lazy property should be safe here
|
||||
if (_nameWithoutSort == null)
|
||||
_nameWithoutSort = text;
|
||||
|
||||
return _nameWithoutSort;
|
||||
}
|
||||
}
|
||||
|
||||
public ColumnInfo Info { get; internal set; }
|
||||
|
||||
public SortHeader(SortHeaderRow row) : base()
|
||||
{
|
||||
_row = row;
|
||||
this.AddManipulator(new Clickable(OnClick));
|
||||
}
|
||||
|
||||
private void OnClick(EventBase evt)
|
||||
{
|
||||
// just flip between Accending and Descending, no going back to none
|
||||
if (SortMode == SortMode.Accending)
|
||||
SortMode = SortMode.Descending;
|
||||
else
|
||||
SortMode = SortMode.Accending;
|
||||
|
||||
_row.ApplySort(this);
|
||||
}
|
||||
|
||||
public void UpdateName()
|
||||
{
|
||||
switch (SortMode)
|
||||
{
|
||||
case SortMode.None:
|
||||
text = NameWithoutSort;
|
||||
break;
|
||||
case SortMode.Accending:
|
||||
text = ARROW_UP + NameWithoutSort;
|
||||
break;
|
||||
case SortMode.Descending:
|
||||
text = ARROW_DOWN + NameWithoutSort;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/SortHeader.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/SortHeader.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab266974aca81aa4abbaefba1e934073
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
54
Assets/Mirage.Profiler/Editor/Table/SortHeaderRow.cs
Executable file
54
Assets/Mirage.Profiler/Editor/Table/SortHeaderRow.cs
Executable file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class SortHeaderRow : LabelRow
|
||||
{
|
||||
private readonly ITableSorter _sorter;
|
||||
private SortHeader _currentSort;
|
||||
|
||||
public SortHeaderRow(Table table, ITableSorter sorter) : base(table, null)
|
||||
{
|
||||
_sorter = sorter ?? throw new ArgumentNullException(nameof(sorter));
|
||||
}
|
||||
|
||||
protected override Label CreateLabel(ColumnInfo column)
|
||||
{
|
||||
var label = column.AllowSort
|
||||
? new SortHeader(this)
|
||||
: new Label();
|
||||
|
||||
SetLabelStyle(column, label);
|
||||
return label;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update names of header based on sort
|
||||
/// </summary>
|
||||
/// <param name="sortHeader"></param>
|
||||
public void SetSortHeader(SortHeader sortHeader)
|
||||
{
|
||||
// not null or current
|
||||
if (_currentSort != null && _currentSort != sortHeader)
|
||||
{
|
||||
_currentSort.SortMode = SortMode.None;
|
||||
_currentSort.UpdateName();
|
||||
}
|
||||
|
||||
_currentSort = sortHeader;
|
||||
_currentSort.UpdateName();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates names and applies sort to table
|
||||
/// </summary>
|
||||
/// <param name="sortHeader"></param>
|
||||
public void ApplySort(SortHeader sortHeader)
|
||||
{
|
||||
SetSortHeader(sortHeader);
|
||||
|
||||
_sorter.Sort(Table, sortHeader);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/SortHeaderRow.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/SortHeaderRow.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b4301d9972553c4ab486e9b8915f784
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
9
Assets/Mirage.Profiler/Editor/Table/SortMode.cs
Executable file
9
Assets/Mirage.Profiler/Editor/Table/SortMode.cs
Executable file
@ -0,0 +1,9 @@
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal enum SortMode
|
||||
{
|
||||
None,
|
||||
Accending,
|
||||
Descending,
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/SortMode.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/SortMode.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0d80f37bc0b1a44cbad7667bba04c79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
113
Assets/Mirage.Profiler/Editor/Table/Table.cs
Executable file
113
Assets/Mirage.Profiler/Editor/Table/Table.cs
Executable file
@ -0,0 +1,113 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace Mirage.NetworkProfiler.ModuleGUI.UITable
|
||||
{
|
||||
internal class Table
|
||||
{
|
||||
public readonly VisualElement VisualElement;
|
||||
public readonly ScrollView ScrollView;
|
||||
|
||||
public readonly SortHeaderRow Header;
|
||||
public readonly List<Row> Rows = new List<Row>();
|
||||
public readonly IReadOnlyList<ColumnInfo> HeaderInfo;
|
||||
|
||||
public bool ContainsEmptyRows { get; private set; }
|
||||
|
||||
public Table(IEnumerable<ColumnInfo> columns, ITableSorter sorter)
|
||||
{
|
||||
// create readonly list from given Enumerable
|
||||
HeaderInfo = new List<ColumnInfo>(columns);
|
||||
|
||||
// create table root and scroll view
|
||||
// root can be Horizontal scroll
|
||||
// Horizontal will also move header, but Vertical keeps header at top
|
||||
VisualElement = new ScrollView(ScrollViewMode.Horizontal);
|
||||
// seperate root for contents, so that Horizontal from scroll isn't applied
|
||||
var root = new VisualElement();
|
||||
VisualElement.Add(root);
|
||||
// using VerticalAndHorizontal fixes header being squashed, not sure why, or if it'll cause future problems
|
||||
ScrollView = new ScrollView(ScrollViewMode.VerticalAndHorizontal);
|
||||
|
||||
// add header to table root
|
||||
// header will initialize labels, but we need to set text
|
||||
Header = new SortHeaderRow(this, sorter);
|
||||
|
||||
// add header and scroll to root
|
||||
root.Add(Header.VisualElement);
|
||||
root.Add(ScrollView);
|
||||
|
||||
// add headers
|
||||
foreach (var c in columns)
|
||||
{
|
||||
var ele = Header.GetLabel(c);
|
||||
ele.text = c.Header;
|
||||
|
||||
if (c.AllowSort)
|
||||
{
|
||||
var sortHeader = (SortHeader)ele;
|
||||
sortHeader.Info = c;
|
||||
}
|
||||
|
||||
// make header element thicker
|
||||
var eleStyle = ele.style;
|
||||
eleStyle.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
eleStyle.borderBottomWidth = 3;
|
||||
eleStyle.borderRightWidth = 3;
|
||||
}
|
||||
}
|
||||
|
||||
public Row AddRow(Row previous = null)
|
||||
{
|
||||
var row = new LabelRow(this, previous);
|
||||
Rows.Add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public Row AddEmptyRow(Row previous = null)
|
||||
{
|
||||
var row = new EmptyRow(this, previous);
|
||||
ContainsEmptyRows = true;
|
||||
Rows.Add(row);
|
||||
return row;
|
||||
}
|
||||
|
||||
public void ChangeWidth(ColumnInfo column, int newWidth, bool setVisibility)
|
||||
{
|
||||
foreach (var row in Rows)
|
||||
{
|
||||
if (row is EmptyRow)
|
||||
continue;
|
||||
|
||||
var label = row.GetLabel(column);
|
||||
var style = label.style;
|
||||
style.width = newWidth;
|
||||
|
||||
if (setVisibility)
|
||||
style.display = newWidth > 0 ? DisplayStyle.Flex : DisplayStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all rows expect header
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
ScrollView.Clear();
|
||||
Rows.Clear();
|
||||
Rows.Add(Header);
|
||||
ContainsEmptyRows = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates names of sort header
|
||||
/// </summary>
|
||||
public void SetSortHeader(ColumnInfo info, SortMode mode)
|
||||
{
|
||||
var sortBy = (SortHeader)Header.GetLabel(info);
|
||||
sortBy.SortMode = mode;
|
||||
Header.SetSortHeader(sortBy);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Editor/Table/Table.cs.meta
Executable file
11
Assets/Mirage.Profiler/Editor/Table/Table.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a7346de93910944c965b68a39d93082
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
21
Assets/Mirage.Profiler/LICENSE
Executable file
21
Assets/Mirage.Profiler/LICENSE
Executable file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 James Frowen
|
||||
|
||||
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.
|
7
Assets/Mirage.Profiler/LICENSE.meta
Executable file
7
Assets/Mirage.Profiler/LICENSE.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4452ce1fed4a2641879ef852c413104
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
1
Assets/Mirage.Profiler/Readme.txt
Executable file
1
Assets/Mirage.Profiler/Readme.txt
Executable file
@ -0,0 +1 @@
|
||||
See full readme here https://github.com/James-Frowen/Mirage.Profiler
|
7
Assets/Mirage.Profiler/Readme.txt.meta
Executable file
7
Assets/Mirage.Profiler/Readme.txt.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc3de9d50be43ed438d71e49f728567c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
8
Assets/Mirage.Profiler/Runtime.meta
Executable file
8
Assets/Mirage.Profiler/Runtime.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82fa6b4931bac2f41b95862e74371e17
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
6
Assets/Mirage.Profiler/Runtime/AssemblyInfo.cs
Executable file
6
Assets/Mirage.Profiler/Runtime/AssemblyInfo.cs
Executable file
@ -0,0 +1,6 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyVersion("1.1.0.11")]
|
||||
|
||||
[assembly: InternalsVisibleTo("Mirage.Profiler.Editor")]
|
11
Assets/Mirage.Profiler/Runtime/AssemblyInfo.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/AssemblyInfo.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0ddded0f98d71546aeae6e4c52aef54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
87
Assets/Mirage.Profiler/Runtime/CountRecorder.cs
Executable file
87
Assets/Mirage.Profiler/Runtime/CountRecorder.cs
Executable file
@ -0,0 +1,87 @@
|
||||
using System.Collections.Generic;
|
||||
using Mirror;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
internal class CountRecorder
|
||||
{
|
||||
private readonly ProfilerCounter<int> _profilerCount;
|
||||
private readonly ProfilerCounter<int> _profilerBytes;
|
||||
private readonly ProfilerCounter<int> _profilerPerSecond;
|
||||
private readonly object _instance;
|
||||
private readonly INetworkInfoProvider _provider;
|
||||
internal readonly Frames _frames;
|
||||
private int _count;
|
||||
private int _bytes;
|
||||
private int _perSecond;
|
||||
private readonly Queue<(float time, int bytes)> _perSecondQueue = new Queue<(float time, int bytes)>();
|
||||
private int _frameIndex = -1;
|
||||
|
||||
public CountRecorder(object instance, INetworkInfoProvider provider, ProfilerCounter<int> profilerCount, ProfilerCounter<int> profilerBytes, ProfilerCounter<int> profilerPerSecond)
|
||||
{
|
||||
_provider = provider;
|
||||
_instance = instance;
|
||||
_profilerCount = profilerCount;
|
||||
_profilerBytes = profilerBytes;
|
||||
_profilerPerSecond = profilerPerSecond;
|
||||
_frames = new Frames();
|
||||
}
|
||||
|
||||
public void OnMessage(NetworkDiagnostics.MessageInfo obj)
|
||||
{
|
||||
// using the profiler-window branch of mirage to allow NetworkDiagnostics to say which server/client is sent the event
|
||||
#if MIRAGE_DIAGNOSTIC_INSTANCE
|
||||
if (obj.instance != _instance)
|
||||
return;
|
||||
#endif
|
||||
|
||||
// dont record anything if frame index is -1
|
||||
// this normally means the profiler has not been activated yet
|
||||
if (_frameIndex == -1)
|
||||
return;
|
||||
|
||||
_count += obj.count;
|
||||
_bytes += obj.bytes * obj.count;
|
||||
|
||||
var frame = _frames.GetFrame(_frameIndex);
|
||||
frame.Messages.Add(new MessageInfo(obj, _provider, frame.Messages.Count));
|
||||
frame.Bytes++;
|
||||
}
|
||||
|
||||
public void EndFrame(int frameIndex)
|
||||
{
|
||||
CaclulatePerSecond(Time.time, _bytes);
|
||||
_profilerCount.Sample(_count);
|
||||
_profilerBytes.Sample(_bytes);
|
||||
_count = 0;
|
||||
_bytes = 0;
|
||||
|
||||
// +1 so that we set next frame
|
||||
// otherwise we clear the frame that the savedata wants to grab
|
||||
_frameIndex = frameIndex + 1;
|
||||
var frame = _frames.GetFrame(_frameIndex);
|
||||
frame.Messages.Clear();
|
||||
frame.Bytes = 0;
|
||||
}
|
||||
|
||||
private void CaclulatePerSecond(float now, int bytes)
|
||||
{
|
||||
// add new values to sum
|
||||
_perSecond += bytes;
|
||||
_perSecondQueue.Enqueue((now, bytes));
|
||||
|
||||
// remove old bytes from sum
|
||||
var removeTime = now - 1;
|
||||
while (_perSecondQueue.Peek().time < removeTime)
|
||||
{
|
||||
var removed = _perSecondQueue.Dequeue();
|
||||
_perSecond -= removed.bytes;
|
||||
}
|
||||
|
||||
// record sample after adding/removing value
|
||||
_profilerPerSecond.Sample(_perSecond);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/CountRecorder.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/CountRecorder.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16575ee3be2c5e046af4438787d215f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
23
Assets/Mirage.Profiler/Runtime/Counters.cs
Executable file
23
Assets/Mirage.Profiler/Runtime/Counters.cs
Executable file
@ -0,0 +1,23 @@
|
||||
using Unity.Profiling;
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
internal static class Counters
|
||||
{
|
||||
public static readonly ProfilerCategory Category = ProfilerCategory.Network;
|
||||
private const ProfilerMarkerDataUnit COUNT = ProfilerMarkerDataUnit.Count;
|
||||
private const ProfilerMarkerDataUnit BYTES = ProfilerMarkerDataUnit.Bytes;
|
||||
|
||||
public static readonly ProfilerCounter<int> PlayerCount = new ProfilerCounter<int>(Category, Names.PLAYER_COUNT, COUNT);
|
||||
public static readonly ProfilerCounter<int> CharCount = new ProfilerCounter<int>(Category, Names.CHARACTER_COUNT, COUNT);
|
||||
public static readonly ProfilerCounter<int> ObjectCount = new ProfilerCounter<int>(Category, Names.OBJECT_COUNT, COUNT);
|
||||
|
||||
public static readonly ProfilerCounter<int> SentCount = new ProfilerCounter<int>(Category, Names.SENT_COUNT, COUNT);
|
||||
public static readonly ProfilerCounter<int> SentBytes = new ProfilerCounter<int>(Category, Names.SENT_BYTES, BYTES);
|
||||
public static readonly ProfilerCounter<int> SentPerSecond = new ProfilerCounter<int>(Category, Names.SENT_PER_SECOND, BYTES);
|
||||
|
||||
public static readonly ProfilerCounter<int> ReceiveCount = new ProfilerCounter<int>(Category, Names.RECEIVED_COUNT, COUNT);
|
||||
public static readonly ProfilerCounter<int> ReceiveBytes = new ProfilerCounter<int>(Category, Names.RECEIVED_BYTES, BYTES);
|
||||
public static readonly ProfilerCounter<int> ReceivePerSecond = new ProfilerCounter<int>(Category, Names.RECEIVED_PER_SECOND, BYTES);
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/Counters.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/Counters.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 40d8d596129601f4d90f0664cb2557c4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
44
Assets/Mirage.Profiler/Runtime/Frame.cs
Executable file
44
Assets/Mirage.Profiler/Runtime/Frame.cs
Executable file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Frames : IEnumerable<Frame>
|
||||
{
|
||||
[SerializeField]
|
||||
private Frame[] _frames;
|
||||
|
||||
public Frames()
|
||||
{
|
||||
_frames = new Frame[NetworkProfilerRecorder.FRAME_COUNT];
|
||||
for (var i = 0; i < _frames.Length; i++)
|
||||
_frames[i] = new Frame();
|
||||
}
|
||||
|
||||
public Frame GetFrame(int frameIndex)
|
||||
{
|
||||
return _frames[frameIndex % _frames.Length];
|
||||
}
|
||||
|
||||
public IEnumerator<Frame> GetEnumerator() => ((IEnumerable<Frame>)_frames).GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<Frame>)_frames).GetEnumerator();
|
||||
|
||||
internal void ValidateSize()
|
||||
{
|
||||
if (_frames.Length != NetworkProfilerRecorder.FRAME_COUNT)
|
||||
{
|
||||
Array.Resize(ref _frames, NetworkProfilerRecorder.FRAME_COUNT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class Frame
|
||||
{
|
||||
public List<MessageInfo> Messages = new List<MessageInfo>();
|
||||
public int Bytes;
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/Frame.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/Frame.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b59754fd175ff2345a6788891dddbfcf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
45
Assets/Mirage.Profiler/Runtime/MessageInfo.cs
Executable file
45
Assets/Mirage.Profiler/Runtime/MessageInfo.cs
Executable file
@ -0,0 +1,45 @@
|
||||
using Mirror;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
[System.Serializable]
|
||||
public class MessageInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Order message was sent/received in frame
|
||||
/// </summary>
|
||||
[SerializeField] private int _order;
|
||||
[SerializeField] private int _bytes;
|
||||
[SerializeField] private int _count;
|
||||
[SerializeField] private string _messageName;
|
||||
// unity can't serialize nullable so store as 2 fields
|
||||
[SerializeField] private bool _hasNetId;
|
||||
[SerializeField] private uint _netId;
|
||||
[SerializeField] private string _objectName;
|
||||
[SerializeField] private string _rpcName;
|
||||
|
||||
public int Order => _order;
|
||||
public string Name => _messageName;
|
||||
public int Bytes => _bytes;
|
||||
public int Count => _count;
|
||||
public int TotalBytes => Bytes * Count;
|
||||
public uint? NetId => _hasNetId ? _netId : default;
|
||||
public string ObjectName => _objectName;
|
||||
public string RpcName => _rpcName;
|
||||
|
||||
public MessageInfo(NetworkDiagnostics.MessageInfo msg, INetworkInfoProvider provider, int order)
|
||||
{
|
||||
_order = order;
|
||||
_bytes = msg.bytes;
|
||||
_count = msg.count;
|
||||
_messageName = msg.message.GetType().FullName;
|
||||
var id = provider.GetNetId(msg);
|
||||
_hasNetId = id.HasValue;
|
||||
_netId = id.GetValueOrDefault();
|
||||
var obj = provider.GetNetworkIdentity(id);
|
||||
_objectName = obj != null ? obj.name : null;
|
||||
_rpcName = provider.GetRpcName(msg);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/MessageInfo.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/MessageInfo.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 668dc7c434784214794807fcfecfe185
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
17
Assets/Mirage.Profiler/Runtime/Mirage.Profiler.asmdef
Executable file
17
Assets/Mirage.Profiler/Runtime/Mirage.Profiler.asmdef
Executable file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Mirage.Profiler",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:b46779583a009f04ba9f5f31d0e7e6ac",
|
||||
"GUID:30817c1a0e6d646d99c048fc403f5979"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
7
Assets/Mirage.Profiler/Runtime/Mirage.Profiler.asmdef.meta
Executable file
7
Assets/Mirage.Profiler/Runtime/Mirage.Profiler.asmdef.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ff9f1c8e2ed4034baf80c43db7a3b6a
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
23
Assets/Mirage.Profiler/Runtime/Names.cs
Executable file
23
Assets/Mirage.Profiler/Runtime/Names.cs
Executable file
@ -0,0 +1,23 @@
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
public static class Names
|
||||
{
|
||||
public const string PLAYER_COUNT = "Player Count";
|
||||
public const string PLAYER_COUNT_TOOLTIP = "Number of players connected to the server";
|
||||
public const string CHARACTER_COUNT = "Character Count";
|
||||
public const string CHARACTER_COUNT_TOOLTIP = "Number of players with spawned GameObjects";
|
||||
|
||||
public const string OBJECT_COUNT = "Object Count";
|
||||
public const string OBJECT_COUNT_TOOLTIP = "Number of NetworkIdentities spawned on the server";
|
||||
|
||||
public const string SENT_COUNT = "Sent Messages";
|
||||
public const string SENT_BYTES = "Sent Bytes";
|
||||
public const string SENT_PER_SECOND = "Sent Per Second";
|
||||
|
||||
public const string RECEIVED_COUNT = "Received Messages";
|
||||
public const string RECEIVED_BYTES = "Received Bytes";
|
||||
public const string RECEIVED_PER_SECOND = "Received Per Second";
|
||||
|
||||
public const string PER_SECOND_TOOLTIP = "Sum of Bytes over the previous second";
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/Names.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/Names.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05fd6c7133d9b2e42a8f282465a445d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
105
Assets/Mirage.Profiler/Runtime/NetworkInfoProvider.cs
Normal file
105
Assets/Mirage.Profiler/Runtime/NetworkInfoProvider.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Mirror;
|
||||
using Mirror.RemoteCalls;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns information about NetworkMessage
|
||||
/// </summary>
|
||||
public interface INetworkInfoProvider
|
||||
{
|
||||
uint? GetNetId(NetworkDiagnostics.MessageInfo info);
|
||||
NetworkIdentity GetNetworkIdentity(uint? netId);
|
||||
string GetRpcName(NetworkDiagnostics.MessageInfo info);
|
||||
}
|
||||
|
||||
public class NetworkInfoProvider : INetworkInfoProvider
|
||||
{
|
||||
public uint? GetNetId(NetworkDiagnostics.MessageInfo info)
|
||||
{
|
||||
switch (info.message)
|
||||
{
|
||||
case CommandMessage msg: return msg.netId;
|
||||
case RpcMessage msg: return msg.netId;
|
||||
case SpawnMessage msg: return msg.netId;
|
||||
case ChangeOwnerMessage msg: return msg.netId;
|
||||
case ObjectDestroyMessage msg: return msg.netId;
|
||||
case ObjectHideMessage msg: return msg.netId;
|
||||
case EntityStateMessage msg: return msg.netId;
|
||||
default: return default;
|
||||
}
|
||||
}
|
||||
|
||||
public NetworkIdentity GetNetworkIdentity(uint? netId)
|
||||
{
|
||||
if (!netId.HasValue)
|
||||
return null;
|
||||
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
NetworkServer.spawned.TryGetValue(netId.Value, out var identity);
|
||||
return identity;
|
||||
}
|
||||
|
||||
if (NetworkClient.active)
|
||||
{
|
||||
NetworkClient.spawned.TryGetValue(netId.Value, out var identity);
|
||||
return identity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetRpcName(NetworkDiagnostics.MessageInfo info)
|
||||
{
|
||||
switch (info.message)
|
||||
{
|
||||
case CommandMessage msg:
|
||||
return GetRpcName(msg.netId, msg.componentIndex, msg.functionHash);
|
||||
case RpcMessage msg:
|
||||
return GetRpcName(msg.netId, msg.componentIndex, msg.functionHash);
|
||||
default: return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#if MIRROR_2022_9_OR_NEWER
|
||||
private string GetRpcName(uint netId, int componentIndex, int functionIndex)
|
||||
#else
|
||||
private string GetRpcName(uint netId, int componentIndex, ushort functionIndex)
|
||||
#endif
|
||||
{
|
||||
var hash = functionIndex;
|
||||
var remoteCallDelegate = RemoteProcedureCalls.GetDelegate(hash);
|
||||
if (remoteCallDelegate != null)
|
||||
{
|
||||
string fixedMethodName = MirrorMethodNameTrimmer.FixMethodName(remoteCallDelegate.Method.Name);
|
||||
string methodFullName = $"{remoteCallDelegate.Method.DeclaringType?.FullName}.{fixedMethodName}";
|
||||
return methodFullName;
|
||||
}
|
||||
|
||||
// todo some error maybe
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Some stuff from Mirror.Weaver
|
||||
/// </summary>
|
||||
private static class MirrorMethodNameTrimmer
|
||||
{
|
||||
private static readonly Regex regex1 = new Regex($"^InvokeUserCode_", RegexOptions.Compiled);
|
||||
private static readonly Regex regex2 = new Regex("__[A-Z][\\w`]*$", RegexOptions.Compiled);
|
||||
|
||||
public static string FixMethodName(string methodName)
|
||||
{
|
||||
methodName = regex1.Replace(methodName, "");
|
||||
methodName = regex2.Replace(methodName, "");
|
||||
|
||||
return methodName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/NetworkInfoProvider.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/NetworkInfoProvider.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8192a6dbec7e3b446bea79c4b0f172be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
112
Assets/Mirage.Profiler/Runtime/NetworkProfilerRecorder.cs
Executable file
112
Assets/Mirage.Profiler/Runtime/NetworkProfilerRecorder.cs
Executable file
@ -0,0 +1,112 @@
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditorInternal;
|
||||
#endif
|
||||
|
||||
namespace Mirage.NetworkProfiler
|
||||
{
|
||||
[DefaultExecutionOrder(int.MaxValue)] // last
|
||||
public class NetworkProfilerRecorder : MonoBehaviour
|
||||
{
|
||||
// singleton because unity only has 1 profiler
|
||||
public static NetworkProfilerRecorder Instance { get; private set; }
|
||||
|
||||
internal static CountRecorder _sentCounter;
|
||||
internal static CountRecorder _receivedCounter;
|
||||
internal const int FRAME_COUNT = 300; // todo find a way to get real frame count
|
||||
|
||||
public delegate void FrameUpdate(int tick);
|
||||
public static event FrameUpdate AfterSample;
|
||||
|
||||
private int _lastProcessedFrame = -1;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (Instance == null)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
_lastProcessedFrame = ProfilerDriver.lastFrameIndex;
|
||||
#endif
|
||||
|
||||
var provider = new NetworkInfoProvider();
|
||||
_sentCounter = new CountRecorder(null, provider, Counters.SentCount, Counters.SentBytes, Counters.SentPerSecond);
|
||||
_receivedCounter = new CountRecorder(null, provider, Counters.ReceiveCount, Counters.ReceiveBytes, Counters.ReceivePerSecond);
|
||||
NetworkDiagnostics.InMessageEvent += _receivedCounter.OnMessage;
|
||||
NetworkDiagnostics.OutMessageEvent += _sentCounter.OnMessage;
|
||||
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
if (_receivedCounter != null)
|
||||
NetworkDiagnostics.InMessageEvent -= _receivedCounter.OnMessage;
|
||||
if (_sentCounter != null)
|
||||
NetworkDiagnostics.OutMessageEvent -= _sentCounter.OnMessage;
|
||||
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!NetworkServer.active && !NetworkClient.active)
|
||||
return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!ProfilerDriver.enabled)
|
||||
return;
|
||||
|
||||
// once a frame, ever frame, no matter what lastFrameIndex is
|
||||
SampleCounts();
|
||||
|
||||
// unity sometimes skips a profiler frame, because unity
|
||||
// so we have to check if that happens and then sample the missing frame
|
||||
while (_lastProcessedFrame < ProfilerDriver.lastFrameIndex)
|
||||
{
|
||||
_lastProcessedFrame++;
|
||||
|
||||
//Debug.Log($"Sample: [LateUpdate, enabled { ProfilerDriver.enabled}, first {ProfilerDriver.firstFrameIndex}, last {ProfilerDriver.lastFrameIndex}, lastProcessed {lastProcessedFrame}]");
|
||||
|
||||
var lastFrame = _lastProcessedFrame;
|
||||
// not sure why frame is offset, but +2 fixes it
|
||||
SampleMessages(lastFrame + 2);
|
||||
}
|
||||
#else
|
||||
// in player, just use ProfilerCounter (frameCount only used by messages)
|
||||
SampleCounts();
|
||||
SampleMessages(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// call this every frame to sample number of players and objects
|
||||
/// </summary>
|
||||
private void SampleCounts()
|
||||
{
|
||||
if (!NetworkServer.active)
|
||||
return;
|
||||
|
||||
Counters.PlayerCount.Sample(NetworkServer.connections.Count);
|
||||
Counters.PlayerCount.Sample(NetworkManager.singleton.numPlayers);
|
||||
Counters.ObjectCount.Sample(NetworkServer.spawned.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// call this when ProfilerDriver shows it is next frame
|
||||
/// </summary>
|
||||
/// <param name="frame"></param>
|
||||
private void SampleMessages(int frame)
|
||||
{
|
||||
_sentCounter.EndFrame(frame);
|
||||
_receivedCounter.EndFrame(frame);
|
||||
AfterSample?.Invoke(frame);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirage.Profiler/Runtime/NetworkProfilerRecorder.cs.meta
Executable file
11
Assets/Mirage.Profiler/Runtime/NetworkProfilerRecorder.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc5b42cbfc38b1c4d86fd25905b19e29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
16
Assets/Mirage.Profiler/package.json
Executable file
16
Assets/Mirage.Profiler/package.json
Executable file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "com.james-frowen.mirage-profiler",
|
||||
"displayName": "Mirage Profiler",
|
||||
"version": "1.1.0-mirror.11",
|
||||
"unity": "2021.3",
|
||||
"description": "Network profiler for Mirage",
|
||||
"author": "James Frowen",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/James-Frowen/Mirage.Profiler"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.unity.profiling.core": "1.0.2"
|
||||
},
|
||||
"samples": []
|
||||
}
|
7
Assets/Mirage.Profiler/package.json.meta
Executable file
7
Assets/Mirage.Profiler/package.json.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c236d885d1e08b41870190b5e9f0d37
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
@ -0,0 +1,590 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
[AddComponentMenu("Network/Network Transform (Unreliable)")]
|
||||
public class HybridNetworkTransform : NetworkTransformBase
|
||||
{
|
||||
[Header("Full Send Interval Multiplier")]
|
||||
[Tooltip("Check/Sync every multiple of Network Manager send interval (= 1 / NM Send Rate), instead of every send interval.\n(30 NM send rate, and 3 interval, is a send every 0.1 seconds)\nA larger interval means less network sends, which has a variety of upsides. The drawbacks are delays and lower accuracy, you should find a nice balance between not sending too much, but the results looking good for your particular scenario.")]
|
||||
[Range(1, 120)]
|
||||
public uint fullSendIntervalMultiplier = 30;
|
||||
private uint fullSendIntervalCounter = 0;
|
||||
double lastFullSendIntervalTime = double.MinValue;
|
||||
private byte lastSentFullSyncIndex = 0;
|
||||
private SyncDataFull lastSentFullSyncData;
|
||||
private QuantizedSnapshot lastSentFullQuantized;
|
||||
private SyncDataFull lastReceivedFullSyncData;
|
||||
private QuantizedSnapshot lastReceivedFullQuantized;
|
||||
|
||||
|
||||
[Header("Delta Send Interval Multiplier")]
|
||||
[Tooltip("Check/Sync every multiple of Network Manager send interval (= 1 / NM Send Rate), instead of every send interval.\n(30 NM send rate, and 3 interval, is a send every 0.1 seconds)\nA larger interval means less network sends, which has a variety of upsides. The drawbacks are delays and lower accuracy, you should find a nice balance between not sending too much, but the results looking good for your particular scenario.")]
|
||||
[Range(1, 120)]
|
||||
public uint deltaSendIntervalMultiplier = 1;
|
||||
private uint deltaSendIntervalCounter = 0;
|
||||
double lastDeltaSendIntervalTime = double.MinValue;
|
||||
|
||||
[Header("Rotation")]
|
||||
[Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
|
||||
public float rotationSensitivity = 0.01f;
|
||||
|
||||
[Header("Precision")]
|
||||
[Tooltip("Position is rounded in order to drastically minimize bandwidth.\n\nFor example, a precision of 0.01 rounds to a centimeter. In other words, sub-centimeter movements aren't synced until they eventually exceeded an actual centimeter.\n\nDepending on how important the object is, a precision of 0.01-0.10 (1-10 cm) is recommended.\n\nFor example, even a 1cm precision combined with delta compression cuts the Benchmark demo's bandwidth in half, compared to sending every tiny change.")]
|
||||
[Range(0.00_01f, 1f)] // disallow 0 division. 1mm to 1m precision is enough range.
|
||||
public float positionPrecision = 0.01f; // 1 cm
|
||||
[Range(0.00_01f, 1f)] // disallow 0 division. 1mm to 1m precision is enough range.
|
||||
public float scalePrecision = 0.01f; // 1 cm
|
||||
|
||||
[SerializeField] protected SyncSettings syncSettings;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
|
||||
// NTBase has options to sync pos/rot/scale. Sync Settings has the same ability
|
||||
// If sync settings is not set, we use NTBase's settings.
|
||||
if (syncSettings != SyncSettings.None)
|
||||
syncSettings = InitSyncSettings();
|
||||
}
|
||||
|
||||
protected virtual SyncSettings InitSyncSettings()
|
||||
{
|
||||
SyncSettings syncSettings = SyncSettings.None;
|
||||
|
||||
if (syncPosition) syncSettings |= (SyncSettings.SyncPosX | SyncSettings.SyncPosY | SyncSettings.SyncPosZ);
|
||||
if (syncRotation) syncSettings |= SyncSettings.SyncRot;
|
||||
if (syncScale) syncSettings |= SyncSettings.SyncScale;
|
||||
if (compressRotation) syncSettings |= SyncSettings.CompressRot;
|
||||
|
||||
return syncSettings;
|
||||
}
|
||||
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
if ((syncSettings & (SyncSettings.CompressRot | SyncSettings.UseEulerAngles)) > 0) syncSettings &= ~SyncSettings.CompressRot;
|
||||
|
||||
fullSendIntervalMultiplier = Math.Max(deltaSendIntervalMultiplier, fullSendIntervalMultiplier) +1;
|
||||
}
|
||||
|
||||
#region Apply Interpolation
|
||||
void Update()
|
||||
{
|
||||
if (isServer) UpdateServerInterpolation();
|
||||
// for all other clients (and for local player if !authority),
|
||||
// we need to apply snapshots from the buffer.
|
||||
// 'else if' because host mode shouldn't interpolate client
|
||||
else if (isClient && !IsClientWithAuthority) UpdateClientInterpolation();
|
||||
}
|
||||
|
||||
void UpdateServerInterpolation()
|
||||
{
|
||||
// apply buffered snapshots IF client authority
|
||||
// -> in server authority, server moves the object
|
||||
// so no need to apply any snapshots there.
|
||||
// -> don't apply for host mode player objects either, even if in
|
||||
// client authority mode. if it doesn't go over the network,
|
||||
// then we don't need to do anything.
|
||||
// -> connectionToClient is briefly null after scene changes:
|
||||
// https://github.com/MirrorNetworking/Mirror/issues/3329
|
||||
if (syncDirection == SyncDirection.ClientToServer &&
|
||||
connectionToClient != null &&
|
||||
!isOwned)
|
||||
{
|
||||
if (serverSnapshots.Count == 0) return;
|
||||
|
||||
// step the transform interpolation without touching time.
|
||||
// NetworkClient is responsible for time globally.
|
||||
SnapshotInterpolation.StepInterpolation(
|
||||
serverSnapshots,
|
||||
connectionToClient.remoteTimeline,
|
||||
out TransformSnapshot from,
|
||||
out TransformSnapshot to,
|
||||
out double t);
|
||||
|
||||
// interpolate & apply
|
||||
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
|
||||
Apply(computed, to);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateClientInterpolation()
|
||||
{
|
||||
// only while we have snapshots
|
||||
if (clientSnapshots.Count == 0) return;
|
||||
|
||||
// step the interpolation without touching time.
|
||||
// NetworkClient is responsible for time globally.
|
||||
SnapshotInterpolation.StepInterpolation(
|
||||
clientSnapshots,
|
||||
NetworkTime.time, // == NetworkClient.localTimeline from snapshot interpolation
|
||||
out TransformSnapshot from,
|
||||
out TransformSnapshot to,
|
||||
out double t);
|
||||
|
||||
// interpolate & apply
|
||||
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
|
||||
Apply(computed, to);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Initial State Serialization
|
||||
public override void OnSerialize(NetworkWriter writer, bool initialState)
|
||||
{
|
||||
// sync target component's position on spawn.
|
||||
// fixes https://github.com/vis2k/Mirror/pull/3051/
|
||||
// (Spawn message wouldn't sync NTChild positions either)
|
||||
if (initialState)
|
||||
{
|
||||
if (syncPosition) writer.WriteVector3(GetPosition());
|
||||
if (syncRotation) writer.WriteQuaternion(GetRotation());
|
||||
if (syncScale) writer.WriteVector3(GetScale());
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnDeserialize(NetworkReader reader, bool initialState)
|
||||
{
|
||||
// sync target component's position on spawn.
|
||||
// fixes https://github.com/vis2k/Mirror/pull/3051/
|
||||
// (Spawn message wouldn't sync NTChild positions either)
|
||||
if (initialState)
|
||||
{
|
||||
if (syncPosition) SetPosition(reader.ReadVector3());
|
||||
if (syncRotation) SetRotation(reader.ReadQuaternion());
|
||||
if (syncScale) SetScale(reader.ReadVector3());
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
// if server then always sync to others.
|
||||
if (isServer) UpdateServerBroadcast();
|
||||
// client authority, and local player (= allowed to move myself)?
|
||||
// 'else if' because host mode shouldn't send anything to server.
|
||||
// it is the server. don't overwrite anything there.
|
||||
else if (isClient && IsClientWithAuthority) UpdateClientBroadcast();
|
||||
}
|
||||
void UpdateServerBroadcast()
|
||||
{
|
||||
// broadcast to all clients each 'sendInterval'
|
||||
// (client with authority will drop the rpc)
|
||||
// NetworkTime.localTime for double precision until Unity has it too
|
||||
//
|
||||
// IMPORTANT:
|
||||
// snapshot interpolation requires constant sending.
|
||||
// DO NOT only send if position changed. for example:
|
||||
// ---
|
||||
// * client sends first position at t=0
|
||||
// * ... 10s later ...
|
||||
// * client moves again, sends second position at t=10
|
||||
// ---
|
||||
// * server gets first position at t=0
|
||||
// * server gets second position at t=10
|
||||
// * server moves from first to second within a time of 10s
|
||||
// => would be a super slow move, instead of a wait & move.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// DO NOT send nulls if not changed 'since last send' either. we
|
||||
// send unreliable and don't know which 'last send' the other end
|
||||
// received successfully.
|
||||
//
|
||||
// Checks to ensure server only sends snapshots if object is
|
||||
// on server authority(!clientAuthority) mode because on client
|
||||
// authority mode snapshots are broadcasted right after the authoritative
|
||||
// client updates server in the command function(see above), OR,
|
||||
// since host does not send anything to update the server, any client
|
||||
// authoritative movement done by the host will have to be broadcasted
|
||||
// here by checking IsClientWithAuthority.
|
||||
// TODO send same time that NetworkServer sends time snapshot?
|
||||
CheckLastSendTime();
|
||||
if (syncDirection == SyncDirection.ServerToClient || IsClientWithAuthority)
|
||||
{
|
||||
if (fullSendIntervalCounter == fullSendIntervalMultiplier) ServerBroadcastFull();
|
||||
else if (deltaSendIntervalCounter == deltaSendIntervalMultiplier) ServerBroadcastDelta();
|
||||
}
|
||||
}
|
||||
#region Server Broadcast Full
|
||||
protected virtual void ServerBroadcastFull()
|
||||
{
|
||||
lastSentFullSyncData = ConstructFullSyncData(true);
|
||||
|
||||
lastSentFullQuantized = ConstructQuantizedSnapshot(lastSentFullSyncData.position, lastSentFullSyncData.rotation, lastSentFullSyncData.scale);
|
||||
|
||||
RpcServerToClientSyncFull(lastSentFullSyncData);
|
||||
}
|
||||
|
||||
private byte NextFullSyncIndex()
|
||||
{
|
||||
if (lastSentFullSyncIndex == 255) lastSentFullSyncIndex = 0;
|
||||
else lastSentFullSyncIndex += 1;
|
||||
|
||||
return lastSentFullSyncIndex;
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcServerToClientSyncFull(SyncDataFull syncData) =>
|
||||
OnServerToClientSyncFull(syncData);
|
||||
|
||||
protected virtual void OnServerToClientSyncFull(SyncDataFull syncData)
|
||||
{
|
||||
// in host mode, the server sends rpcs to all clients.
|
||||
// the host client itself will receive them too.
|
||||
// -> host server is always the source of truth
|
||||
// -> we can ignore any rpc on the host client
|
||||
// => otherwise host objects would have ever growing clientBuffers
|
||||
// (rpc goes to clients. if isServer is true too then we are host)
|
||||
if (isServer) return;
|
||||
|
||||
// don't apply for local player with authority
|
||||
if (IsClientWithAuthority) return;
|
||||
|
||||
double timestamp = NetworkClient.connection.remoteTimeStamp;
|
||||
|
||||
// TODO, if we are syncing full by pos axis, we need to maybe
|
||||
// use current non-synced axis instead of giving it a 0.
|
||||
lastReceivedFullSyncData = syncData;
|
||||
lastReceivedFullQuantized = ConstructQuantizedSnapshot(syncData.position, syncData.rotation, syncData.scale);
|
||||
|
||||
// We don't care if we are adding 'default' to any field because
|
||||
// syncing is checked again in Apply before applying the changes.
|
||||
AddSnapshot(clientSnapshots, timestamp + timeStampAdjustment + offset, syncData.position, syncData.rotation, syncData.scale);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Server Broadcast Delta
|
||||
protected virtual void ServerBroadcastDelta()
|
||||
{
|
||||
SyncDataFull currentFull = ConstructFullSyncData(false);
|
||||
QuantizedSnapshot currentQuantized = ConstructQuantizedSnapshot(currentFull.position, currentFull.rotation, currentFull.scale);
|
||||
|
||||
SyncDataDelta syncDataDelta = DeriveDelta(currentQuantized);
|
||||
|
||||
RpcServerToClientSyncDelta(syncDataDelta);
|
||||
}
|
||||
|
||||
[ClientRpc (channel = Channels.Unreliable)]
|
||||
void RpcServerToClientSyncDelta(SyncDataDelta syncData) =>
|
||||
OnServerToClientSyncDelta(syncData);
|
||||
|
||||
protected virtual void OnServerToClientSyncDelta(SyncDataDelta delta)
|
||||
{
|
||||
// in host mode, the server sends rpcs to all clients.
|
||||
// the host client itself will receive them too.
|
||||
// -> host server is always the source of truth
|
||||
// -> we can ignore any rpc on the host client
|
||||
// => otherwise host objects would have ever growing clientBuffers
|
||||
// (rpc goes to clients. if isServer is true too then we are host)
|
||||
if (isServer) return;
|
||||
|
||||
// don't apply for local player with authority
|
||||
if (IsClientWithAuthority) return;
|
||||
|
||||
double timestamp = NetworkClient.connection.remoteTimeStamp;
|
||||
|
||||
// If the delta syncdata is not based on the last received full sync, we discard.
|
||||
if (delta.fullSyncDataIndex != lastReceivedFullSyncData.fullSyncDataIndex) return;
|
||||
|
||||
ApplyDelta(delta, out Vector3 position, out Quaternion rotation, out Vector3 scale);
|
||||
|
||||
// We don't care if we are adding 'default' to any field because
|
||||
// syncing is checked again in Apply before applying the changes.
|
||||
AddSnapshot(clientSnapshots, timestamp + timeStampAdjustment + offset, position, rotation, scale);
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
/*
|
||||
if (sendIntervalCounter == sendIntervalMultiplier && // same interval as time interpolation!
|
||||
(syncDirection == SyncDirection.ServerToClient || IsClientWithAuthority))
|
||||
{
|
||||
// send snapshot without timestamp.
|
||||
// receiver gets it from batch timestamp to save bandwidth.
|
||||
TransformSnapshot snapshot = Construct();
|
||||
cachedSnapshotComparison = CompareSnapshots(snapshot);
|
||||
if (cachedSnapshotComparison && hasSentUnchangedPosition && onlySyncOnChange) { return; }
|
||||
|
||||
if (compressRotation)
|
||||
{
|
||||
RpcServerToClientSyncCompressRotation(
|
||||
// only sync what the user wants to sync
|
||||
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
|
||||
syncRotation && rotationChanged ? Compression.CompressQuaternion(snapshot.rotation) : default(uint?),
|
||||
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
RpcServerToClientSync(
|
||||
// only sync what the user wants to sync
|
||||
syncPosition && positionChanged ? snapshot.position : default(Vector3?),
|
||||
syncRotation && rotationChanged ? snapshot.rotation : default(Quaternion?),
|
||||
syncScale && scaleChanged ? snapshot.scale : default(Vector3?)
|
||||
);
|
||||
}
|
||||
|
||||
if (cachedSnapshotComparison)
|
||||
{
|
||||
hasSentUnchangedPosition = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasSentUnchangedPosition = false;
|
||||
lastSnapshot = snapshot;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void UpdateClientBroadcast()
|
||||
{
|
||||
// https://github.com/vis2k/Mirror/pull/2992/
|
||||
if (!NetworkClient.ready) return;
|
||||
|
||||
// send to server each 'sendInterval'
|
||||
// NetworkTime.localTime for double precision until Unity has it too
|
||||
//
|
||||
// IMPORTANT:
|
||||
// snapshot interpolation requires constant sending.
|
||||
// DO NOT only send if position changed. for example:
|
||||
// ---
|
||||
// * client sends first position at t=0
|
||||
// * ... 10s later ...
|
||||
// * client moves again, sends second position at t=10
|
||||
// ---
|
||||
// * server gets first position at t=0
|
||||
// * server gets second position at t=10
|
||||
// * server moves from first to second within a time of 10s
|
||||
// => would be a super slow move, instead of a wait & move.
|
||||
//
|
||||
// IMPORTANT:
|
||||
// DO NOT send nulls if not changed 'since last send' either. we
|
||||
// send unreliable and don't know which 'last send' the other end
|
||||
// received successfully.
|
||||
CheckLastSendTime();
|
||||
|
||||
if (syncDirection == SyncDirection.ServerToClient) return;
|
||||
|
||||
if (fullSendIntervalCounter == fullSendIntervalMultiplier) ClientBroadcastFull();
|
||||
else if (deltaSendIntervalCounter == deltaSendIntervalMultiplier) ClientBroadcastDelta();
|
||||
}
|
||||
|
||||
#region Client Broadcast Full
|
||||
protected virtual void ClientBroadcastFull()
|
||||
{
|
||||
lastSentFullSyncData = ConstructFullSyncData(true);
|
||||
|
||||
lastSentFullQuantized = ConstructQuantizedSnapshot(lastSentFullSyncData.position, lastSentFullSyncData.rotation, lastSentFullSyncData.scale);
|
||||
|
||||
CmdClientToServerSyncFull(lastSentFullSyncData);
|
||||
}
|
||||
|
||||
[Command]
|
||||
void CmdClientToServerSyncFull(SyncDataFull syncData)
|
||||
{
|
||||
OnClientToServerSyncFull(syncData);
|
||||
|
||||
if (syncDirection == SyncDirection.ClientToServer)
|
||||
RpcServerToClientSyncFull(syncData);
|
||||
}
|
||||
|
||||
protected virtual void OnClientToServerSyncFull(SyncDataFull syncData)
|
||||
{
|
||||
// in host mode, the server sends rpcs to all clients.
|
||||
// the host client itself will receive them too.
|
||||
// -> host server is always the source of truth
|
||||
// -> we can ignore any rpc on the host client
|
||||
// => otherwise host objects would have ever growing clientBuffers
|
||||
// (rpc goes to clients. if isServer is true too then we are host)
|
||||
if (syncDirection != SyncDirection.ClientToServer) return;
|
||||
|
||||
double timestamp = connectionToClient.remoteTimeStamp;
|
||||
|
||||
// See Server's issue
|
||||
lastReceivedFullSyncData = syncData;
|
||||
lastReceivedFullQuantized = ConstructQuantizedSnapshot(syncData.position, syncData.rotation, syncData.scale);
|
||||
|
||||
// We don't care if we are adding 'default' to any field because
|
||||
// syncing is checked again in Apply before applying the changes.
|
||||
AddSnapshot(serverSnapshots, timestamp + timeStampAdjustment + offset, syncData.position, syncData.rotation, syncData.scale);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Client Broadcast Delta
|
||||
protected virtual void ClientBroadcastDelta()
|
||||
{
|
||||
SyncDataFull currentFull = ConstructFullSyncData(false);
|
||||
QuantizedSnapshot currentQuantized = ConstructQuantizedSnapshot(currentFull.position, currentFull.rotation, currentFull.scale);
|
||||
|
||||
SyncDataDelta syncDataDelta = DeriveDelta(currentQuantized);
|
||||
Debug.Log($"Client sending sync data delta index: {syncDataDelta.fullSyncDataIndex}");
|
||||
CmdClientToServerSyncDelta(syncDataDelta);
|
||||
}
|
||||
|
||||
[Command(channel = Channels.Unreliable)]
|
||||
void CmdClientToServerSyncDelta(SyncDataDelta delta)
|
||||
{
|
||||
OnClientToServerSyncDelta(delta);
|
||||
|
||||
if (syncDirection == SyncDirection.ClientToServer)
|
||||
RpcServerToClientSyncDelta(delta);
|
||||
}
|
||||
|
||||
protected virtual void OnClientToServerSyncDelta(SyncDataDelta delta)
|
||||
{
|
||||
// only apply if in client authority mode
|
||||
if (syncDirection != SyncDirection.ClientToServer) return;
|
||||
|
||||
// protect against ever growing buffer size attacks
|
||||
if (serverSnapshots.Count >= connectionToClient.snapshotBufferSizeLimit) return;
|
||||
|
||||
//if (!isLocalPlayer) Debug.Log($"delta index received: {delta.fullSyncDataIndex}, last received {lastReceivedFullSyncData.fullSyncDataIndex}");
|
||||
if (delta.fullSyncDataIndex != lastReceivedFullSyncData.fullSyncDataIndex) return;
|
||||
|
||||
double timestamp = connectionToClient.remoteTimeStamp;
|
||||
|
||||
ApplyDelta(delta, out Vector3 position, out Quaternion rotation, out Vector3 scale);
|
||||
|
||||
// We don't care if we are adding 'default' to any field because
|
||||
// syncing is checked again in Apply before applying the changes.
|
||||
AddSnapshot(serverSnapshots, timestamp + timeStampAdjustment + offset, position, rotation, scale);
|
||||
}
|
||||
#endregion
|
||||
protected virtual SyncDataFull ConstructFullSyncData(bool updateIndex)
|
||||
{
|
||||
return new SyncDataFull(
|
||||
updateIndex? NextFullSyncIndex() : lastSentFullSyncIndex,
|
||||
syncSettings,
|
||||
GetPosition(),
|
||||
GetRotation(),
|
||||
GetScale()
|
||||
);
|
||||
}
|
||||
|
||||
protected virtual QuantizedSnapshot ConstructQuantizedSnapshot(Vector3 position, Quaternion rotation, Vector3 scale)
|
||||
{
|
||||
Compression.ScaleToLong(position, positionPrecision, out Vector3Long positionQuantized);
|
||||
Compression.ScaleToLong(scale, scalePrecision, out Vector3Long scaleQuantized);
|
||||
return new QuantizedSnapshot(
|
||||
positionQuantized,
|
||||
rotation,
|
||||
scaleQuantized
|
||||
);
|
||||
}
|
||||
|
||||
protected virtual SyncDataDelta DeriveDelta(QuantizedSnapshot current)
|
||||
{
|
||||
SyncDataDelta syncDataDelta = new SyncDataDelta();
|
||||
syncDataDelta.fullSyncDataIndex = lastSentFullSyncIndex;
|
||||
syncDataDelta.deltaHeader = DeltaHeader.None;
|
||||
|
||||
syncDataDelta.position = current.position - lastSentFullQuantized.position;
|
||||
|
||||
if ((syncSettings & SyncSettings.SyncPosX) > 0 && syncDataDelta.position.x != 0)
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.PosX;
|
||||
|
||||
if ((syncSettings & SyncSettings.SyncPosY) > 0 && syncDataDelta.position.y != 0)
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.PosY;
|
||||
|
||||
if ((syncSettings & SyncSettings.SyncPosZ) > 0 && syncDataDelta.position.z != 0)
|
||||
{
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.PosZ;
|
||||
}
|
||||
|
||||
// Rotation: We have 3 options:
|
||||
// 1) Send compressed Quaternion
|
||||
// 2) Send uncompressed Quaternion
|
||||
// 3) Send Euler Angles
|
||||
// If user only ever rotates 1, 2 axes then option 3 may save more bandwidth since
|
||||
// we delta each axis.
|
||||
// 1 and 2 prevents gimbal lock etc, and 2 if user requires absolute precision.
|
||||
// We use 4 bits to express rotation type and change (NonEulerAngles, RotX, RotY, RotZ):
|
||||
// 1) If nothing has changed or < rotationSensitivity, all 4 will be false. Doesn't matter which method
|
||||
// we are treating rotation. We are not reading anything on the receiving side.
|
||||
// 2) If NonEulerAngles is false, we check the next 3 for each individual axis.
|
||||
// 3) If NonEulerAngles is true, we are sending Quaternion. We piggyback on the RotX bit to tell us
|
||||
// if it is compressed Quat or uncompressed Quat.
|
||||
if ((syncSettings & SyncSettings.SyncRot) > 0)
|
||||
{
|
||||
if ((syncSettings & SyncSettings.UseEulerAngles) > 0)
|
||||
{
|
||||
Compression.ScaleToLong(lastSentFullQuantized.rotation.eulerAngles, rotationSensitivity, out Vector3Long lastRotationEuler);
|
||||
Compression.ScaleToLong(current.rotation.eulerAngles, rotationSensitivity, out Vector3Long currentRotationEuler);
|
||||
|
||||
syncDataDelta.eulRotation = currentRotationEuler - lastRotationEuler;
|
||||
|
||||
if (syncDataDelta.eulRotation.x != 0) syncDataDelta.deltaHeader |= DeltaHeader.RotX;
|
||||
if (syncDataDelta.eulRotation.y != 0) syncDataDelta.deltaHeader |= DeltaHeader.RotY;
|
||||
if (syncDataDelta.eulRotation.z != 0) syncDataDelta.deltaHeader |= DeltaHeader.RotZ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Quaternion.Angle(lastSentFullQuantized.rotation, current.rotation) > rotationSensitivity)
|
||||
{
|
||||
syncDataDelta.quatRotation = current.rotation;
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.SendQuat;
|
||||
if ((syncSettings & SyncSettings.CompressRot) > 0)
|
||||
{
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.RotX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((syncSettings & SyncSettings.SyncScale) > 0)
|
||||
{
|
||||
syncDataDelta.scale = current.scale - lastSentFullQuantized.scale;
|
||||
if (syncDataDelta.scale != Vector3Long.zero)
|
||||
{
|
||||
syncDataDelta.deltaHeader |= DeltaHeader.Scale;
|
||||
}
|
||||
}
|
||||
|
||||
return syncDataDelta;
|
||||
}
|
||||
|
||||
protected virtual void ApplyDelta(SyncDataDelta delta, out Vector3 position, out Quaternion rotation, out Vector3 scale)
|
||||
{
|
||||
position = Compression.ScaleToFloat(lastReceivedFullQuantized.position + delta.position, positionPrecision);
|
||||
|
||||
if ((lastReceivedFullSyncData.syncSettings & SyncSettings.UseEulerAngles) > 0)
|
||||
{
|
||||
Vector3 eulRotation = Compression.ScaleToFloat(lastReceivedFullQuantized.rotationEuler + delta.eulRotation, rotationSensitivity);
|
||||
|
||||
rotation = Quaternion.Euler(eulRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((delta.deltaHeader & DeltaHeader.SendQuat) > 0)
|
||||
rotation = delta.quatRotation;
|
||||
else
|
||||
rotation = lastReceivedFullSyncData.rotation;
|
||||
}
|
||||
|
||||
scale = Compression.ScaleToFloat(lastReceivedFullQuantized.scale + delta.scale, scalePrecision);
|
||||
}
|
||||
|
||||
protected virtual void CheckLastSendTime()
|
||||
{
|
||||
// We check interval every frame, and then send if interval is reached.
|
||||
// So by the time sendIntervalCounter == sendIntervalMultiplier, data is sent,
|
||||
// thus we reset the counter here.
|
||||
// This fixes previous issue of, if sendIntervalMultiplier = 1, we send every frame,
|
||||
// because intervalCounter is always = 1 in the previous version.
|
||||
|
||||
if (fullSendIntervalCounter == fullSendIntervalMultiplier) fullSendIntervalCounter = 0;
|
||||
|
||||
if (deltaSendIntervalCounter == deltaSendIntervalMultiplier) deltaSendIntervalCounter = 0;
|
||||
|
||||
// timeAsDouble not available in older Unity versions.
|
||||
if (AccurateInterval.Elapsed(NetworkTime.localTime, NetworkServer.sendInterval, ref lastFullSendIntervalTime))
|
||||
fullSendIntervalCounter++;
|
||||
|
||||
if (AccurateInterval.Elapsed(NetworkTime.localTime, NetworkServer.sendInterval, ref lastDeltaSendIntervalTime))
|
||||
deltaSendIntervalCounter++;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb1619f96225342ac97095bd7e87d3e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
210
Assets/Mirror/Components/NetworkTransform/SyncData.cs
Normal file
210
Assets/Mirror/Components/NetworkTransform/SyncData.cs
Normal file
@ -0,0 +1,210 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
//using Unity.VisualScripting;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct SyncDataFull
|
||||
{
|
||||
public byte fullSyncDataIndex;
|
||||
public SyncSettings syncSettings;
|
||||
public Vector3 position;
|
||||
public Quaternion rotation;
|
||||
public Vector3 scale;
|
||||
|
||||
public SyncDataFull(byte fullSyncDataIndex, SyncSettings syncSettings, Vector3 position, Quaternion rotation, Vector3 scale)
|
||||
{
|
||||
this.fullSyncDataIndex = fullSyncDataIndex;
|
||||
this.syncSettings = syncSettings;
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
this.scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
public struct SyncDataDelta
|
||||
{
|
||||
public byte fullSyncDataIndex;
|
||||
public DeltaHeader deltaHeader;
|
||||
public Vector3Long position;
|
||||
public Quaternion quatRotation;
|
||||
public Vector3Long eulRotation;
|
||||
public Vector3Long scale;
|
||||
|
||||
public SyncDataDelta(byte fullSyncDataIndex, DeltaHeader deltaHeader, Vector3Long position, Quaternion rotation,Vector3Long eulRotation, Vector3Long scale)
|
||||
{
|
||||
this.fullSyncDataIndex = fullSyncDataIndex;
|
||||
this.deltaHeader = deltaHeader;
|
||||
this.position = position;
|
||||
this.quatRotation = rotation;
|
||||
this.eulRotation = eulRotation;
|
||||
this.scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
public struct QuantizedSnapshot
|
||||
{
|
||||
public Vector3Long position;
|
||||
public Quaternion rotation;
|
||||
public Vector3Long rotationEuler;
|
||||
public Vector3Long scale;
|
||||
|
||||
public QuantizedSnapshot(Vector3Long position, Quaternion rotation, Vector3Long scale)
|
||||
{
|
||||
this.position = position;
|
||||
this.rotation = rotation;
|
||||
this.rotationEuler = new Vector3Long();
|
||||
this.scale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DeltaHeader : byte
|
||||
{
|
||||
None = 0,
|
||||
PosX = 1 << 0,
|
||||
PosY = 1 << 1,
|
||||
PosZ = 1 << 2,
|
||||
SendQuat = 1 << 3,
|
||||
RotX = 1 << 4,
|
||||
RotY = 1 << 5,
|
||||
RotZ = 1 << 6,
|
||||
Scale = 1 << 7,
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum SyncSettings : byte
|
||||
{
|
||||
None = 0,
|
||||
SyncPosX = 1 << 0,
|
||||
SyncPosY = 1 << 1,
|
||||
SyncPosZ = 1 << 2,
|
||||
SyncRot = 1 << 3,
|
||||
SyncScale = 1 << 4,
|
||||
CompressRot = 1 << 5,
|
||||
UseEulerAngles = 1 << 6,
|
||||
|
||||
}
|
||||
|
||||
public static class SyncDataReaderWriter
|
||||
{
|
||||
public static void WriteSyncDataFull(this NetworkWriter writer, SyncDataFull syncData)
|
||||
{
|
||||
writer.WriteByte(syncData.fullSyncDataIndex);
|
||||
writer.WriteByte((byte)syncData.syncSettings);
|
||||
|
||||
if ((syncData.syncSettings & SyncSettings.SyncPosX) > 0) writer.WriteFloat(syncData.position.x);
|
||||
if ((syncData.syncSettings & SyncSettings.SyncPosY) > 0) writer.WriteFloat(syncData.position.y);
|
||||
if ((syncData.syncSettings & SyncSettings.SyncPosZ) > 0) writer.WriteFloat(syncData.position.z);
|
||||
|
||||
if ((syncData.syncSettings & SyncSettings.SyncRot) > 0)
|
||||
{
|
||||
if ((syncData.syncSettings & SyncSettings.CompressRot) > 0) writer.WriteUInt(Compression.CompressQuaternion(syncData.rotation));
|
||||
else writer.WriteQuaternion(syncData.rotation);
|
||||
}
|
||||
|
||||
if ((syncData.syncSettings & SyncSettings.SyncScale) > 0) writer.WriteVector3(syncData.scale);
|
||||
|
||||
}
|
||||
|
||||
public static SyncDataFull ReadSyncDataFull(this NetworkReader reader)
|
||||
{
|
||||
byte index = reader.ReadByte();
|
||||
SyncSettings syncSettings = (SyncSettings)reader.ReadByte();
|
||||
|
||||
// If we have nothing to read here, let's say because posX is unchanged, then we can write anything
|
||||
// for now, but in the NT, we will need to check changedData again, to put the right values of the axis
|
||||
// back. We don't have it here.
|
||||
|
||||
Vector3 position = new Vector3(
|
||||
(syncSettings & SyncSettings.SyncPosX) > 0 ? reader.ReadFloat() : default,
|
||||
(syncSettings & SyncSettings.SyncPosY) > 0 ? reader.ReadFloat() : default,
|
||||
(syncSettings & SyncSettings.SyncPosZ) > 0 ? reader.ReadFloat() : default
|
||||
);
|
||||
|
||||
Quaternion rotation = new Quaternion();
|
||||
if ((syncSettings & SyncSettings.SyncRot) > 0)
|
||||
rotation = (syncSettings & SyncSettings.CompressRot) > 0 ? Compression.DecompressQuaternion(reader.ReadUInt()) : reader.ReadQuaternion();
|
||||
else
|
||||
rotation = new Quaternion();
|
||||
|
||||
Vector3 scale = (syncSettings & SyncSettings.SyncScale) > 0 ? reader.ReadVector3() : default;
|
||||
|
||||
return new SyncDataFull(index, syncSettings, position, rotation, scale);
|
||||
}
|
||||
|
||||
public static void WriteSyncDataDelta(this NetworkWriter writer, SyncDataDelta syncData)
|
||||
{
|
||||
writer.WriteByte(syncData.fullSyncDataIndex);
|
||||
writer.WriteByte((byte)syncData.deltaHeader);
|
||||
|
||||
if ((syncData.deltaHeader & DeltaHeader.PosX) > 0) Compression.CompressVarInt(writer, syncData.position.x);
|
||||
if ((syncData.deltaHeader & DeltaHeader.PosY) > 0) Compression.CompressVarInt(writer, syncData.position.y);
|
||||
if ((syncData.deltaHeader & DeltaHeader.PosZ) > 0) Compression.CompressVarInt(writer, syncData.position.z);
|
||||
|
||||
if ((syncData.deltaHeader & DeltaHeader.SendQuat) > 0)
|
||||
{
|
||||
if ((syncData.deltaHeader & DeltaHeader.RotX) > 0) writer.WriteUInt(Compression.CompressQuaternion(syncData.quatRotation));
|
||||
else writer.WriteQuaternion(syncData.quatRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((syncData.deltaHeader & DeltaHeader.RotX) > 0) Compression.CompressVarInt(writer, syncData.eulRotation.x);
|
||||
if ((syncData.deltaHeader & DeltaHeader.RotY) > 0) Compression.CompressVarInt(writer, syncData.eulRotation.y);
|
||||
if ((syncData.deltaHeader & DeltaHeader.RotZ) > 0) Compression.CompressVarInt(writer, syncData.eulRotation.z);
|
||||
}
|
||||
|
||||
if ((syncData.deltaHeader & DeltaHeader.Scale) > 0)
|
||||
{
|
||||
Compression.CompressVarInt(writer, syncData.scale.x);
|
||||
Compression.CompressVarInt(writer, syncData.scale.y);
|
||||
Compression.CompressVarInt(writer, syncData.scale.z);
|
||||
}
|
||||
}
|
||||
|
||||
public static SyncDataDelta ReadSyncDataDelta(this NetworkReader reader)
|
||||
{
|
||||
byte index = reader.ReadByte();
|
||||
DeltaHeader header = (DeltaHeader)reader.ReadByte();
|
||||
|
||||
Vector3Long position = new Vector3Long(
|
||||
(header & DeltaHeader.PosX) > 0 ? Compression.DecompressVarInt(reader) : 0,
|
||||
(header & DeltaHeader.PosY) > 0 ? Compression.DecompressVarInt(reader) : 0,
|
||||
(header & DeltaHeader.PosZ) > 0 ? Compression.DecompressVarInt(reader) : 0
|
||||
);
|
||||
|
||||
Quaternion quatRotation = new Quaternion();
|
||||
Vector3Long eulRotation = new Vector3Long();
|
||||
|
||||
if ((header & DeltaHeader.SendQuat) > 0)
|
||||
{
|
||||
if ((header & DeltaHeader.RotX) > 0) quatRotation = Compression.DecompressQuaternion(reader.ReadUInt());
|
||||
else quatRotation = reader.ReadQuaternion();
|
||||
}
|
||||
else
|
||||
{
|
||||
eulRotation = new Vector3Long(
|
||||
(header & DeltaHeader.RotX) > 0 ? Compression.DecompressVarInt(reader) : 0,
|
||||
(header & DeltaHeader.RotY) > 0 ? Compression.DecompressVarInt(reader) : 0,
|
||||
(header & DeltaHeader.RotZ) > 0 ? Compression.DecompressVarInt(reader) : 0
|
||||
);
|
||||
}
|
||||
|
||||
Vector3Long scale = new Vector3Long();
|
||||
if ((header & DeltaHeader.Scale) > 0)
|
||||
{
|
||||
scale = new Vector3Long(
|
||||
Compression.DecompressVarInt(reader),
|
||||
Compression.DecompressVarInt(reader),
|
||||
Compression.DecompressVarInt(reader)
|
||||
);
|
||||
}
|
||||
|
||||
return new SyncDataDelta(index, header, position, quatRotation, eulRotation, scale);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Mirror/Components/NetworkTransform/SyncData.cs.meta
Normal file
11
Assets/Mirror/Components/NetworkTransform/SyncData.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c363936c3e954543824fa48e6534ed9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -15,6 +15,7 @@ GameObject:
|
||||
- component: {fileID: 2240606817507776182}
|
||||
- component: {fileID: 6900008319038825817}
|
||||
- component: {fileID: 5194388907919410155}
|
||||
- component: {fileID: 6104086894243768031}
|
||||
m_Layer: 0
|
||||
m_Name: Tank
|
||||
m_TagString: Untagged
|
||||
@ -29,14 +30,15 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916082411674582}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 7831918942946891954}
|
||||
- {fileID: 4116800716706440423}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &114118589361100106
|
||||
MonoBehaviour:
|
||||
@ -50,9 +52,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 9b91ecbcc199f4492b9a91e820070131, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
clientStarted: 0
|
||||
sceneId: 0
|
||||
_assetId: 3454335836
|
||||
_assetId: 2638947628
|
||||
serverOnly: 0
|
||||
visible: 0
|
||||
hasSpawned: 0
|
||||
@ -63,7 +64,7 @@ MonoBehaviour:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916082411674582}
|
||||
m_Enabled: 1
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a553cb17010b2403e8523b558bffbc14, type: 3}
|
||||
m_Name:
|
||||
@ -75,6 +76,8 @@ MonoBehaviour:
|
||||
syncPosition: 1
|
||||
syncRotation: 1
|
||||
syncScale: 0
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 1
|
||||
interpolatePosition: 1
|
||||
interpolateRotation: 1
|
||||
interpolateScale: 0
|
||||
@ -84,8 +87,6 @@ MonoBehaviour:
|
||||
showGizmos: 0
|
||||
showOverlay: 0
|
||||
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 1
|
||||
bufferResetMultiplier: 5
|
||||
positionSensitivity: 0.01
|
||||
rotationSensitivity: 0.01
|
||||
@ -117,7 +118,7 @@ MonoBehaviour:
|
||||
health: 4
|
||||
--- !u!95 &2240606817507776182
|
||||
Animator:
|
||||
serializedVersion: 3
|
||||
serializedVersion: 5
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
@ -130,10 +131,12 @@ Animator:
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorControllerStateOnDisable: 0
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
--- !u!195 &6900008319038825817
|
||||
NavMeshAgent:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -164,11 +167,55 @@ SphereCollider:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916082411674582}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 1
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
serializedVersion: 3
|
||||
m_Radius: 0.5
|
||||
m_Center: {x: 0, y: 0.25, z: 0}
|
||||
--- !u!114 &6104086894243768031
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1916082411674582}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fb1619f96225342ac97095bd7e87d3e6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
syncDirection: 1
|
||||
syncMode: 0
|
||||
syncInterval: 0
|
||||
target: {fileID: 4492442352427800}
|
||||
syncPosition: 1
|
||||
syncRotation: 1
|
||||
syncScale: 1
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 0
|
||||
interpolatePosition: 1
|
||||
interpolateRotation: 1
|
||||
interpolateScale: 0
|
||||
coordinateSpace: 0
|
||||
sendIntervalMultiplier: 1
|
||||
timelineOffset: 0
|
||||
showGizmos: 0
|
||||
showOverlay: 0
|
||||
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
|
||||
fullSendIntervalMultiplier: 13
|
||||
deltaSendIntervalMultiplier: 13
|
||||
rotationSensitivity: 0.01
|
||||
positionPrecision: 0.01
|
||||
scalePrecision: 0.01
|
||||
syncSettings: 95
|
||||
--- !u!1 &4730779867780281009
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -192,12 +239,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4730779867780281009}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: -0, y: 0.000000119209275, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0.0015906466, z: 0.009359999}
|
||||
m_LocalScale: {x: 0.01, y: 0.010000003, z: 0.010000002}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 7831918942946891958}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &6425560216547760105
|
||||
GameObject:
|
||||
@ -225,12 +273,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6425560216547760105}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0.8, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4492442352427800}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1346539668293290578
|
||||
MeshRenderer:
|
||||
@ -243,10 +292,12 @@ MeshRenderer:
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
@ -271,6 +322,7 @@ MeshRenderer:
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!102 &1985504562751981867
|
||||
TextMesh:
|
||||
serializedVersion: 3
|
||||
@ -305,45 +357,12 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: afa2d590c474413d9fc183551385ed85, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &9196118806080746389
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7831918942947312790}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a553cb17010b2403e8523b558bffbc14, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
syncDirection: 1
|
||||
syncMode: 0
|
||||
syncInterval: 0
|
||||
target: {fileID: 7831918942946891958}
|
||||
syncPosition: 0
|
||||
syncRotation: 1
|
||||
syncScale: 0
|
||||
interpolatePosition: 0
|
||||
interpolateRotation: 1
|
||||
interpolateScale: 0
|
||||
coordinateSpace: 0
|
||||
sendIntervalMultiplier: 3
|
||||
timelineOffset: 1
|
||||
showGizmos: 0
|
||||
showOverlay: 0
|
||||
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 1
|
||||
bufferResetMultiplier: 5
|
||||
positionSensitivity: 0.01
|
||||
rotationSensitivity: 0.01
|
||||
scaleSensitivity: 0.01
|
||||
--- !u!1001 &7831918942947279416
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 4492442352427800}
|
||||
m_Modifications:
|
||||
- target: {fileID: 100010, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
|
||||
@ -400,6 +419,21 @@ PrefabInstance:
|
||||
objectReference: {fileID: 2100000, guid: 2e67e42170aa64aa9a33424f8045ac89, type: 2}
|
||||
m_RemovedComponents:
|
||||
- {fileID: 9500000, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects:
|
||||
- targetCorrespondingSourceObject: {fileID: 400014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 5718089106632469514}
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 100014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 9196118806080746389}
|
||||
- targetCorrespondingSourceObject: {fileID: 100014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 5018727074361621677}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 38b49695fc0a4418bbc350f2366660c5, type: 3}
|
||||
--- !u!4 &7831918942946891954 stripped
|
||||
Transform:
|
||||
@ -407,15 +441,85 @@ Transform:
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 7831918942947279416}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &7831918942947312790 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 100014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 7831918942947279416}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!4 &7831918942946891958 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 400014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 7831918942947279416}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &7831918942947312790 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 100014, guid: 38b49695fc0a4418bbc350f2366660c5,
|
||||
type: 3}
|
||||
m_PrefabInstance: {fileID: 7831918942947279416}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &9196118806080746389
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7831918942947312790}
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a553cb17010b2403e8523b558bffbc14, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
syncDirection: 1
|
||||
syncMode: 0
|
||||
syncInterval: 0
|
||||
target: {fileID: 7831918942946891958}
|
||||
syncPosition: 0
|
||||
syncRotation: 1
|
||||
syncScale: 0
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 1
|
||||
interpolatePosition: 0
|
||||
interpolateRotation: 1
|
||||
interpolateScale: 0
|
||||
coordinateSpace: 0
|
||||
sendIntervalMultiplier: 3
|
||||
timelineOffset: 1
|
||||
showGizmos: 0
|
||||
showOverlay: 0
|
||||
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
|
||||
bufferResetMultiplier: 5
|
||||
positionSensitivity: 0.01
|
||||
rotationSensitivity: 0.01
|
||||
scaleSensitivity: 0.01
|
||||
--- !u!114 &5018727074361621677
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7831918942947312790}
|
||||
m_Enabled: 0
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fb1619f96225342ac97095bd7e87d3e6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
syncDirection: 0
|
||||
syncMode: 0
|
||||
syncInterval: 0
|
||||
target: {fileID: 7831918942946891958}
|
||||
syncPosition: 1
|
||||
syncRotation: 1
|
||||
syncScale: 0
|
||||
onlySyncOnChange: 1
|
||||
compressRotation: 1
|
||||
interpolatePosition: 1
|
||||
interpolateRotation: 1
|
||||
interpolateScale: 1
|
||||
coordinateSpace: 0
|
||||
sendIntervalMultiplier: 1
|
||||
timelineOffset: 0
|
||||
showGizmos: 0
|
||||
showOverlay: 0
|
||||
overlayColor: {r: 0, g: 0, b: 0, a: 0.5}
|
||||
fullSendIntervalMultiplier: 30
|
||||
deltaSendIntervalMultiplier: 30
|
||||
rotationSensitivity: 0.01
|
||||
positionPrecision: 0.01
|
||||
scalePrecision: 0.01
|
||||
syncSettings: 0
|
||||
|
@ -98,13 +98,14 @@ LightmapSettings:
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_LightingSettings: {fileID: 4890085278179872738, guid: 1cb229a9b0b434acf9cb6b263057a2a0, type: 2}
|
||||
m_LightingSettings: {fileID: 4890085278179872738, guid: 1cb229a9b0b434acf9cb6b263057a2a0,
|
||||
type: 2}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 2
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
@ -117,7 +118,7 @@ NavMeshSettings:
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
accuratePlacement: 0
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
@ -155,9 +156,17 @@ Camera:
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_FocalLength: 50
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
@ -191,13 +200,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 88936773}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0.92387956, z: -0.38268343, w: 0}
|
||||
m_LocalPosition: {x: 0, y: 6.5, z: 8}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 45, y: 180, z: 0}
|
||||
--- !u!114 &88936778
|
||||
MonoBehaviour:
|
||||
@ -212,7 +221,9 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
height: 150
|
||||
offsetY: 40
|
||||
maxLogCount: 50
|
||||
showInEditor: 0
|
||||
hotKey: 293
|
||||
--- !u!1 &251893064
|
||||
GameObject:
|
||||
@ -238,13 +249,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 251893064}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 3, y: 0, z: 3}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 6
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &251893066
|
||||
MonoBehaviour:
|
||||
@ -282,13 +293,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 535739935}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 3, y: 0, z: -3}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 4
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &535739937
|
||||
MonoBehaviour:
|
||||
@ -371,9 +382,17 @@ MeshCollider:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1107091652}
|
||||
m_Material: {fileID: 0}
|
||||
m_IncludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_ExcludeLayers:
|
||||
serializedVersion: 2
|
||||
m_Bits: 0
|
||||
m_LayerOverridePriority: 0
|
||||
m_IsTrigger: 0
|
||||
m_ProvidesContacts: 0
|
||||
m_Enabled: 1
|
||||
serializedVersion: 4
|
||||
serializedVersion: 5
|
||||
m_Convex: 0
|
||||
m_CookingOptions: 30
|
||||
m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
|
||||
@ -392,13 +411,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1107091652}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1282001517
|
||||
GameObject:
|
||||
@ -413,6 +432,7 @@ GameObject:
|
||||
- component: {fileID: 1282001519}
|
||||
- component: {fileID: 1282001521}
|
||||
- component: {fileID: 1282001522}
|
||||
- component: {fileID: 1282001523}
|
||||
m_Layer: 0
|
||||
m_Name: NetworkManager
|
||||
m_TagString: Untagged
|
||||
@ -427,13 +447,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1282001517}
|
||||
serializedVersion: 2
|
||||
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_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1282001519
|
||||
MonoBehaviour:
|
||||
@ -463,22 +483,29 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
dontDestroyOnLoad: 1
|
||||
runInBackground: 1
|
||||
autoStartServerBuild: 1
|
||||
headlessStartMode: 1
|
||||
editorAutoStart: 0
|
||||
sendRate: 30
|
||||
autoStartServerBuild: 0
|
||||
autoConnectClientBuild: 0
|
||||
sendRate: 120
|
||||
offlineScene:
|
||||
onlineScene:
|
||||
transport: {fileID: 1282001521}
|
||||
networkAddress: localhost
|
||||
maxConnections: 100
|
||||
disconnectInactiveConnections: 0
|
||||
disconnectInactiveTimeout: 60
|
||||
authenticator: {fileID: 0}
|
||||
playerPrefab: {fileID: 1916082411674582, guid: 6f43bf5488a7443d19ab2a83c6b91f35, type: 3}
|
||||
playerPrefab: {fileID: 1916082411674582, guid: 6f43bf5488a7443d19ab2a83c6b91f35,
|
||||
type: 3}
|
||||
autoCreatePlayer: 1
|
||||
playerSpawnMethod: 1
|
||||
spawnPrefabs:
|
||||
- {fileID: 5890560936853567077, guid: b7dd46dbf38c643f09e206f9fa4be008, type: 3}
|
||||
exceptionsDisconnect: 1
|
||||
snapshotSettings:
|
||||
bufferTimeMultiplier: 2
|
||||
bufferLimit: 32
|
||||
catchupNegativeThreshold: -1
|
||||
catchupPositiveThreshold: 1
|
||||
catchupSpeed: 0.019999999552965164
|
||||
@ -487,6 +514,8 @@ MonoBehaviour:
|
||||
dynamicAdjustment: 1
|
||||
dynamicAdjustmentTolerance: 1
|
||||
deliveryTimeEmaDuration: 2
|
||||
evaluationMethod: 0
|
||||
evaluationInterval: 3
|
||||
timeInterpolationGui: 1
|
||||
--- !u!114 &1282001521
|
||||
MonoBehaviour:
|
||||
@ -500,7 +529,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 6b0fecffa3f624585964b0d0eb21b18e, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Port: 7777
|
||||
port: 7777
|
||||
DualMode: 1
|
||||
NoDelay: 1
|
||||
Interval: 10
|
||||
@ -512,8 +541,8 @@ MonoBehaviour:
|
||||
SendWindowSize: 4096
|
||||
MaxRetransmit: 40
|
||||
MaximizeSocketBuffers: 1
|
||||
ReliableMaxMessageSize: 298449
|
||||
UnreliableMaxMessageSize: 1199
|
||||
ReliableMaxMessageSize: 297433
|
||||
UnreliableMaxMessageSize: 1194
|
||||
debugLog: 0
|
||||
statisticsGUI: 0
|
||||
statisticsLog: 0
|
||||
@ -533,6 +562,18 @@ MonoBehaviour:
|
||||
padding: 2
|
||||
width: 180
|
||||
height: 25
|
||||
--- !u!114 &1282001523
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1282001517}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: bc5b42cbfc38b1c4d86fd25905b19e29, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!1 &1458789072
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -557,13 +598,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1458789072}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -3, y: 0, z: 3}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 7
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1458789074
|
||||
MonoBehaviour:
|
||||
@ -601,13 +642,13 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1501912662}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -3, y: 0, z: -3}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 5
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1501912664
|
||||
MonoBehaviour:
|
||||
@ -707,11 +748,23 @@ Transform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2054208274}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.10938167, y: 0.8754261, z: -0.40821788, w: 0.23456976}
|
||||
m_LocalPosition: {x: 0, y: 10, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 50, y: 150, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 88936777}
|
||||
- {fileID: 1107091656}
|
||||
- {fileID: 2054208276}
|
||||
- {fileID: 1282001518}
|
||||
- {fileID: 535739936}
|
||||
- {fileID: 1501912663}
|
||||
- {fileID: 251893065}
|
||||
- {fileID: 1458789073}
|
||||
|
8
Assets/ParrelSync.meta
Executable file
8
Assets/ParrelSync.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd07209623118fc46a32850135be3f32
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/ParrelSync/Examples.meta
Executable file
8
Assets/ParrelSync/Examples.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 654be33bd44a76a4c8c180d1da6ad066
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
31
Assets/ParrelSync/Examples/CustomArgumentExample.cs
Executable file
31
Assets/ParrelSync/Examples/CustomArgumentExample.cs
Executable file
@ -0,0 +1,31 @@
|
||||
// This should be editor only
|
||||
#if UNITY_EDITOR
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ParrelSync.Example
|
||||
{
|
||||
public class CustomArgumentExample : MonoBehaviour
|
||||
{
|
||||
// Start is called before the first frame update
|
||||
void Start()
|
||||
{
|
||||
// Is this editor instance running a clone project?
|
||||
if (ClonesManager.IsClone())
|
||||
{
|
||||
Debug.Log("This is a clone project.");
|
||||
|
||||
//Argument can be set from the clones manager window.
|
||||
string customArgument = ClonesManager.GetArgument();
|
||||
Debug.Log("The custom argument of this clone project is: " + customArgument);
|
||||
// Do what ever you need with the argument string.
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("This is the original project.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
11
Assets/ParrelSync/Examples/CustomArgumentExample.cs.meta
Executable file
11
Assets/ParrelSync/Examples/CustomArgumentExample.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 346d302ecc25a9a41b48b857ce51d873
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
22
Assets/ParrelSync/LICENSE.md
Executable file
22
Assets/ParrelSync/LICENSE.md
Executable file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Greg M
|
||||
Copyright (c) 2020 Ian and Contributors
|
||||
|
||||
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.
|
7
Assets/ParrelSync/LICENSE.md.meta
Executable file
7
Assets/ParrelSync/LICENSE.md.meta
Executable file
@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4b327eab8d866e4087e166da8cafc09
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/ParrelSync/ParrelSync.meta
Executable file
8
Assets/ParrelSync/ParrelSync.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f5fec620d3bc9546a41a5b67cb9f8b6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/ParrelSync/ParrelSync/Editor.meta
Executable file
8
Assets/ParrelSync/ParrelSync/Editor.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a31ea7d0315594440839cdb0db6bc411
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock.meta
Executable file
8
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock.meta
Executable file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b14e706b1e7cb044b23837e8a70cad9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
22
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock/EditorQuit.cs
Executable file
22
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock/EditorQuit.cs
Executable file
@ -0,0 +1,22 @@
|
||||
using UnityEditor;
|
||||
namespace ParrelSync
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public class EditorQuit
|
||||
{
|
||||
/// <summary>
|
||||
/// Is editor being closed
|
||||
/// </summary>
|
||||
static public bool IsQuiting { get; private set; }
|
||||
static void Quit()
|
||||
{
|
||||
IsQuiting = true;
|
||||
}
|
||||
|
||||
static EditorQuit()
|
||||
{
|
||||
IsQuiting = false;
|
||||
EditorApplication.quitting += Quit;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock/EditorQuit.cs.meta
Executable file
11
Assets/ParrelSync/ParrelSync/Editor/AssetModBlock/EditorQuit.cs.meta
Executable file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf2888ff90706904abc2d851c3e59e00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,34 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
namespace ParrelSync
|
||||
{
|
||||
/// <summary>
|
||||
/// For preventing assets being modified from the clone instance.
|
||||
/// </summary>
|
||||
public class ParrelSyncAssetModificationProcessor : UnityEditor.AssetModificationProcessor
|
||||
{
|
||||
public static string[] OnWillSaveAssets(string[] paths)
|
||||
{
|
||||
if (ClonesManager.IsClone() && Preferences.AssetModPref.Value)
|
||||
{
|
||||
if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
ClonesManager.ProjectName + ": Asset modifications saving detected and blocked",
|
||||
"Asset modifications saving are blocked in the clone instance. \n\n" +
|
||||
"This is a clone of the original project. \n" +
|
||||
"Making changes to asset files via the clone editor is not recommended. \n" +
|
||||
"Please use the original editor window if you want to make changes to the project files.",
|
||||
"ok"
|
||||
);
|
||||
foreach (var path in paths)
|
||||
{
|
||||
Debug.Log("Attempting to save " + path + " are blocked.");
|
||||
}
|
||||
}
|
||||
return new string[0] { };
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 755e570bd21b39440a923056e60f1450
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user