chore(sdk): add SDK to project

This commit is contained in:
EmirHanMamak 2026-01-16 23:14:13 +03:00
parent 150f629313
commit 080b594bc6
6146 changed files with 2269361 additions and 9 deletions

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c76035eb5ca0b504e9909b48a3b96131
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5520642325e02e846ab317a30ab5ec46
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3c7b44f7ef02738429b2850a72b59b60
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: e2d7ea0845de4cf984265d2a444b7aa4
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.186/Google.IOSResolver.dll
- gvhp_targets-editor
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 0
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: fa49a85d4ba140a0ae21528ed12d174c
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.186/Google.JarResolver.dll
- gvhp_targets-editor
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: d8bb10c56a0147bc855a6296778e025e
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.186/Google.PackageManagerResolver.dll
- gvhp_targets-editor
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
fileFormatVersion: 2
guid: 5980a684c61d42fbb6b74e2eb3477016
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.186/Google.VersionHandlerImpl.dll
- gvhp_targets-editor
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Any:
enabled: 0
settings: {}
Editor:
enabled: 1
settings:
DefaultValueInitialized: true
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: aba4ee01c6d145f7bf2d944d892f709a
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/CHANGELOG.md
timeCreated: 1584567712
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
fileFormatVersion: 2
guid: f7632a50b10045458c53a5ddf7b6d238
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/Google.VersionHandler.dll
- gvhp_targets-editor
timeCreated: 1480838400
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
isPreloaded: 0
isOverridable: 0
platformData:
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,245 @@
Copyright (C) 2014 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================================================
This package uses MiniJSON
Copyright (c) 2013 Calvin Rien
Based on the JSON parser by Patrick van Bergen
http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
Simplified it so that it doesn't throw exceptions
and can be used in Unity iPhone with maximum code stripping.
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.

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: ae8b2bc8d1ac4ad48f0ab2b2e7ac75fb
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/LICENSE
timeCreated: 1584567712
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,903 @@
# External Dependency Manager for Unity
[![openupm](https://img.shields.io/npm/v/com.google.external-dependency-manager?label=openupm&registry_uri=https://package.openupm.com)](https://openupm.com/packages/com.google.external-dependency-manager/)
[![openupm](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.downloads&suffix=%2Fmonth&url=https%3A%2F%2Fpackage.openupm.com%2Fdownloads%2Fpoint%2Flast-month%2Fcom.google.external-dependency-manager)](https://openupm.com/packages/com.google.external-dependency-manager/)
## Overview
The External Dependency Manager for Unity (EDM4U) (formerly Play Services
Resolver/Jar Resolver) is intended to be used by any Unity package or user that
requires:
* Android specific libraries (e.g
[AARs](https://developer.android.com/studio/projects/android-library.html))
* iOS [CocoaPods](https://cocoapods.org/)
* Version management of transitive dependencies
* Management of Package Manager (PM) Registries
If you want to add and use iOS/Android dependencies directly in your project,
then you should to install EDM4U in your project.
If you are a package user and the plugin you are using depends on EDM4U, *and*
the package does not include EDM4U as a package dependency already, then you
should to install EDM4U in your project.
If you are a UPM package maintainer and your package requires EDM4U, then you
should add EDM4U as a
[package dependency](https://docs.unity3d.com/2019.3/Documentation/Manual/upm-dependencies.html)
in your package manifest (`package.json`):
```json
{
"dependencies": {
"com.google.external-dependency-manager": "1.2.178"
}
}
```
You should still install EDM4U to test out the package during development.
If you are a legacy `.unitypackage` package maintainer and your package requires
EDM4U, please ask the user to install EDM4U separately. You should install EDM4U
to test out the package during development.
Updated releases are available on
[GitHub](https://github.com/googlesamples/unity-jar-resolver)
## Requirements
The *Android Resolver* and *iOS Resolver* components of the plugin only work
with Unity version 4.6.8 or higher.
The *Version Handler* component only works with Unity 5.x or higher as it
depends upon the `PluginImporter` UnityEditor API.
The *Package Manager Resolver* component only works with Unity 2018.4 or above,
when [scoped registry](https://docs.unity3d.com/Manual/upm-scoped.html) support
was added to the Package Manager.
## Getting Started
Check out [troubleshooting](troubleshooting-faq.md) if you need help.
### Install via OpenUPM
EDM4U is available on
[OpenUPM](https://openupm.com/packages/com.google.external-dependency-manager/):
```shell
openupm add com.google.external-dependency-manager
```
### Install via git URL
1. Open Package Manager
2. Click on the + icon on the top left corner of the "Package Manager" screen
3. Click on "Install package from git url..."
4. Paste: https://github.com/googlesamples/unity-jar-resolver.git?path=upm
### Install via Google APIs for Unity
EDM4U is available both in UPM and legacy `.unitypackage` formats on
[Google APIs for Unity](https://developers.google.com/unity/archive#external_dependency_manager_for_unity).
You may install the UPM version (.tgz) as a
[local UPM package](https://docs.unity3d.com/Manual/upm-ui-local.html).
You can also install EDM4U in your project as a `.unitypackage`. This is not
recommended due to potential conflicts.
### Conflict Resolution
For historical reasons, a package maintainer may choose to embed EDM4U in their
package for ease of installation. This will create a conflict when you try to
install EDM4U with the steps above, or with another package with embedded EDM4U.
If your project imported a `.unitypackage` that has a copy of EDM4U embedded in
it, you may safely delete it from your Assets folder. If your project depends on
another UPM package with EDM4U, please reach out to the package maintainer and
ask them to replace it with a dependency to this package. In the meantime, you
can workaround the issue by copying the package to your Packages folder (to
create an
[embedded package](https://docs.unity3d.com/Manual/upm-concepts.html#Embedded))
and perform the steps yourself to avoid a dependency conflict.
### Config file
To start adding dependencies to your project, copy and rename the
[SampleDependencies.xml](https://github.com/googlesamples/unity-jar-resolver/blob/master/sample/Assets/ExternalDependencyManager/Editor/SampleDependencies.xml)
file into your plugin and add the dependencies your project requires.
The XML file needs to be under an `Editor` directory and match the name
`*Dependencies.xml`. For example, `MyPlugin/Editor/MyPluginDependencies.xml`.
## Usages
### Android Resolver
The Android Resolver copies specified dependencies from local or remote Maven
repositories into the Unity project when a user selects Android as the build
target in the Unity editor.
For example, to add the Google Play Games library
(`com.google.android.gms:play-services-games` package) at version `9.8.0` to the
set of a plugin's Android dependencies:
```xml
<dependencies>
<androidPackages>
<androidPackage spec="com.google.android.gms:play-services-games:9.8.0">
<androidSdkPackageIds>
<androidSdkPackageId>extra-google-m2repository</androidSdkPackageId>
</androidSdkPackageIds>
</androidPackage>
</androidPackages>
</dependencies>
```
The version specification (last component) supports:
* Specific versions e.g `9.8.0`
* Partial matches e.g `9.8.+` would match 9.8.0, 9.8.1 etc. choosing the most
recent version
* Latest version using `LATEST` or `+`. We do *not* recommend using this
unless you're 100% sure the library you depend upon will not break your
Unity plugin in future
The above example specifies the dependency as a component of the Android SDK
manager such that the Android SDK manager will be executed to install the
package if it's not found. If your Android dependency is located on Maven
central it's possible to specify the package simply using the `androidPackage`
element:
```xml
<dependencies>
<androidPackages>
<androidPackage spec="com.google.api-client:google-api-client-android:1.22.0" />
</androidPackages>
</dependencies>
```
#### Auto-resolution
By default the Android Resolver automatically monitors the dependencies you have
specified and the `Plugins/Android` folder of your Unity project. The resolution
process runs when the specified dependencies are not present in your project.
The *auto-resolution* process can be disabled via the `Assets > External
Dependency Manager > Android Resolver > Settings` menu.
Manual resolution can be performed using the following menu options:
* `Assets > External Dependency Manager > Android Resolver > Resolve`
* `Assets > External Dependency Manager > Android Resolver > Force Resolve`
#### Deleting libraries
Resolved packages are tracked via asset labels by the Android Resolver. They can
easily be deleted using the `Assets > External Dependency Manager > Android
Resolver > Delete Resolved Libraries` menu item.
#### Android Manifest Variable Processing
Some AAR files (for example play-services-measurement) contain variables that
are processed by the Android Gradle plugin. Unfortunately, Unity does not
perform the same processing when using Unity's Internal Build System, so the
Android Resolver plugin handles known cases of this variable substitution by
exploding the AAR into a folder and replacing `${applicationId}` with the
`bundleID`.
Disabling AAR explosion and therefore Android manifest processing can be done
via the `Assets > External Dependency Manager > Android Resolver > Settings`
menu. You may want to disable explosion of AARs if you're exporting a project to
be built with Gradle/Android Studio.
#### ABI Stripping
Some AAR files contain native libraries (.so files) for each ABI supported by
Android. Unfortunately, when targeting a single ABI (e.g x86), Unity does not
strip native libraries for unused ABIs. To strip unused ABIs, the Android
Resolver plugin explodes an AAR into a folder and removes unused ABIs to reduce
the built APK size. Furthermore, if native libraries are not stripped from an
APK (e.g you have a mix of Unity's x86 library and some armeabi-v7a libraries)
Android may attempt to load the wrong library for the current runtime ABI
completely breaking your plugin when targeting some architectures.
AAR explosion and therefore ABI stripping can be disabled via the `Assets >
External Dependency Manager > Android Resolver > Settings` menu. You may want to
disable explosion of AARs if you're exporting a project to be built with
Gradle/Android Studio.
#### Resolution Strategies
By default the Android Resolver will use Gradle to download dependencies prior
to integrating them into a Unity project. This works with Unity's internal build
system and Gradle/Android Studio project export.
It's possible to change the resolution strategy via the `Assets > External
Dependency Manager > Android Resolver > Settings` menu.
##### Download Artifacts with Gradle
Using the default resolution strategy, the Android resolver executes the
following operations:
- Remove the result of previous Android resolutions. E.g Delete all files and
directories labeled with "gpsr" under `Plugins/Android` from the project.
- Collect the set of Android dependencies (libraries) specified by a project's
`*Dependencies.xml` files.
- Run `download_artifacts.gradle` with Gradle to resolve conflicts and, if
successful, download the set of resolved Android libraries (AARs, JARs).
- Process each AAR/JAR so that it can be used with the currently selected
Unity build system (e.g Internal vs. Gradle, Export vs. No Export). This
involves patching each reference to `applicationId` in the
`AndroidManifest.xml` with the project's bundle ID. This means resolution
must be run again if the bundle ID has changed.
- Move the processed AARs to `Plugins/Android` so they will be included when
Unity invokes the Android build.
##### Integrate into mainTemplate.gradle
Unity 5.6 introduced support for customizing the `build.gradle` used to build
Unity projects with Gradle. When the *Patch mainTemplate.gradle* setting is
enabled, rather than downloading artifacts before the build, Android resolution
results in the execution of the following operations:
- Remove the result of previous Android resolutions. E.g Delete all files and
directories labeled with "gpsr" under `Plugins/Android` from the project and
remove sections delimited with `// Android Resolver * Start` and `// Android
Resolver * End` lines.
- Collect the set of Android dependencies (libraries) specified by a project's
`*Dependencies.xml` files.
- Rename any `.srcaar` files in the build to `.aar` and exclude them from
being included directly by Unity in the Android build as
`mainTemplate.gradle` will be patched to include them instead from their
local maven repositories.
- Inject the required Gradle repositories into `mainTemplate.gradle` at the
line matching the pattern `.*apply plugin:
'com\.android\.(application|library)'.*` or the section starting at the line
`// Android Resolver Repos Start`. If you want to control the injection
point in the file, the section delimited by the lines `// Android Resolver
Repos Start` and `// Android Resolver Repos End` should be placed in the
global scope before the `dependencies` section.
- Inject the required Android dependencies (libraries) into
`mainTemplate.gradle` at the line matching the pattern `***DEPS***` or the
section starting at the line `// Android Resolver Dependencies Start`. If
you want to control the injection point in the file, the section delimited
by the lines `// Android Resolver Dependencies Start` and `// Android
Resolver Dependencies End` should be placed in the `dependencies` section.
- Inject the packaging options logic, which excludes architecture specific
libraries based upon the selected build target, into `mainTemplate.gradle`
at the line matching the pattern `android +{` or the section starting at the
line `// Android Resolver Exclusions Start`. If you want to control the
injection point in the file, the section delimited by the lines `// Android
Resolver Exclusions Start` and `// Android Resolver Exclusions End` should
be placed in the global scope before the `android` section.
#### Dependency Tracking
The Android Resolver creates the
`ProjectSettings/AndroidResolverDependencies.xml` to quickly determine the set
of resolved dependencies in a project. This is used by the auto-resolution
process to only run the expensive resolution process when necessary.
#### Displaying Dependencies
It's possible to display the set of dependencies the Android Resolver would
download and process in your project via the `Assets > External Dependency
Manager > Android Resolver > Display Libraries` menu item.
### iOS Resolver
The iOS resolver component of this plugin manages
[CocoaPods](https://cocoapods.org/). A CocoaPods `Podfile` is generated and the
`pod` tool is executed as a post build process step to add dependencies to the
Xcode project exported by Unity.
Dependencies for iOS are added by referring to CocoaPods.
For example, to add the AdMob pod, version 7.0 or greater with bitcode enabled:
```xml
<dependencies>
<iosPods>
<iosPod name="Google-Mobile-Ads-SDK" version="~> 7.0" bitcodeEnabled="true"
minTargetSdk="6.0" addToAllTargets="false" />
</iosPods>
</dependencies>
```
#### Integration Strategies
The `CocoaPods` are either:
* Downloaded and injected into the Xcode project file directly, rather than
creating a separate xcworkspace. We call this `Xcode project` integration.
* If the Unity version supports opening a xcworkspace file, the `pod` tool is
used as intended to generate a xcworkspace which references the CocoaPods.
We call this `Xcode workspace` integration.
The resolution strategy can be changed via the `Assets > External Dependency
Manager > iOS Resolver > Settings` menu.
##### Appending text to generated Podfile
In order to modify the generated Podfile you can create a script like this:
```csharp
using System.IO;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;
public class PostProcessIOS : MonoBehaviour
{
// Must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and
// that it's added before "pod install" (50).
[PostProcessBuildAttribute(45)]
private static void PostProcessBuild_iOS(BuildTarget target, string buildPath)
{
if (target == BuildTarget.iOS)
{
using (StreamWriter sw = File.AppendText(buildPath + "/Podfile"))
{
// E.g. add an app extension
sw.WriteLine("\ntarget 'NSExtension' do\n pod 'Firebase/Messaging', '6.6.0'\nend");
}
}
}
}
```
### Package Manager Resolver
Adding registries to the
[Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) is a
manual process. The Package Manager Resolver (PMR) component of this plugin
makes it easy for plugin maintainers to distribute new PM registry servers and
easy for plugin users to manage PM registry servers.
#### Adding Registries
For example, to add a registry for plugins in the scope `com.coolstuff`:
```xml
<registries>
<registry name="Cool Stuff"
url="https://unityregistry.coolstuff.com"
termsOfService="https://coolstuff.com/unityregistry/terms"
privacyPolicy="https://coolstuff.com/unityregistry/privacy">
<scopes>
<scope>com.coolstuff</scope>
</scopes>
</registry>
</registries>
```
When PMR is loaded it will prompt the developer to add the registry to their
project if it isn't already present in the `Packages/manifest.json` file.
For more information, see Unity's documentation on
[scoped package registries](https://docs.unity3d.com/Manual/upm-scoped.html).
#### Managing Registries
It's possible to add and remove registries that are specified via PMR XML
configuration files via the following menu options:
* `Assets > External Dependency Manager > Package Manager Resolver > Add
Registries` will prompt the user with a window which allows them to add
registries discovered in the project to the Package Manager.
* `Assets > External Dependency Manager > Package Manager Resolver > Remove
Registries` will prompt the user with a window which allows them to remove
registries discovered in the project from the Package Manager.
* `Assets > External Dependency Manager > Package Manager Resolver > Modify
Registries` will prompt the user with a window which allows them to add or
remove registries discovered in the project.
#### Migration
PMR can migrate Version Handler packages installed in the `Assets` folder to PM
packages. This requires the plugins to implement the following:
* `.unitypackage` must include a Version Handler manifests that describes the
components of the plugin. If the plugin has no dependencies the manifest
would just include the files in the plugin.
* The PM package JSON provided by the registry must include a keyword (in the
`versions.VERSION.keyword` list) that maps the PM package to a Version
Handler package using the format `vh-name:VERSION_HANDLER_MANIFEST_NAME`
where `VERSION_HANDLER_MANIFEST_NAME` is the name of the manifest defined in
the `.unitypackage`. For more information see the description of the
`gvhp_manifestname` asset label in the [Version Handler](#version-handler)
section.
When using the `Assets > External Dependency Manager > Package Manager
Resolver > Migrate Packages` menu option, PMR then will:
* List all Version Handler manager packages in the project.
* Search all available packages in the PM registries and fetch keywords
associated with each package parsing the Version Handler manifest names for
each package.
* Map each installed Version Handler package to a PM package.
* Prompt the user to migrate the discovered packages.
* Perform package migration for all selected packages if the user clicks the
`Apply` button.
#### Configuration
PMR can be configured via the `Assets > External Dependency Manager > Package
Manager Resolver > Settings` menu option:
* `Add package registries` when enabled, when the plugin loads or registry
configuration files change, this will prompt the user to add registries that
are not present in the Package Manager.
* `Prompt to add package registries` will cause a developer to be prompted
with a window that will ask for confirmation before adding registries. When
this is disabled registries are added silently to the project.
* `Prompt to migrate packages` will cause a developer to be prompted with a
window that will ask for confirmation before migrating packages installed in
the `Assets` directory to PM packages.
* `Enable Analytics Reporting` when enabled, reports the use of the plugin to
the developers so they can make imrpovements.
* `Verbose logging` when enabled prints debug information to the console which
can be useful when filing bug reports.
### Version Handler
The Version Handler component of this plugin manages:
* Shared Unity plugin dependencies.
* Upgrading Unity plugins by cleaning up old files from previous versions.
* Uninstallation of plugins that are distributed with manifest files.
* Restoration of plugin assets to their original install locations if assets
are tagged with the `exportpath` label.
Since the Version Handler needs to modify Unity asset metadata (`.meta` files),
to enable/disable components, rename and delete asset files it does not work
with Package Manager installed packages. It's still possible to include EDM4U in
Package Manager packages, the Version Handler component simply won't do anything
to PM plugins in this case.
#### Using Version Handler Managed Plugins
If a plugin is imported at multiple different versions into a project, if the
Version Handler is enabled, it will automatically check all managed assets to
determine the set of assets that are out of date and assets that should be
removed. To disable automatic checking managed assets disable the `Enable
version management` option in the `Assets > External Dependency Manager >
Version Handler > Settings` menu.
If version management is disabled, it's possible to check managed assets
manually using the `Assets > External Dependency Manager > Version Handler >
Update` menu option.
##### Listing Managed Plugins
Plugins managed by the Version Handler, those that ship with manifest files, can
displayed using the `Assets > External Dependency Manager > Version Handler >
Display Managed Packages` menu option. The list of plugins are written to the
console window along with the set of files used by each plugin.
##### Uninstalling Managed Plugins
Plugins managed by the Version Handler, those that ship with manifest files, can
be removed using the `Assets > External Dependency Manager > Version Handler >
Uninstall Managed Packages` menu option. This operation will display a window
that allows a developer to select a set of plugins to remove which will remove
all files owned by each plugin excluding those that are in use by other
installed plugins.
Files managed by the Version Handler, those labeled with the `gvh` asset label,
can be checked to see whether anything needs to be upgraded, disabled or removed
using the `Assets > External Dependency Manager > Version Handler > Update` menu
option.
##### Restore Install Paths
Some developers move assets around in their project which can make it harder for
plugin maintainers to debug issues if this breaks Unity's
[special folders](https://docs.unity3d.com/Manual/SpecialFolders.html) rules. If
assets are labeled with their original install/export path (see
`gvhp_exportpath` below), Version Handler can restore assets to their original
locations when using the `Assets > External Dependency Manager > Version
Handler > Move Files To Install Locations` menu option.
##### Settings
Some behavior of the Version Handler can be configured via the `Assets >
External Dependency Manager > Version Handler > Settings` menu option.
* `Enable version management` controls whether the plugin should automatically
check asset versions and apply changes. If this is disabled the process
should be run manually when installing or upgrading managed plugins using
`Assets > External Dependency Manager > Version Handler > Update`.
* `Rename to canonical filenames` is a legacy option that will rename files to
remove version numbers and other labels from filenames.
* `Prompt for obsolete file deletion` enables the display of a window when
obsolete files are deleted allowing the developer to select which files to
delete and those to keep.
* `Allow disabling files via renaming` controls whether obsolete or disabled
files should be disabled by renaming them to `myfilename_DISABLED`. Renaming
to disable files is required in some scenarios where Unity doesn't support
removing files from the build via the PluginImporter.
* `Enable Analytics Reporting` enables/disables usage reporting to plugin
developers to improve the product.
* `Verbose logging` enables *very* noisy log output that is useful for
debugging while filing a bug report or building a new managed plugin.
* `Use project settings` saves settings for the plugin in the project rather
than system-wide.
#### Redistributing a Managed Plugin
The Version Handler employs a couple of methods for managing version selection,
upgrade and removal of plugins.
* Each plugin can ship with a manifest file that lists the files it includes.
This makes it possible for Version Handler to calculate the difference in
assets between the most recent release of a plugin and the previous release
installed in a project. If a files are removed the Version Handler will
prompt the user to clean up obsolete files.
* Plugins can ship using assets with unique names, unique GUIDs and version
number labels. Version numbers can be attached to assets using labels or
added to the filename (e.g `myfile.txt` would be `myfile_version-x.y.z.txt).
This allows the Version Handler to determine which set of files are the same
file at different versions, select the most recent version and prompt the
developer to clean up old versions.
Unity plugins can be managed by the Version Handler using the following steps:
1. Add the `gvh` asset label to each asset (file) you want Version Handler to
manage.
1. Add the `gvh_version-VERSION` label to each asset where `VERSION` is the
version of the plugin you're releasing (e.g 1.2.3).
1. Add the `gvhp_exportpath-PATH` label to each asset where `PATH` is the
export path of the file when the `.unitypackage` is created. This is used to
track files if they're moved around in a project by developers.
1. Optional: Add `gvh_targets-editor` label to each editor DLL in your plugin
and disable `editor` as a target platform for the DLL. The Version Handler
will enable the most recent version of this DLL when the plugin is imported.
1. Optional: If your plugin is included in other Unity plugins, you should add
the version number to each filename and change the GUID of each asset. This
allows multiple versions of your plugin to be imported into a Unity project,
with the Version Handler component activating only the most recent version.
1. Create a manifest text file named `MY_UNIQUE_PLUGIN_NAME_VERSION.txt` that
lists all the files in your plugin relative to the project root. Then add
the `gvh_manifest` label to the asset to indicate this file is a plugin
manifest.
1. Optional: Add a `gvhp_manifestname-NAME` label to your manifest file to
provide a human readable name for your package. If this isn't provided the
name of the manifest file will be used as the package name. NAME can match
the pattern `[0-9]+[a-zA-Z -]` where a leading integer will set the priority
of the name where `0` is the highest priority and preferably used as the
display name. The lowest value (i.e highest priority name) will be used as
the display name and all other specified names will be aliases of the
display name. Aliases can refer to previous names of the package allowing
renaming across published versions.
1. Redistribute EDM4U Unity plugin with your plugin. See the
[Plugin Redistribution](#plugin-redistribution) section for details.
If you follow these steps:
* When users import a newer version of your plugin, files referenced by the
older version's manifest are cleaned up.
* The latest version of the plugin will be selected when users import multiple
packages that include your plugin, assuming the steps in
[Plugin Redistribution](#plugin-redistribution) are followed.
## Background
Many Unity plugins have dependencies upon Android specific libraries, iOS
CocoaPods, and sometimes have transitive dependencies upon other Unity plugins.
This causes the following problems:
* Integrating platform specific (e.g Android and iOS) libraries within a Unity
project can be complex and a burden on a Unity plugin maintainer.
* The process of resolving conflicting dependencies on platform specific
libraries is pushed to the developer attempting to use a Unity plugin. The
developer trying to use your plugin is very likely to give up when faced
with Android or iOS specific build errors.
* The process of resolving conflicting Unity plugins (due to shared Unity
plugin components) is pushed to the developer attempting to use your Unity
plugin. In an effort to resolve conflicts, the developer will very likely
attempt to resolve problems by deleting random files in your plugin, report
bugs when that doesn't work and finally give up.
EDM4U provides solutions for each of these problems.
### Android Dependency Management
The *Android Resolver* component of this plugin will download and integrate
Android library dependencies and handle any conflicts between plugins that share
the same dependencies.
Without the Android Resolver, typically Unity plugins bundle their AAR and JAR
dependencies, e.g. a Unity plugin `SomePlugin` that requires the Google Play
Games Android library would redistribute the library and its transitive
dependencies in the folder `SomePlugin/Android/`. When a user imports
`SomeOtherPlugin` that includes the same libraries (potentially at different
versions) in `SomeOtherPlugin/Android/`, the developer using `SomePlugin` and
`SomeOtherPlugin` will see an error when building for Android that can be hard
to interpret.
Using the Android Resolver to manage Android library dependencies:
* Solves Android library conflicts between plugins.
* Handles all of the various processing steps required to use Android
libraries (AARs, JARs) in Unity 4.x and above projects. Almost all versions
of Unity have - at best - partial support for AARs.
* (Experimental) Supports minification of included Java components without
exporting a project.
### iOS Dependency Management
The *iOS Resolver* component of this plugin integrates with
[CocoaPods](https://cocoapods.org/) to download and integrate iOS libraries and
frameworks into the Xcode project Unity generates when building for iOS. Using
CocoaPods allows multiple plugins to utilize shared components without forcing
developers to fix either duplicate or incompatible versions of libraries
included through multiple Unity plugins in their project.
### Package Manager Registry Setup
The [Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) makes
use of [NPM](https://www.npmjs.com/) registry servers for package hosting and
provides ways to discover, install, upgrade and uninstall packages. This makes
it easier for developers to manage plugins within their projects.
However, installing additional package registries requires a few manual steps
that can potentially be error prone. The *Package Manager Resolver* component of
this plugin integrates with [PM](https://docs.unity3d.com/Manual/Packages.html)
to provide a way to auto-install PM package registries when a `.unitypackage` is
installed which allows plugin maintainers to ship a `.unitypackage` that can
provide access to their own PM registry server to make it easier for developers
to manage their plugins.
### Unity Plugin Version Management
Finally, the *Version Handler* component of this plugin simplifies the process
of managing transitive dependencies of Unity plugins and each plugin's upgrade
process.
For example, without the Version Handler plugin, if:
* Unity plugin `SomePlugin` includes `EDM4U` plugin at version 1.1.
* Unity plugin `SomeOtherPlugin` includes `EDM4U` plugin at version 1.2.
The version of `EDM4U` included in the developer's project depends upon the
order the developer imports `SomePlugin` or `SomeOtherPlugin`.
This results in:
* `EDM4U` at version 1.2, if `SomePlugin` is imported then `SomeOtherPlugin`
is imported.
* `EDM4U` at version 1.1, if `SomeOtherPlugin` is imported then `SomePlugin`
is imported.
The Version Handler solves the problem of managing transitive dependencies by:
* Specifying a set of packaging requirements that enable a plugin at different
versions to be imported into a Unity project.
* Providing activation logic that selects the latest version of a plugin
within a project.
When using the Version Handler to manage `EDM4U` included in `SomePlugin` and
`SomeOtherPlugin`, from the prior example, version 1.2 will always be the
version activated in a developer's Unity project.
Plugin creators are encouraged to adopt this library to ease integration for
their customers. For more information about integrating EDM4U into your own
plugin, see the [Plugin Redistribution](#plugin-redistribution) section of this
document.
## Analytics
The External Dependency Manager for Unity plugin by default logs usage to Google
Analytics. The purpose of the logging is to quantitatively measure the usage of
functionality, to gather reports on integration failures and to inform future
improvements to the developer experience of the External Dependency Manager
plugin. Note that the analytics collected are limited to the scope of the EDM4U
plugins usage.
For details of what is logged, please refer to the usage of
`EditorMeasurement.Report()` in the source code.
## Plugin Redistribution
If you are a package maintainer and your package depends on EDM4U, it is highly
recommended to use the UPM format and add EDM4U as a dependency. If you must
include it in your `.unitypackage`, redistributing `EDM4U` inside your own
plugin might ease the integration process for your users.
If you wish to redistribute `EDM4U` inside your plugin, you **must** follow
these steps when importing the `external-dependency-manager-*.unitypackage`, and
when exporting your own plugin package:
1. Import the `external-dependency-manager-*.unitypackage` into your plugin
project by
[running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
ensuring that you add the `-gvh_disable` option.
1. Export your plugin by
[running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
ensuring that you:
- Include the contents of the `Assets/PlayServicesResolver` and
`Assets/ExternalDependencyManager` directory.
- Add the `-gvh_disable` option.
You **must** specify the `-gvh_disable` option in order for the Version Handler
to work correctly!
For example, the following command will import the
`external-dependency-manager-1.2.46.0.unitypackage` into the project
`MyPluginProject` and export the entire Assets folder to
`MyPlugin.unitypackage`:
```shell
Unity -gvh_disable \
-batchmode \
-importPackage external-dependency-manager-1.2.46.0.unitypackage \
-projectPath MyPluginProject \
-exportPackage Assets MyPlugin.unitypackage \
-quit
```
### Background
The *Version Handler* component relies upon deferring the load of editor DLLs so
that it can run first and determine the latest version of a plugin component to
activate. The build of `EDM4U` plugin has Unity asset metadata that is
configured so that the editor components are not initially enabled when it's
imported into a Unity project. To maintain this configuration when importing the
`external-dependency-manager.unitypackage` into a Unity plugin project, you
*must* specify the command line option `-gvh_disable` which will prevent the
Version Handler component from running and changing the Unity asset metadata.
## Building from Source
To build this plugin from source you need the following tools installed: * Unity
2021 and below (with iOS and Android modules installed) * Java 11
You can build the plugin by running the following from your shell (Linux / OSX):
```shell
./gradlew build
```
or Windows:
```shell
./gradlew.bat build
```
If Java 11 is not your default Java command, add
`-Dorg.gradle.java.home=<PATH_TO_JAVA_HOME>` to the command above.
## Testing
You can run the tests by running the following from your shell (Linux / OSX):
```shell
./gradlew test
```
or Windows:
```shell
./gradlew.bat test
```
The following properties can be set to narrow down the tests to run or change
the test run behavior.
* `INTERACTIVE_MODE_TESTS_ENABLED` - Default to `1`. Set to `1` to enable
interactive mode tests, which requires GPU on the machine. Otherwise, only
run tests in the batch mode.
* `INCLUDE_TEST_TYPES` - Default to empty string, which means to include every
type of the test. To narrow down the types of test to run, set this
properties with a list of case-insensitive type strings separated by comma.
For instance, `-PINCLUDE_TEST_TYPES="Python,NUnit"` means to include only
Python tests and NUnit tests. See `TestTypeEnum` in `build.gradle` for
available options.
* `EXCLUDE_TEST_TYPES` - Default to empty string, which means to exclude none.
To add types of tests to exclude, set this properties with a list of
case-insensitive type strings separated by comma. For instance,
`-PEXCLUDE_TEST_TYPES="Python,NUnit"` means to exclude Python tests and
NUnit tests. See `TestTypeEnum` in `build.gradle` for available options.
* `INCLUDE_TEST_MODULES` - Default to empty string, which means to include the
tests for every modules. To narrow down modules to test, set this properties
with a list of case-insensitive module strings separated by comma. For
instance, `-PINCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests
for tools and Android Resolver only. See `TestModuleEnum` in `build.gradle`
for available options.
* `EXCLUDE_TEST_MODULES` - Default to empty string, which means to exclude
none. To add modules to exclude, set this properties with a list of
case-insensitive module strings separated by comma. For instance,
`-PEXCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests for any
modules other than tools and Android Resolver. See `TestModuleEnum` in
`build.gradle` for available options.
* `EXCLUDE_TESTS` - Default to empty string, which means to exclude none. To
add tests to exclude, set this properties with a list of case-insensitive
test names separated by comma. For instance,
`-PEXCLUDE_TESTS="testGenGuids,testDownloadArtifacts"` means to run tests
except the tests with name of `testGenGuids` and `testDownloadArtifacts`.
* `CONTINUE_ON_FAIL_FOR_TESTS_ENABLED` - Default to `1`. Set to `1` to
continue running the next test when the current one fails. Otherwise, the
build script stops whenever any test fails.
For instance, by running the following command, it only runs the Unity
integration tests that does not requires GPU, but exclude tests for Android
Resolver module and iOS Resolver module.
```shell
./gradlew test \
-PINTERACTIVE_MODE_TESTS_ENABLED=0 \
-PINCLUDE_TEST_TYPES="Integration" \
-PEXCLUDE_TEST_MODULES="AndroidResolver,iOSResolver"
```
## Releasing
Each time a new build of this plugin is checked into the source tree you need to
do the following:
* Bump the plugin version variable `pluginVersion` in `build.gradle`
* Update `CHANGELOG.md` with the new version number and changes included in
the release.
* Build the release using `./gradlew release` which performs the following:
* Updates `external-dependency-manager-*.unitypackage`
* Copies the unpacked plugin to the `exploded` directory.
* Updates template metadata files in the `plugin` directory. The GUIDs of
all asset metadata is modified due to the version number change. Each
file within the plugin is versioned to allow multiple versions of the
plugin to be imported into a Unity project which allows the most recent
version to be activated by the Version Handler component.
* Create release commit using `./gradlew gitCreateReleaseCommit` which
performs `git commit -a -m "description from CHANGELOG.md"`
* Once the release commit is merge, tag the release using `./gradlew
gitTagRelease` which performs the following:
* `git tag -a pluginVersion -m "version RELEASE"` to tag the release.
* Update tags on remote branch using `git push --tag REMOTE HEAD:master`

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 77919e84cef8419ab4b725fc16e83d52
labels:
- gvh
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/README.md
timeCreated: 1584567712
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
Assets/ExternalDependencyManager/Editor/1.2.186/Google.IOSResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.186/Google.IOSResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.186/Google.JarResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.186/Google.JarResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.186/Google.PackageManagerResolver.dll
Assets/ExternalDependencyManager/Editor/1.2.186/Google.PackageManagerResolver.pdb
Assets/ExternalDependencyManager/Editor/1.2.186/Google.VersionHandlerImpl.dll
Assets/ExternalDependencyManager/Editor/1.2.186/Google.VersionHandlerImpl.pdb
Assets/ExternalDependencyManager/Editor/CHANGELOG.md
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll
Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb
Assets/ExternalDependencyManager/Editor/LICENSE
Assets/ExternalDependencyManager/Editor/README.md

View file

@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: c9a3138961c74d99b7046b783112fceb
labels:
- gvh
- gvh_manifest
- gvh_version-1.2.186
- gvhp_exportpath-ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.186_manifest.txt
- gvhp_manifestname-0External Dependency Manager
- gvhp_manifestname-play-services-resolver
timeCreated: 1474401009
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

8
Assets/ShiroginSDK.meta Normal file
View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4bc1c52626077d846b784d8d9b511534
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,90 @@
# 🧾 ShiroginSDK Changelog
Tüm önemli değişiklikler bu dosyada tutulacaktır.
Her sürüm, SDK mimarisine eklenen yeni sistemleri ve servisleri içerir.
---
## [1.1.1] - 2025-10-23
### ✨ Eklendi
- 🔧 **Core Düzeltmeler**
- `AnalyticsEventListener` AnalyticsEventListenerService'e dönüştürüldü ve analiz event bug düzeltildi..
---
## [1.1.0] - 2025-10-15
### ✨ Eklendi
- 📦 **Asset Registry Manager & Setup Wizard**
- **Akıllı Kurulum Sihirbazı:** SDK ilk yüklendiğinde açılan ve projenin eksiklerini (Core Missing) analiz eden bir sihirbaz eklendi.
- **Queue (Kuyruk) Sistemi:** "Download & Install All Core" butonu ile birden fazla paket (IAP, Ads, Analytics vb.) sırasıyla indirilir ve kurulur.
- **UPM Entegrasyonu:** Unity IAP, TextMeshPro ve Android Logcat gibi Unity paketleri artık Asset Manager üzerinden otomatik (`Client.Add`) komutuyla kuruluyor.
- **Versiyon Kontrolü:** Yüklü paketlerin versiyonları denetlenir. Eğer farklı bir versiyon yüklüyse arayüzde "Update" veya "Switch" seçenekleri belirir.
- **Cache Yönetimi:** İndirilen `.unitypackage` dosyaları cache'de tutulur, tekrar indirme yapmadan "Import (Cached)" özelliği ile hızlı kurulum sağlanır.
- **Görsel Durum Bildirimi:** Paketlerin durumu (Yüklü, İndirilmiş, Eksik) renkli kutular (Yeşil, Mavi, Kırmızı, Gri) ile görselleştirildi.
- 📚 **Entegre Dokümantasyon Sistemi**
- Asset Manager üzerinden paketlerin dokümantasyonları (Markdown) direkt olarak projeye indirilebiliyor.
- Web butonu ile ilgili paketin online dokümantasyonuna yönlendirme eklendi.
- 🔧 **Core İyileştirmeler**
- `AssetRegistryEditor` UPM isteklerini asenkron takip edecek şekilde güncellendi.
- Setup Wizard modunda, temel paketlerin (Essential Packages) versiyonları kilitlendi, böylece uyumsuz versiyon kurulumu engellendi.
- `DOTween``DOTweenPro`'ya dönüştürüldü.
---
## [1.0.0] - 2025-10-01
### ✨ Eklendi
- 🎯 **IAP Service (In-App Purchasing)**
- `IAPService` sınıfı eklendi. Unity IAP üzerinden satın alma, ödül verme ve restore işlemleri destekleniyor.
- `StoreRepository`, `StoreSection`, `StoreItem` ScriptableObject yapıları oluşturuldu.
- `RewardDefinition` ile oyun içi ödüller merkezi olarak tanımlanabiliyor.
- `UIStoreItem_*` sınıfları (Consumable, NonConsumable, Bundle, Rewarded, Chest) ile dinamik mağaza UI sistemi kuruldu.
- `EconomyData` ve `StoreData` entegrasyonu sağlandı.
- 💳 **Economy & Data System**
- `BaseData` mimarisi kuruldu. Veriler JSON tabanlı kayıt ve yükleme destekliyor.
- `EconomyData`, `InventoryData`, `StatsData` yapılandırıldı.
- `DataService` merkezi kayıt erişimi sağlıyor.
- Oyunlara özel veri kullanımı seçilebilir hale getirildi (örnek: sadece Gold/Gems/Tickets tutma).
- 📊 **Analytics System (Firebase • AppsFlyer • Facebook)**
- `AnalyticsService` merkezi çoklu SDK entegrasyonu eklendi.
- `AnalyticsEventMapper` ile oyun içi eventler (LevelUp, Purchase vb.) otomatik olarak ilgili SDK'nın formatına dönüştürülüyor.
- 🔥 **Firebase Service**
- `FirebaseService` eklendi (Analytics + Remote Config).
- Remote Config değerleri `RemoteConfigDefinition` ScriptableObject üzerinden tanımlanabiliyor.
- İnternet yoksa son başarılı config değerlerini kullanan Cache sistemi eklendi.
- 📱 **Facebook Service**
- `FacebookService` eklendi (Init, Login, LogEvent).
- iOS/Android için otomatik App Activation desteği.
- 🎯 **Ads Service (AppLovin MAX)**
- `AdsService` eklendi.
- Banner, Interstitial ve Rewarded reklamlar için event-based yönetim (Load, Show, Fail, Reward).
- `AdAnalyticsEvent` ile reklam gelirleri analitik servisine otomatik raporlanıyor.
- 📺 **UI & Popup System**
- `PopupService` ve `PopupBase` mimarisi kuruldu.
- Popuplar, öncelik sırasına (Priority Queue) göre gösteriliyor.
- `ThemeService` ile UI görselleri dinamik olarak değiştirilebiliyor (Örn: Yılbaşı teması).
- CanvasGroup tabanlı Fade-In/Out ve şeffaflık animasyonu desteği getirildi.
- 🎨 **Theme Service**
- `ThemeService` eklendi.
- Her oyun için özel tema setleri (örnek: Fire, Snow, Dungeon, etc.) tanımlanabiliyor.
- UI elementleri tema bazlı otomatik güncellenebiliyor.
- 🧰 **Editor Tools**
- `SDKConfigurator` ve `PlayerPrefs Editor` arayüzleri oluşturuldu.
- SDK modülleri Unity Editor üzerinden kolayca açılıp kapatılabiliyor.
- Otomatik versiyonlama ve konfigürasyon denetimi sağlandı.
- ⚙️ **Core Improvements**
- `SDKInitializer` eklendi — tüm servisleri sırasıyla başlatıyor.
- Servislerin `DontDestroyOnLoad` yaşam döngüsü standardize edildi.
- Konsol logları ShiroginSDK etiketiyle birleştirildi.

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6db0fc2e192287047b11c849d60c1a6a
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 927923e41e42e2f4287b9fab1dda50e7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 00f3ad63bef944008dba51a3801a9542
timeCreated: 1760703697

View file

@ -0,0 +1,191 @@
# 📊 ShiroginSDK Analytics System (v1.0.0 Updated)
**Firebase • AppsFlyer • Facebook**
Tam Otomatik Event Forwarding + Çok Katmanlı Servis Mimarisi
Bu doküman, **ShiroginSDK v1.0.0** sürümüyle birlikte gelen yeni analiz servis mimarisini açıklar.
Yeni sistem, `ShiroginServiceRegistry` üzerinden otomatik başlatılır ve **Firebase**, **AppsFlyer** ve **Facebook** SDKlarına merkezi olarak bağlanır.
Tüm loglama işlemleri event tabanlıdır — geliştirici sadece `EventService.Invoke()` çağrısı yapar.
---
## 🚀 1. Genel Bakış
Yeni analiz sistemi üç ana katmandan oluşur:
1. **EventService (Core)**
→ Oyun içi tüm `ShiroginEvents`leri yönetir.
`AnalyticsService`, `FirebaseService`, `AppsFlyerService` gibi servislerle otomatik iletişim kurar.
2. **AnalyticsService (Coordinator)**
→ Eventleri dinler, `AnalyticsEventMapper` üzerinden uygun SDK formatına dönüştürür.
→ Aktif SDKlara log iletir (`FirebaseService`, `AppsFlyerService`, `FacebookService`).
3. **Platform Servisleri (Backends)**
→ Her SDKya özel bağlantı, initialization ve log gönderim süreçlerini yönetir.
> Tüm servisler `ShiroginServiceRegistry.InitializeAll()` çağrısı ile otomatik olarak başlatılır.
---
## 🧩 2. Yeni Servis Mimarisi
### 🔹 Başlatma (Yeni Sistem)
```csharp
ShiroginServiceRegistry.InitializeAll();
```
Bu çağrı aşağıdaki servisleri sırayla oluşturur:
* **EventService** → Event iletişimi
* **DataService** → Kalıcı veri yönetimi
* **FirebaseService** → Firebase Analytics + RemoteConfig
* **AppsFlyerService** → AppsFlyer SDK
* **FacebookService** → Meta SDK
* **AnalyticsService** → Otomatik event yönlendirme
---
### 🔹 Örnek Event Akışı
```csharp
EventService.Invoke(new ShiroginEvents.PurchaseCompletedEvent(product));
```
Bu çağrıdan sonra sistem sırasıyla:
1. `EventService` eventi yakalar.
2. `AnalyticsService` event tipini analiz eder.
3. `AnalyticsEventMapper` parametreleri uygun SDK formatına çevirir.
4. `FirebaseService`, `AppsFlyerService` ve `FacebookService` log gönderir.
Tam akış:
```
[Game Event]
EventService
AnalyticsService
Firebase / AppsFlyer / Facebook
```
---
## 🔥 3. Servis Güncellemeleri
### 🔸 AnalyticsService
Artık doğrudan `ServiceBase`den türetilir ve `ServiceLocator` üzerinden erişilir.
Kendi `Initialize()` metodunda bağlı SDKları otomatik bulur.
```csharp
var analytics = ServiceLocator.Get<IAnalyticsService>();
analytics.LogEvent(AnalyticsEventType.Purchase, parameters);
```
### 🔸 FirebaseService
Yeni sistemde hem **RemoteConfig** hem **Analytics** aynı instance üzerinden yönetilir.
Başarılı başlatma sonrası artık `FirebaseReadyEvent` yayınlanır.
```csharp
_eventService.Invoke(new ShiroginEvents.FirebaseReadyEvent());
```
### 🔸 AppsFlyerService
Tamamen `ServiceBase` yapısına geçti.
Başlatma işlemi otomatik olarak `ShiroginServiceRegistry` içinde gerçekleşir.
### 🔸 EventService
Artık SDKnin ana “event bus” yapısıdır.
Tüm servisler (Firebase, Ads, IAP, RemoteConfig) kendi eventlerini bu sistem üzerinden yayınlar.
Oyun içi tüm veri akışı **tek bir hat üzerinden** ilerler.
---
## 🧠 4. Otomatik Event Forwarding (Yeni)
Yeni versiyonda, `AnalyticsEventListener` artık doğrudan kullanılmaz.
Onun yerine `AnalyticsService` kendisi `EventService`e subscribe olur ve dinleme işlemini merkezi hale getirir.
Örnek:
```csharp
EventService.Invoke(new ShiroginEvents.AdWatchedEvent("rewarded", true));
```
→ Otomatik olarak üç platforma log gönderilir.
---
## 🧰 5. SDKConfig Entegrasyonu
Yeni `SDKConfig` sınıfı üzerinden tüm analitik modüller yönetilir:
```csharp
public bool enableFirebaseAnalytics = true;
public bool enableAppsFlyerAnalytics = true;
public bool enableFacebookAnalytics = true;
```
Bu alanlar `AnalyticsService.Initialize()` aşamasında kontrol edilir, aktif olmayan SDKlara log gönderimi yapılmaz.
---
## 🧾 6. Örnek Kullanım: Satın Alma Eventi
```csharp
EventService.Invoke(new ShiroginEvents.PurchaseCompletedEvent("com.game.removeads", 4.99f, "USD"));
```
Arka planda:
* `AnalyticsService``Purchase` eventini tanır.
* `AnalyticsEventMapper` → Parametreleri dönüştürür.
* `FirebaseService`, `AppsFlyerService`, `FacebookService` → Aynı anda log gönderir.
* `FirebaseReadyEvent` ve `PurchaseLoggedEvent` tetiklenir.
---
## ⚙️ 7. Güncellenen Özellikler
✅ Yeni Service Registry sistemi (otomatik init & dispose)
✅ EventService tabanlı otomatik log forwarding
✅ Firebase + AppsFlyer + Facebook tam entegrasyon
✅ Async initialization + error fallback
✅ Tek config kaynağı (`SDKConfig`)
✅ Event bazlı hata loglama (örneğin `NoConnectionEvent`, `RewardedAdUnavailableEvent`)
---
## 📘 8. Uyumlu Modüller
Yeni sistem doğrudan şu modüllerle entegre çalışır:
* `IAPService` → Satın alma logları
* `AdsService` → Ad impression + revenue logları
* `PopupService` → UI etkileşimleri
* `RemoteConfigService` → Firebase senkronizasyon eventleri
---
## 🧠 9. Özet
ShiroginSDK Analytics System v1.0.0 artık tamamen **service-driven ve event-driven** mimari üzerinde çalışır.
Tüm SDKlar ortak bir event hattını kullanır, initialization sırası `ShiroginServiceRegistry` tarafından yönetilir.
Bu yapı hem oyun entegrasyonunu sadeleştirir hem de log takibini merkezi hale getirir.
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.Analytics
---

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec1723ff3a314f6c81983487e251813e
timeCreated: 1759957415

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 203a4f02da8b4acda8478c9a876451c4
timeCreated: 1760703707

View file

@ -0,0 +1,78 @@
# 📘 Shirogin SDK Facebook Entegrasyonu
**Güncelleme Tarihi:** 08.10.2025
Bu doküman, Shirogin SDK kullanan Unity projelerinde **Facebook SDK entegrasyonunu** adım adım nasıl yapacağınızııklar. Kurulum sürecinde dikkat edilmesi gereken noktalar, hata önleme ipuçları ve gerekli bağlantılar da dahildir.
---
## ⚙️ 1. Facebook SDK Kurulumu
ShiroginSDK, üçüncü parti entegrasyonlar için hazır klasör yapısı ile gelir. Eğer proje içinde Facebook SDK bulunmuyorsa aşağıdaki adımları izleyin:
1. [Facebook SDK for Unity](https://github.com/facebook/facebook-sdk-for-unity/releases) sayfasına gidin.
2. En güncel sürümü indirelim.
3. **Package Manager üzerinden kurulum yaparken. Packages'ın eklemeyin çünkü projeyi bozuyor**
4. Kurulum esnasında çıkan pencerede **“IAPWarper”** gibi hata veren bileşenlerin işaretini kaldırın veya bu kodları projeden kaldırın.
![Facebook Kurulum Adımı](https://i.imgur.com/lwoHK3t.png)
---
## 🛠️ 2. Facebook Ayarlarını Yapılandırma
Kurulum tamamlandıktan sonra, Facebook SDK ayarlarını Unity üzerinden yapılandırın:
1. Menüden şu yolu izleyin:
**Facebook → Edit Settings**
![Facebook Ayarları](https://i.imgur.com/iRR2dFH.png)
---
## 🌐 3. Facebook Geliştirici Paneli Ayarları
Facebook uygulama bilgilerinizi almak için [Facebook Developers](https://developers.facebook.com/apps/) adresine gidin:
1. Oyununuzu veya uygulamanızı seçin.
2. Aşağıdaki bilgileri bulun:
- **App ID:** Uygulamanızın benzersiz kimliği
![App ID](https://i.imgur.com/lDlA5Um.png)
- **Client Token:** Kimlik doğrulama için gerekli
![Client Token](https://i.imgur.com/iWzdXrg.png)
---
## 🧰 4. Unity Üzerinde Son Ayarların Yapılması
Facebook panelinden aldığınız bilgileri Unityde açtığınız **Edit Settings** ekranına girin:
- **App Name** → Uygulama adınız
- **App ID** → Uygulama kimliğiniz
- **Client Token** → Kimlik doğrulama anahtarınız
Ardından **Regenerate Android Manifest** butonuna tıklayarak gerekli Android izinlerini ve yapılandırmaları otomatik oluşturun.
![Manifest Yenileme](https://i.imgur.com/45EBCGH.png)
---
## ✅ Sonuç
Bu adımlar tamamlandığında Shirogin SDK projeniz, Facebook SDK ile tamamen entegre şekilde çalışmaya hazır hale gelir.
Artık Facebook ile giriş, analiz olayları takibi ve sosyal özellikleri sorunsuzca kullanabilirsiniz.
---
📌 **Not:** Entegrasyon sırasında hatalarla karşılaşırsanız `Console` sekmesini kontrol edin. Eksik izinler veya hatalı kimlik bilgileri çoğu zaman burada detaylı şekilde listelenir.
---
**Hazırlayan:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.Facebook
---

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7b7c45874a7f8d94abfaf69611b88084
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 471745bf357b458ca01b2080f4d14094
timeCreated: 1760703774

View file

@ -0,0 +1,213 @@
# 🛒 ShiroginSDK IAP System (v1.0.0)
**Unity In-App Purchase Entegrasyonu + Oyun İçi Ekonomi + Ödül + Event-Driven Mimari**
Bu doküman, **ShiroginSDK v1.0.0** sürümüyle güncellenen IAP (In-App Purchase) altyapısınııklar.
Yeni sistem, **ShiroginServiceRegistry** üzerinden başlatılan **modüler servis yapısına** sahiptir.
Tüm satın alma işlemleri, **event tabanlı** olarak SDK genelindeki diğer servislerle otomatik senkronize edilir.
---
## 🚀 1. Genel Bakış
ShiroginSDK IAP sistemi:
* ✅ `ServiceBase` tabanlı, modüler ve bağımsız servis yapısı
* ✅ `EventService` ile tam event forwarding desteği
* ✅ Unity IAP, oyun içi ekonomi, sandık, bundle ve reklamla ödül sistemleri
* ✅ Otomatik `AnalyticsService` & `PopupService` entegrasyonu
* ✅ Tek konfigürasyon noktası: `SDKConfig.storeRepository`
---
## 🧩 2. Servis Mimarisi
### 🔹 IAPService (Yeni Sistem)
`IAPService`, artık `ServiceBase`ten türetilen bir servis olarak `ShiroginServiceRegistry` tarafından otomatik olarak başlatılır.
Kendi bağımlılıklarını `ServiceLocator` aracılığıyla çözer.
**Başlatma:**
```csharp
ShiroginServiceRegistry.InitializeAll();
````
Bu çağrı, IAPService dahil tüm servisleri (`EventService`, `DataService`, `FirebaseService`, `AnalyticsService`) otomatik olarak oluşturur.
**IAPService Görevleri:**
* Unity IAP sistemini başlatır (`UnityPurchasing.Initialize()`).
* Satın alma işlemlerini yönetir.
* Başarılı işlemlerde ödül verir (`StoreItem.GiveReward()`).
* EventService üzerinden satın alma eventlerini yayınlar.
**Kilit Eventler:**
```csharp
_eventService.Invoke(new ShiroginEvents.PurchaseCompletedEvent(product, 1)); // Analytics
_eventService.Invoke(new ShiroginEvents.Shop_UIStoreItem_Rewarded(item.GetRewards())); // Popup
```
---
### 🔹 DataService Entegrasyonu (v1.0.0 Güncel)
Yeni sistemde `DataService`, `ServiceBase`ten türetilen bağımsız bir servistir.
`ServiceLocator` aracılığıyla erişilir ve oyun boyunca tüm veri nesnelerini (`RuntimeData`, `EconomyData`, `StoreData`, `InventoryData` vb.) yönetir.
```csharp
var dataService = ServiceLocator.Get<IDataService>();
var storeData = dataService.Get<StoreData>();
storeData.MarkPurchased(productId, 1, item.category);
var runtime = dataService.Get<RuntimeData>();
runtime.UpdatePlaySessionCount();
```
Artık **`DataService.Get<T>()`** gibi statik çağrılar yerine, `ServiceLocator` üzerinden interface tabanlı erişim kullanılmalıdır.
Bu sayede her servis, kendi yaşam döngüsünde `OnInitialize()` içinde `IDataService`i çözer ve paylaşılan statei korur.
> 🔹 `IDataService``Get<T>()`, `Save<T>()`, `Delete<T>()` gibi tip güvenli fonksiyonlar sağlar.
> 🔹 `DataService` runtime'da `DontDestroyOnLoad` olarak yaşar.
> 🔹 Oyun verileri JSON tabanlı olarak `Application.persistentDataPath || PlayerPrefs` altında tutulur.
---
### 🔹 EventService Entegrasyonu
Her önemli işlem event tabanlı çalışır.
IAPService, `EventService` üzerinden ShiroginEvents yayınlayarak SDK içindeki diğer sistemlere sinyal gönderir.
Örneğin:
* `PurchaseCompletedEvent``AnalyticsService` tarafından loglanır
* `Shop_UIStoreItem_Rewarded``PopupService` tarafından UI bildirimi gösterilir
---
## 💳 3. IAPService API Özeti
| Metod | Açıklama |
| ------------------------------------ | ------------------------------------------ |
| `Buy(StoreItem item)` | Belirtilen ürünü satın alır. |
| `Buy(string productId)` | Product ID ile satın alma başlatır. |
| `RestorePurchases()` | Kalıcı ürünleri geri yükler (iOS/Android). |
| `GetLocalizedPriceString(productId)` | Cihazın mağaza diline uygun fiyat döner. |
---
## 🧠 4. Akış Diyagramı
```
[UIStoreItem_Consumable.OnPurchaseButton]
[IAPService.Buy()]
Unity IAP Purchase Flow
[IAPService.ProcessPurchase()]
[StoreItem.GiveReward()]
[EventService.Invoke(ShiroginEvents.PurchaseCompletedEvent)]
[AnalyticsService + PopupService]
```
Tüm veri ve event akışı tek hattan ilerler.
Bu sayede loglama, ödül ve UI işlemleri birbiriyle senkronize yürür.
---
## 🧰 5. Editor & Test Özellikleri
* **FakeStore Modu:**
Unity Editor içinde `StandardPurchasingModule` otomatik olarak FakeStoreu açar.
Test sırasında gerçek ödeme yapılmaz.
```csharp
module.useFakeStoreAlways = true;
module.useFakeStoreUIMode = FakeStoreUIMode.StandardUser;
```
* **Loglama:**
Başarılı ve başarısız tüm işlemler konsola `[IAPService]` etiketiyle yazılır.
---
## 🪙 6. Ekonomi & Ödül Yönetimi
IAP sistemi, **EconomyData** ve **RewardDefinition** ile entegredir:
* Satın alma sonrası ödül → `StoreItem.GiveReward()`
* Sandık → `StoreItem.GiveRandomReward()`
* Kaldırılan reklamlar → `RewardType.RemoveAds`
* Currency tabanlı satın alma → `EconomyData.SpendCurrency()`
---
## 🔔 7. Event Referansları
| Event | Açıklama |
| ---------------------------- | ---------------------------------------- |
| `PurchaseCompletedEvent` | Satın alma başarıyla tamamlandı. |
| `PurchaseFailedEvent` | Satın alma başarısız oldu. |
| `RewardGivenEvent` | Ödül verildi. |
| `Shop_UIStoreItem_Rewarded` | Mağaza ödül popupı tetiklendi. |
| `NoConnectionEvent` | İnternet bağlantısı yok. |
| `RewardedAdUnavailableEvent` | Reklam izlenemedi (AdsService kaynaklı). |
---
## 🧩 8. Yeni Mimarideki Geliştirmeler
| Alan | Yenilik |
| ---------------------------- | ------------------------------------------------------------ |
| **Lifecycle** | Tüm servisler `ServiceBase` üstünden yönetilir. |
| **Bağımlılıklar** | `ServiceLocator` ile otomatik çözülür. |
| **Otomatik EventForwarding** | `EventService` ile doğrudan bağlı. |
| **Firebase Entegrasyonu** | Satın alma logları otomatik olarak `FirebaseService`e akar. |
| **Gelişmiş Restore Logic** | Android ve iOS için unified restore desteği. |
| **UI Bağımsızlık** | Tüm itemler `UIStoreItemBase` tabanlı. |
---
## 🧠 9. Örnek Kod: Mağaza Butonu
```csharp
public void OnGemPackButton()
{
var store = ServiceLocator.Get<IIAPService>();
var item = store.StoreRepository.GetByProductId("com.shirogin.gem_pack_1");
store.Buy(item);
}
```
Satın alma tamamlandığında:
* Oyuncuya ödül verilir.
* `AnalyticsService` log gönderir.
* `PopupService` başarı mesajı gösterir.
---
## 🧾 10. Özet
ShiroginSDK IAP System v1.0.0,
tamamen **event-driven**, **service-oriented** ve **UI-modüler** bir yapıdadır.
Tüm işlemler `ServiceRegistry` tarafından kontrol edilir,
ve sonuçlar SDK genelinde otomatik olarak paylaşılır.
> 🔗 Tam uyumlu servisler:
> **EventService**, **AnalyticsService**, **PopupService**, **EconomyData**, **RemoteConfigService**
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.IAP

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e3e15d2313f244849429fc3d1b530798
timeCreated: 1760522167

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3562bf0ca8ce484a8189bddcdd5c9177
timeCreated: 1760703724

View file

@ -0,0 +1,244 @@
# 📘 ShiroginSDK Remote Config (v1.0.0)
**Firebase Remote Config Integration • ServiceLocator Architecture • Local Fallback System**
Bu doküman, **ShiroginSDK v1.0.0** sürümünde güncellenen Remote Config sistemini açıklar.
Sistem, Firebase Remote Config verilerini dinamik olarak çeker, `RemoteConfigData` ile birleştirir ve local cache sayesinde offline durumda dahi çalışmaya devam eder.
---
## 🚀 1. Firebase Remote Config Kurulumu
### 🔹 Adım 1 — Firebase Projesini Aç
1. [https://console.firebase.google.com](https://console.firebase.google.com) adresine gidin.
2. Projenizi seçin ve sol menüden **Remote Config** sekmesini açın.
---
### 🔹 Adım 2 — Yeni Parametre veya Grup Oluştur
Firebase Remote Config ekranında, SDKdaki `RemoteConfigData` değişkenlerini temsil edecek parametreleri oluşturun.
#### 🖼️ Yeni Parametre Ekleme:
![Add-New-Variable](https://i.imgur.com/hgTGj6T.png)
#### 🖼️ Yeni Grup Oluşturma:
![Add-New-Group](https://i.imgur.com/bPCWOGl.png)
Parametrelerinizi mantıksal gruplar hâlinde düzenlemek, **ShiroginSDK**nin `selectedRemoteGroup` seçeneğiyle eşleşmesini kolaylaştırır.
---
### 🔹 Adım 3 — JSON Formatı ile Firebasee Aktarma
`RemoteConfigDefinition` ScriptableObject üzerinden **Import from RemoteConfigData** butonu ile tüm değişkenleri otomatik olarak senkronize edebilirsiniz.
Daha sonra **Copy JSON to Clipboard** diyerek aşağıdaki formatta Firebasee yapıştırın:
![Add Remote Data](https://i.imgur.com/fG1pvw0.png)
```json
{
"shiroginsdk_remote_config": {
"hp_scale": 100,
"target_fps": 60,
"rewarded_after_interstitial_time": 50
}
}
````
---
## ⚙️ 2. Sistem Mimarisi
| Bileşen | Tür | Açıklama |
| ----------------------- | ------------- | -------------------------------------------------------------- |
| **RemoteConfigService** | `ServiceBase` | Firebaseden verileri çeker, cacheler ve event yayınlar. |
| **RemoteConfigData** | `BaseData` | Çekilen verilerin local kopyasını ve erişim APIsini tutar. |
| **FirebaseService** | `ServiceBase` | Firebase bağlantı yönetimini sağlar. |
| **IEventService** | `Interface` | Güncelleme eventlerini tetikler (`RemoteConfigUpdatedEvent`). |
---
## 🧠 3. Başlatma ve Yaşam Döngüsü
Tüm servisler `ShiroginServiceRegistry` tarafından yönetilir:
```csharp
ShiroginServiceRegistry.InitializeAll();
```
Bu çağrı ile:
* `FirebaseService`
* `DataService`
* `RemoteConfigService`
* `EventService`
otomatik olarak başlatılır ve `ServiceLocator`a kaydedilir.
---
## 🔄 4. Çalışma Akışı
```
Firebase Remote Config
RemoteConfigService (Fetch + Cache)
RemoteConfigData (Merge)
EventService → RemoteConfigUpdatedEvent
SDK Modülleri (Economy, Ads, Gameplay)
```
---
## 🧩 5. Kullanım
### 🔹 Verilere Erişim
Artık `ServiceLocator` tabanlı erişim kullanılır:
```csharp
var dataService = ServiceLocator.Get<IDataService>();
var config = dataService.Get<RemoteConfigData>();
int hpScale = config.GetInt("hp_scale", 100);
float fps = config.GetFloat("target_fps", 60f);
string storeJson = config.GetString("store_data");
```
---
### 🔹 Firebaseden Manuel Veri Yenileme
```csharp
var remoteConfig = ServiceLocator.Get<IRemoteConfigService>();
remoteConfig.ForceRefresh();
```
Bu işlem, Firebaseden en son JSONu çeker, cachei günceller ve
otomatik olarak `RemoteConfigUpdatedEvent` yayınlar.
---
### 🔹 Event Dinleme
Güncellenmiş Remote Config verisini yakalamak için:
```csharp
var eventService = ServiceLocator.Get<IEventService>();
eventService.Subscribe<ShiroginEvents.RemoteConfigUpdatedEvent>(e =>
{
Debug.Log($"RemoteConfig updated: {e.RawJson}");
});
```
---
## 🪙 6. Local Fallback (Offline Çalışma)
* Remote fetch başarısız olursa, `RemoteConfigService` local cachei (`RemoteConfigData`) kullanır.
* Cache 24 saatten daha yeni ise remote fetch atlanır.
* Veriler `Application.persistentDataPath || PlayerPrefs` altında JSON olarak saklanır.
```csharp
var data = ServiceLocator.Get<IDataService>().Get<RemoteConfigData>();
Debug.Log($"Cached HP Scale: {data.GetInt("hp_scale")}");
```
---
## 🧾 7. Firebase JSON Yapısı
**Örnek:**
```json
{
"ShiroginSDK": {
"hp_scale": 120,
"target_fps": 60,
"remove_ads_popup_active": true,
"enemy_hp_multiplier": "1,1.5,2,2.5,3",
"store_data": "{\"shopItems\":[{\"id\":1,\"name\":\"Starter Pack\",\"price\":100}]}"
}
}
```
Bu JSON, `SDKConfig.selectedRemoteGroup` alanında belirtilen grup adıyla eşleşir (örnek: `"ShiroginSDK"`).
---
## 🧩 8. RemoteConfigService APIsi
| Fonksiyon | Açıklama |
| -------------------------------------------- | -------------------------------------- |
| `ForceRefresh()` | Firebaseden manuel veri çekimi yapar. |
| `GetInt(string key, int defaultValue)` | Sayısal değer döner. |
| `GetFloat(string key, float defaultValue)` | Ondalık değer döner. |
| `GetBool(string key, bool defaultValue)` | Mantıksal değer döner. |
| `GetString(string key, string defaultValue)` | Metin döner. |
---
## 🧰 9. RemoteConfigDefinition (Editor Tool)
### Konum
`Assets/ShiroginSDK/Editor/RemoteConfig/RemoteConfigDefinitionEditor.cs`
Bu araç, Firebase Remote Config parametrelerini düzenlemek, senkronize etmek ve JSON oluşturmak için görsel arayüz sağlar.
| Buton | İşlev |
| ----------------------------------- | ---------------------------------------------- |
| ✅ **Validate Keys** | Boş veya tekrarlanan anahtarları kontrol eder. |
| 🔤 **Sort by Key** | Anahtarları alfabetik sıraya dizer. |
| 📋 **Copy JSON to Clipboard** | Firebasee yapıştırılabilir JSON oluşturur. |
| ⬆️ **Import from RemoteConfigData** | SDK tarafındaki alanları otomatik ekler. |
---
## 🌍 10. Offline & Event Akışı
**Senaryo 1 Online:**
1. Firebase bağlantısı kurulur
2. JSON çekilir
3. Cache güncellenir
4. `RemoteConfigUpdatedEvent` tetiklenir
5. SDK modülleri (Ads, Economy vb.) yeni ayarları uygular
**Senaryo 2 Offline:**
1. Remote fetch başarısız olur
2. Local cache kullanılır
3. Oyun eski verilerle devam eder
---
## 📹 11. Kurulum Videosu
🎥 [Remote Config Setup Video](https://dl.dropboxusercontent.com/scl/fi/vm3z2wpcgudm3th15pkwb/NVIDIA_Overlay_IM0A21PVZv.mp4?rlkey=olnmi1xn4qzfp8ug04w3qfaoo&dl=0)
---
## 🧱 12. Özet
| Konsept | Açıklama |
| ----------------------- | --------------------------------------------------- |
| **Mimari** | `ServiceLocator` tabanlı bağımsız servis yapısı |
| **Firebase Bağlantısı** | `FirebaseService` eventleriyle senkronize |
| **Cache Yönetimi** | 24 saatlik otomatik yenileme kontrolü |
| **Event Forwarding** | `RemoteConfigUpdatedEvent` ile global bildirim |
| **Offline Desteği** | Local cache fallback mekanizması |
| **Tip Güvenli Erişim** | `GetInt`, `GetString`, `GetBool` yardımcı metotları |
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.RemoteConfig
---

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 52b42bbed015431e9025e5b5eb0952cb
timeCreated: 1760369687

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f255a838e5b64d92a828601e2e1d7da0
timeCreated: 1760705299

View file

@ -0,0 +1,326 @@
# 🧩 ShiroginSDK Service Architecture (v1.0.0)
**Modular • Service-Oriented • Event-Driven Core Framework**
ShiroginSDKnin servis çekirdeği, tüm SDK modüllerinin (IAP, Popup, Theme, Analytics, RemoteConfig vb.) **tek merkezden yönetilmesini** sağlayan soyut bir sistemdir.
Bu sistem, Unity projelerinde **bağımlılık enjeksiyonu**, **servis yaşam döngüsü** ve **event forwarding** gibi prensipleri uygular.
---
## 🧱 Temel Yapı
| Bileşen | Görev |
|----------|--------|
| **IShiroginService** | Her servisin uyması gereken temel sözleşme (interface) |
| **ServiceBase** | Ortak yaşam döngüsü (Initialize / Dispose) davranışını sağlar |
| **ServiceLocator** | Global erişim noktası, tüm servis örneklerini saklar |
| **ShiroginServiceRegistry** | Servislerin otomatik başlatılmasını ve kayıt edilmesini yönetir |
| **IDataService**, **IEventService** vb. | Özelleşmiş interface türleri (örnek: veri yönetimi, event yönetimi) |
---
## ⚙️ Mimarinin Amacı
- 🔁 Servislerin bağımlılıklarını merkezi olarak yönetmek
- 🧩 Servislerin birbiriyle loosely coupled (gevşek bağlı) çalışmasını sağlamak
- 🧠 Modül bazlı mimariyle SDKnın kolay genişletilmesini mümkün kılmak
- 🧰 Editor araçları, runtime servisler ve sistem olaylarını tek lifecycle üzerinden yürütmek
---
## 🔄 Servis Yaşam Döngüsü
Tüm servisler aşağıdaki sırayla çalışır:
```
SDKInitializer
ShiroginServiceRegistry.InitializeAll()
ServiceLocator.Register<T>()
OnInitialize() çağrısı
EventService aracılığıyla sistem olayları
OnDispose() (Oyun kapanışı veya reset)
````
---
## 🧠 1. IShiroginService
Tüm servislerin temel interfacei.
```csharp
public interface IShiroginService
{
void Initialize();
void Dispose();
bool IsInitialized { get; }
}
````
Amaç: Her servisin başlatma ve temizleme rutinini standartlaştırmak.
---
## 🧩 2. ServiceBase
`MonoBehaviour`e ihtiyaç duymadan servislerin yaşam döngüsünü yönetir.
Tüm custom servisler bu sınıftan türetilir.
```csharp
public abstract class ServiceBase : IShiroginService
{
public bool IsInitialized { get; private set; }
public void Initialize()
{
if (IsInitialized) return;
OnInitialize();
IsInitialized = true;
}
public void Dispose()
{
OnDispose();
IsInitialized = false;
}
protected abstract void OnInitialize();
protected virtual void OnDispose() { }
}
```
> 🔹 Her servis `OnInitialize()` ve `OnDispose()` metodlarını override ederek kendi kurulum/temizlik işlemlerini yapar.
> 🔹 `DontDestroyOnLoad` kullanımı veya `MonoBehaviour` bağımlılığı olmadan çalışır.
---
## 🧭 3. ServiceLocator
Servislerin global erişim noktasını oluşturur.
İçerisinde **tip güvenli bir dictionary** tutar ve `Get<T>()` ile erişim sağlar.
```csharp
var dataService = ServiceLocator.Get<IDataService>();
var eventService = ServiceLocator.Get<IEventService>();
```
Yeni servis kaydı:
```csharp
ServiceLocator.Register<IDataService>(new DataService());
```
> 📌 ServiceLocator, **Singleton yerine Dependency Injection** felsefesine yakındır.
> Servislerin instance yönetimi `ShiroginServiceRegistry` tarafından yapılır.
---
## 🏗️ 4. ShiroginServiceRegistry
Tüm servislerin oluşturulmasından, sırayla başlatılmasından ve kapatılmasından sorumludur.
```csharp
public static class ShiroginServiceRegistry
{
public static void InitializeAll()
{
// ------------------------------------------------------------------
// 🧩 CORE LAYER
// ------------------------------------------------------------------
RegisterAndInit<IEventService>(new EventService()); // Events backbone
RegisterAndInit<IDataService>(new DataService()); // Save/Load system
RegisterAndInit<ICoroutineRunnerService>(new CoroutineRunnerService()); // Global coroutine executor
// ------------------------------------------------------------------
// 🔥 BACKEND LAYER
// ------------------------------------------------------------------
RegisterAndInit<IFirebaseService>(new FirebaseService()); // Firebase Analytics + Remote Config
RegisterAndInit<IRemoteConfigService>(new RemoteConfigService()); // Uses FirebaseService
RegisterAndInit<IAppsFlyerService>(new AppsFlyerService()); // AppsFlyer analytics SDK
RegisterAndInit<IFacebookService>(new FacebookService()); // Facebook SDK
RegisterAndInit<IAnalyticsService>(new AnalyticsService()); // Unified analytics forwarding
// ------------------------------------------------------------------
// 💰 ECONOMY & ADS
// ------------------------------------------------------------------
RegisterAndInit<IIAPService>(new IAPService()); // In-app purchases
RegisterAndInit<IAdsService>(new AdsService()); // Rewarded & Interstitial ads
// ------------------------------------------------------------------
// 🎨 UI / PRESENTATION LAYER
// ------------------------------------------------------------------
RegisterAndInit<IThemeService>(new ThemeService()); // UI themes / skins
RegisterAndInit<IPopupService>(new PopupService()); // Global popup queue system
UnityEngine.Debug.Log("[ShiroginSDK] ✅ All services initialized successfully.");
}
/// <summary>
/// Registers and initializes a service with its interface type.
/// </summary>
private static void RegisterAndInit<TInterface>(ServiceBase service) where TInterface : class
{
// Register by interface type
ServiceLocator.Register<TInterface>((TInterface)(object)service);
service.Initialize();
}
public static void DisposeAll()
{
ServiceLocator.Clear();
UnityEngine.Debug.Log("[ShiroginSDK] 🧹 All services disposed.");
}
}
```
> ✅ Servisler initialize sırasına göre çalışır, disposeda ters sırayla kapanır.
> 🧩 Yeni servis eklemek yalnızca ` RegisterAndInit<IPopupService>(new MyService());` satırı eklemek kadar kolaydır.
---
## 🧩 5. Yeni Servis Oluşturma
Aşağıda yeni bir servis (örnek: `AudioService`) nasıl ekleneceği gösterilmiştir.
```csharp
using ShiroginSDK.Runtime.Services.Base;
using ShiroginSDK.Runtime.Services.Interfaces;
using UnityEngine;
public class AudioService : ServiceBase, IAudioService
{
protected override void OnInitialize()
{
Debug.Log("[AudioService] Initialized");
// Audio mixer setup, volume cache, etc.
}
protected override void OnDispose()
{
Debug.Log("[AudioService] Disposed");
}
public void PlaySFX(string clipName)
{
// ...
}
}
```
**Kayıt etmek için:**
```csharp
RegisterAndInit<IEventService>(new AudioService());
```
Artık global erişim mümkündür:
```csharp
ServiceLocator.Get<IAudioService>().PlaySFX("coin_pickup");
```
---
## ⚙️ 6. Servis Arası Etkileşim
Her servis `ServiceLocator` aracılığıyla diğer servislere erişebilir:
```csharp
protected override void OnInitialize()
{
var dataService = ServiceLocator.Get<IDataService>();
var eventService = ServiceLocator.Get<IEventService>();
eventService.Invoke(new GameStartedEvent());
}
```
> Bu sayede servisler birbirine doğrudan bağımlı değil, **aracılı** çalışır.
> Bu yapı **test edilebilirliği** ve **mock servis** kullanımını kolaylaştırır.
---
## 🧠 7. İleri Konular
### 🔹 CoroutineRunnerService
`MonoBehaviour` bağlamında coroutine çalıştırmak için soyut bir arayüz sunar.
`CoroutineRunner.Start(...)` ile tüm SDK servisleri coroutine başlatabilir.
### 🔹 IDataService
Oyun verilerinin (`BaseData`) yönetimini sağlar: `Get<T>()`, `SaveAll()`, `ToJson()` vb.
### 🔹 IEventService
SDK genelinde event forwarding sağlar.
Tüm servisler eventleri publish/subscribe ile paylaşır.
Örneğin:
```csharp
_eventService.Invoke(new ShiroginEvents.PurchaseCompletedEvent());
```
### 🔹 Extensibility (Genişletilebilirlik)
Yeni servisler yalnızca şu 3 adıma ihtiyaç duyar:
1. `ServiceBase`ten türet
2. Gerekirse bir `IYourService` interface tanımla
3. `ShiroginServiceRegistry.Register(new YourService())` çağrısı ekle
---
## 🔍 8. Debugging & Logging
Tüm servisler initialization sırasında log üretir.
Hatalar `[ShiroginSDK]` etiketiyle konsola basılır.
Örnek:
```
[PopupService] ✅ Initialized and bound to SDKConfig.
[ThemeService] 🌈 Active theme set to 'Snow'
[IAPService] 💳 Unity Purchasing initialized.
```
---
## ⚡ 9. Performans Notları
* Servisler `DontDestroyOnLoad` veya `MonoBehaviour` objelerine bağlı değildir.
* Tüm bağımlılıklar `ServiceLocator` üzerinden çözülür, böylece yükleme sırası kontrol altındadır.
* Gereksiz singleton oluşturulmaz, yalnızca kullanılan servisler bellekte kalır.
---
## 🧾 10. Özet
| Özellik | Açıklama |
| -------------------------- | --------------------------------------------- |
| 🔄 **Lifecycle Yönetimi** | `ServiceBase` ve `ServiceRegistry` üzerinden |
| 🧩 **Bağımlılık Yönetimi** | `ServiceLocator` ile type-safe çözüm |
| 🚀 **Modüler Yapı** | Her modül bağımsız servis olarak çalışır |
| 🧱 **Event-Driven** | `IEventService` ile sistem içi iletişim |
| ⚙️ **Kolay Genişletme** | 3 satırda yeni servis ekleme |
| 🧠 **Test Edilebilir** | Mock servislerle bağımsız testler yapılabilir |
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services
---
> 📘 İpucu:
> Servis sisteminin amacı “oyun sistemleri arasındaki sınırları netleştirmek”tir.
> Bu yapıyı doğru kullandığında, SDKnın her yeni modülü — IAP, Popup, RemoteConfig, Theme — sadece **bir servis olarak** eklenir.
> Böylece ShiroginSDK, Unitynin üstünde kendi mini operating systemini oluşturur.

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 628d5666b26b45d683592368eac9c010
timeCreated: 1760705260

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 678e9dc85a964935a34eb45f1a113498
timeCreated: 1760703765

View file

@ -0,0 +1,261 @@
# 📦 ShiroginSDK Popup Service (v1.0.0)
**Event-Driven Popup Management • ServiceLocator Integration • DOTween Animations**
Bu doküman, **ShiroginSDK v1.0.0** sürümünde güncellenen `PopupService` sistemini açıklar.
Yeni mimari, `ServiceBase` tabanlı olup tüm servisler `ShiroginServiceRegistry` tarafından başlatılır.
`PopupService`, `EventService` üzerinden tetiklenen eventlere yanıt verir ve `PopupRepository`deki prefablar aracılığıyla popupları yönetir.
---
## 🚀 1. Genel Bakış
`PopupService`, oyun boyunca kullanıcıya gösterilen tüm popupları (ödül, hata, bağlantı hatası, satın alma, reklam vb.) kontrol eder.
**Özellikler:**
* ✅ `ServiceBase` tabanlı bağımsız servis yapısı
* ✅ `ServiceLocator` ile erişim
* ✅ `EventService` üzerinden otomatik popup tetikleme
* ✅ `DOTween` tabanlıılış / kapanış animasyonları
* ✅ `PopupRepository` üzerinden prefab yönetimi
* ✅ Otomatik queue sistemi (popuplar sırayla gösterilir)
---
## 🧩 2. Mimari Bileşenler
| Bileşen | Tip | Açıklama |
|---------------------|------------------|-----------|
| **PopupService** | `ServiceBase` | Tüm popup kuyruğunu ve animasyonları yönetir. |
| **PopupRepository** | `ScriptableObject` | Popup prefab referanslarını tutar. |
| **UIPopupBase** | `MonoBehaviour` | Her popup prefabının temel sınıfıdır. |
---
## 🧠 3. Servis Mimarisi
### 🔹 Başlatma
Tüm servisler otomatik olarak `ShiroginServiceRegistry.InitializeAll()` çağrısı ile başlatılır.
```csharp
ShiroginServiceRegistry.InitializeAll();
````
Bu çağrı `EventService`, `PopupService`, `AnalyticsService`, `IAPService` gibi tüm servisleri oluşturur ve `ServiceLocator`a kaydeder.
---
### 🔹 Erişim
`PopupService` artık singleton değil.
Servise erişim `ServiceLocator` üzerinden yapılır:
```csharp
var popupService = ServiceLocator.Get<IPopupService>();
popupService.Show(PopupType.RewardClaimed, eventData);
```
---
## 🪄 4. Popup Gösterme Yöntemleri
### 🔸 1⃣ EventService Üzerinden Otomatik Gösterim
`PopupService`, `IEventService`e abonedir ve belirli `ShiroginEvents` eventlerine yanıt verir.
Aşağıdaki eventler otomatik olarak popup tetikler:
| Event Tipi | Gösterilen PopupType |
| ---------------------------- | --------------------------------- |
| `RewardClaimedEvent` | `PopupType.RewardClaimed` |
| `PurchaseCompletedEvent` | `PopupType.PurchaseSuccess` |
| `PurchaseFailedEvent` | `PopupType.PurchaseFailed` |
| `NoConnectionEvent` | `PopupType.NoConnection` |
| `RewardedAdUnavailableEvent` | `PopupType.RewardedAdUnavailable` |
**Örnek:**
```csharp
var eventService = ServiceLocator.Get<IEventService>();
eventService.Invoke(new ShiroginEvents.RewardClaimedEvent("daily_bonus", "Daily Reward", 100, "Gold"));
```
`PopupService`, bu eventi dinler ve otomatik olarak `RewardClaimed` popupını sıraya alır.
---
### 🔸 2⃣ Kod Üzerinden Manuel Gösterim
```csharp
var popupService = ServiceLocator.Get<IPopupService>();
popupService.Show(PopupType.NoConnection, new ShiroginEvents.NoConnectionEvent());
```
Bu yöntemle event olmadan da popup manuel olarak sıraya eklenebilir.
---
## 🧩 5. Yeni Popup Eklemek
### 1⃣ Yeni Prefab Oluştur
1. `Assets/ShiroginSDK/UI/Popups/` klasörüne git.
2. Yeni prefab oluştur (örnek: `UIPopup_LevelUp`).
3. `UIPopupBase`den türeyen script ekle:
```csharp
using UnityEngine;
using ShiroginSDK.Runtime.UI.Scripts.UI.Base;
public class UIPopup_LevelUp : PopupBase
{
[SerializeField] private TMPro.TextMeshProUGUI levelText;
public override void Initialize(object data)
{
if (data is ShiroginEvents.PlayerLevelUpEvent e)
levelText.text = $"LEVEL {e.NewLevel}";
}
}
```
---
### 2⃣ PopupType Enumuna Ekle
```csharp
public enum PopupType
{
RewardClaimed,
PurchaseSuccess,
LevelUp,
NoConnection,
PurchaseFailed,
RewardedAdUnavailable
}
```
---
### 3⃣ PopupRepositoryye Ekle
1. `Assets/Resources/PopupRepository.asset` dosyasını aç.
2. Yeni satır ekle:
* **PopupType:** `LevelUp`
* **Prefab:** `UIPopup_LevelUp`
---
### 4⃣ Test Et
```csharp
var popupService = ServiceLocator.Get<IPopupService>();
popupService.Show(PopupType.LevelUp, new ShiroginEvents.PlayerLevelUpEvent(9, 10, 500));
```
veya event tabanlı:
```csharp
ServiceLocator.Get<IEventService>().Invoke(new ShiroginEvents.PlayerLevelUpEvent(9, 10, 500));
```
---
## 🎬 6. Animasyon ve Kuyruk Sistemi
Popuplar sırayla gösterilir.
Yeni bir popup çağrıldığında, mevcut popup kapanana kadar bekler.
Kapanış tamamlandığında sıradaki popup açılır.
**Akış:**
```
RewardClaimed → PurchaseSuccess → NoConnection
```
**DOTween Animasyonları:**
* Fade in/out (arka plan karartması)
* Scale up (açılış animasyonu)
* Scale down (kapanış animasyonu)
---
## ⚙️ 7. Kullanılabilir API
| Fonksiyon / Özellik | Açıklama |
| ------------------------------------- | ------------------------------------------- |
| `Show(PopupType, object)` | Popup gösterir. |
| `Enqueue(PopupType, object)` | Popupı sıraya ekler. |
| `SetCanvas(Canvas canvas)` | Root canvas atanır. |
| `SetRepository(PopupRepository repo)` | Repository dinamik olarak değiştirilebilir. |
---
---
## 🧩 8. Servis Entegrasyonu
PopupService, şu servislerle entegre çalışır:
| Servis | Amaç |
| ------------------- | ----------------------------------- |
| `IEventService` | Popup tetikleme eventlerini dinler |
| `IDataService` | Gerekirse popup bazlı veri erişimi |
| `IAnalyticsService` | Popup açılma logları (isteğe bağlı) |
Bu sistem, SDK genelinde **event-driven** iletişim modeline bağlıdır.
Hiçbir servis diğerine doğrudan referans vermez — tüm bağlantılar `ServiceLocator` üzerinden yapılır.
---
## 🧱 10. Örnek Dosya Yapısı
```
Assets/
└── ShiroginSDK/
├── Runtime/
│ ├── Core/
│ │ └── SDKInitializer.cs
│ ├── Services/
│ │ ├── Base/
│ │ └── Implementations/
│ │ └── PopupService.cs
│ └── UI/
│ ├── PopupBase.cs
│ └── UIPopup_LevelUp.cs
└── Resources/
└── PopupRepository.asset
```
---
## 🧾 11. Özet
| İşlem | Nasıl Yapılır | Örnek |
| --------------- | ------------------------------------- | ----------------------------------------------------------------- |
| Popup göster | `ServiceLocator.Get<IPopupService>()` | `popupService.Show(PopupType.RewardClaimed, data)` |
| Event tetikle | `ServiceLocator.Get<IEventService>()` | `eventService.Invoke(new ShiroginEvents.RewardClaimedEvent(...))` |
| Yeni popup ekle | Prefab + Enum + Repository | `UIPopup_LevelUp` |
| Kuyruk sistemi | Otomatik | Popup kapanmadan yeni popup gösterilmez |
---
## ✅ 12. Örnek Akış
1. Oyuncu günlük ödül alır
2. `RewardClaimedEvent` tetiklenir
3. `IEventService` popup servisine sinyal gönderir
4. `PopupService` `RewardClaimed` popupını sıraya ekler
5. DOTween ile popup açılır
6. Kapanış sonrası sıradaki popup gösterilir
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.UI.PopupService
---

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 78d0c951bdfc443e97d3e13bf874f549
timeCreated: 1760031740

View file

@ -0,0 +1,251 @@
# 🎨 ShiroginSDK Theme System (v1.0.0)
**Dynamic UI Skin Management • ServiceLocator Architecture • Scriptable Themes**
ShiroginSDKnin **Theme System**i, oyun içi UI öğelerinin (ikonlar, arka planlar, butonlar vb.) görünümünü merkezi ve modüler bir şekilde yönetmeni sağlar.
Tüm temalar **enum tabanlı**, **ScriptableObject destekli**, **event-driven** bir yapıda çalışır.
---
## 🚀 1. Amaç
Bu sistemin hedefi, farklı temalar arasında **anında geçiş** sağlamak ve
UI öğelerinin görünümünü **kod değiştirmeden** yönetmektir.
**Temel Hedefler:**
- 🎨 Farklı temalar arasında runtime geçiş
- 🔁 Tema değiştiğinde tüm UInın otomatik yenilenmesi
- 💾 Oyuncunun seçtiği temanın kaydedilmesi (`ThemeData`)
- 🧩 Enum tabanlı erişim (string hatalarına karşı güvenli)
- ⚡ `ScriptableObject` tabanlı profil yapısı
---
## 🧱 2. Sistem Bileşenleri
| Bileşen | Açıklama |
|----------|-----------|
| **ThemeAssetType** | Görsel tiplerini (ikon, arka plan, vb.) enum olarak listeler |
| **ThemeProfile** | Tek bir temaya ait sprite eşlemelerini tutar |
| **ThemeRepository** | Tüm `ThemeProfile` varlıklarını barındırır |
| **ThemeService** | Aktif temayı yönetir, `ServiceLocator` ile erişilir |
| **ThemeImage** | UI `Image` bileşenlerine otomatik sprite ataması yapar |
| **ThemeData** | Oyuncunun aktif temasını ve açılmış temaları saklar |
---
## 🧩 3. ThemeAssetType.cs
UI görsellerinin türlerini `enum` olarak tanımlar.
Kod içinde string kullanmadan, **type-safe** erişim sağlar.
```csharp
public enum ThemeAssetType
{
None = 0,
// ICONS
Icon_SettingsButton,
Icon_VibrationButton,
// BACKGROUNDS
Background_MainMenu,
Background_PopupSuccess,
// CURRENCIES
Icon_Currency_Gold,
Icon_Currency_Gem,
}
````
---
## 🗂️ 5. ThemeRepository
Tüm temaları tek bir ScriptableObject altında toplar.
Örneğin:
* `Default`
* `Dark`
* `Snow`
* `Halloween`
Unityde oluşturmak için:
> **Right Click → Create → ShiroginSDK → Theme → Theme Repository**
Ardından `ThemeProfile` varlıklarını listeye ekle.
---
## 🧠 6. ThemeService (Yeni Sistem)
`ThemeService`, artık `ServiceBase`ten türetilir ve `ServiceLocator` aracılığıyla erişilir.
Tüm bağımlılıklarını (`IDataService`, `IEventService`) otomatik çözer.
### Başlatma
`ShiroginServiceRegistry.InitializeAll();` çağrısı ile otomatik olarak başlatılır.
`SDKConfig.themeRepository` üzerinden `ThemeRepository` referansını yükler.
```csharp
var themeService = ServiceLocator.Get<IThemeService>();
themeService.SetTheme("Snow");
```
---
### 🎨 Aktif Tema Değiştirme
```csharp
var themeService = ServiceLocator.Get<IThemeService>();
themeService.SetTheme("Dark");
```
Bu işlem:
1. `_repository` içinden “Dark” temasını bulur
2. `ThemeChanged` eventini yayınlar
3. `ThemeData.ActiveTheme`i kaydeder
4. Tüm `ThemeImage` bileşenlerini otomatik yeniler
---
### 🧩 Sprite Alma
```csharp
var themeService = ServiceLocator.Get<IThemeService>();
var icon = themeService.GetSprite(ThemeAssetType.Icon_SettingsButton);
myButton.image.sprite = icon;
```
---
### 💾 Kaydetme & Yükleme
Aktif tema `ThemeData` aracılığıyla otomatik olarak saklanır ve oyun açılışında geri yüklenir.
```csharp
var dataService = ServiceLocator.Get<IDataService>();
var themeData = dataService.Get<ThemeData>();
Debug.Log($"Active Theme: {themeData.ActiveTheme}");
```
---
## 🧰 7. ThemeImage.cs
Herhangi bir `Image` bileşenine eklenir.
Seçilen `ThemeAssetType`a göre spriteı otomatik günceller.
Tema değiştiğinde eventler aracılığıyla yeniden sprite ataması yapılır.
**Kullanım:**
1. UI objene `ThemeImage` componenti ekle
2. `assetType` alanında örneğin `Icon_SettingsButton` seç
3. Tema değiştiğinde sprite otomatik değişir
---
## 💾 8. ThemeData.cs
Oyuncunun aktif temasını ve açılmış temalarını saklar.
Oyun açılışında `DataService` tarafından otomatik yüklenir.
```csharp
public class ThemeData : BaseData
{
protected override string PrefsKey => "THEME_DATA";
public string ActiveTheme = "Default";
public List<string> UnlockedThemes = new() { "Default" };
}
```
---
## 🔄 9. Akış Mantığı
```
[SDKInitializer]
ThemeService → Load from ThemeRepository
ThemeData.ActiveTheme → Set Active
ThemeChanged Event
ThemeImage components update automatically
```
---
## 🌈 10. Tema Geçişi Örneği
```csharp
using ShiroginSDK.Runtime.Services.Interfaces;
using UnityEngine;
public class ThemeSwitcher : MonoBehaviour
{
public void OnClick_SnowTheme()
{
ServiceLocator.Get<IThemeService>().SetTheme("Snow");
}
public void OnClick_DefaultTheme()
{
ServiceLocator.Get<IThemeService>().SetTheme("Default");
}
}
```
---
## 🌟 11. Özellikler
| Özellik | Açıklama |
| ---------------------------- | -------------------------------------------- |
| 💾 Kalıcı Kayıt | `ThemeData` aracılığıyla otomatik kaydedilir |
| 🔄 Gerçek Zamanlı Güncelleme | Tema değişince tüm UI otomatik yenilenir |
| 🧩 Modüler | Yeni tema eklemek yalnızca 1 dakika sürer |
| 🎨 Editor Dostu | ScriptableObject tabanlı sistem |
| ⚡ Optimize | Sadece aktif tema bellekte tutulur |
| 🧱 Tip Güvenli | Enum tabanlı erişim ile string hatası yok |
---
## 💡 12. Geliştirici Tavsiyeleri
* Her temaya kısa, net bir isim ver (`Default`, `Dark`, `Snow`)
* Sprite atlaslarını kullanarak performansı artır
* `ThemeImage` ile manuel sprite değişiminden kaçın
* Tema varlıklarını düzenli tut (`Assets/ShiroginSDK/Themes/`)
* `ThemeRepository` içine tüm temaları dahil etmeyi unutma
---
## ✅ 13. Özet
ShiroginSDK Theme System v1.0.0:
* UI görsellerini tek yerden yönetmeni sağlar
* Oyuncu seçimini kaydeder ve yeniden yükler
* Enum tabanlı tip güvenli sistemle çalışır
* `ServiceLocator` mimarisiyle SDKnın diğer servisleriyle uyumludur
---
**Prepared by:** Emir Han MAMAK
**Version:** 1.1.0 (2025.12)
**Module:** ShiroginSDK.Runtime.Services.Implementations.UI.ThemeService
---
**Scriptable Objects :**
![DarkTheme](https://i.imgur.com/kpxL5x2.png)
![FireTheme](https://i.imgur.com/tik1x6a.png)
![SnowTheme](https://i.imgur.com/n6eqh2o.png)

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e8bbe50da2eb4c5799bac59716cc0616
timeCreated: 1760341956

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 71f30c3bc79174444af5a06024dd58d2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 557403d2360a1f84ab6d8e1d9ba77438
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,925 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine.Networking;
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;
using ShiroginSDK.Runtime.Core.SDK;
using ShiroginSDK.Editor.Core;
namespace ShiroginSDK.Editor.Bitbucket
{
// --- DATA STRUCTURES ---
[Serializable]
public class VersionData
{
public string version;
public string packageName; // NEW: Specific ID for this version (e.g., shirogin.sdk.appsflyer.6.17.72)
public string url;
public string docsWebUrl;
public string docsDownloadUrl;
public string releaseNotes;
public string fileSize;
public bool isExternal;
public bool isUpm;
public string upmId;
}
[Serializable]
public class PackageData
{
public string uniqueId;
public string displayName;
public string category;
public VersionData[] versions;
}
[Serializable]
public class PackageManifest
{
public PackageData[] packages;
}
// --- CONFIG EDITOR ---
[CustomEditor(typeof(ShiroginConfig))]
public class ShiroginConfigEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Space(20);
GUILayout.Label("SDK Compilation Control", EditorStyles.boldLabel);
GUI.backgroundColor = new Color(0.2f, 0.8f, 0.2f);
if (GUILayout.Button("✅ Apply Changes & Compile", GUILayout.Height(40)))
{
bool confirm = EditorUtility.DisplayDialog("Apply SDK Defines?",
"This will trigger a script recompilation.\nEnsure required packages are installed.",
"Yes, Compile", "Cancel");
if (confirm) ShiroginDefinesManager.UpdateDefines();
}
GUI.backgroundColor = Color.white;
GUILayout.Space(10);
GUI.backgroundColor = new Color(0.2f, 0.6f, 1f);
if (GUILayout.Button("OPEN ASSET MANAGER", GUILayout.Height(40)))
{
AssetRegistryEditor.ShowWindow();
}
GUI.backgroundColor = Color.white;
}
}
// --- EDITOR WINDOW ---
[InitializeOnLoad]
public class AssetRegistryEditor : EditorWindow
{
private ShiroginConfig _config;
private static AddRequest _upmAddRequest;
private static bool _isInstallingUpm;
// Essential Packages (Uses EXACT packageName from VersionData)
private readonly List<string> _essentialPackages = new List<string>
{
"shirogin.sdk.googleexternaldependencymanager.1.2.186.Shirogin",
"shirogin.sdk.dotweenpro.1.0.380.Shirogin",
"com.unity.purchasing.4.13.0",
"shirogin.sdk.applovinmax.8.5.1.Shirogin",
"shirogin.sdk.firebaseanalytics.13.6.0.Shirogin",
"shirogin.sdk.appsflyer.6.17.72.Shirogin",
"shirogin.sdk.facebooksdk.18.0.0.Shirogin",
"shirogin.sdk.firebaseremoteconfig.13.6.0.Shirogin"
};
private readonly List<string> _rootPackages = new List<string>
{
"Firebase", "Facebook", "AppLovin", "AppsFlyer", "External"
};
private Queue<PackageData> _downloadQueue = new Queue<PackageData>();
private PackageData _currentQueueItem;
private VersionData _currentQueueVersion;
private bool _isQueueRunning = false;
private List<PackageData> allPackages = new List<PackageData>();
private List<PackageData> filteredPackages = new List<PackageData>();
private HashSet<string> existingFolderNames = new HashSet<string>();
private bool _isSetupMode = false;
private string searchString = "";
private string selectedCategory = "All";
private string[] categories = new string[] { "All" };
private Vector2 scrollPos;
private bool showSettings = false;
private string jsonListUrl = "";
private string repoToken = "";
private Dictionary<string, int> packageVersionSelection = new Dictionary<string, int>();
private bool isWorking = false;
private string statusMessage = "Waiting for download...";
private float downloadProgress = 0f;
private EditorApplication.CallbackFunction downloadDelegate;
private UnityWebRequest currentRequest;
static AssetRegistryEditor()
{
EditorApplication.delayCall += CheckFirstTimeInstall;
EditorApplication.update += UpmProgressUpdate;
}
private static void CheckFirstTimeInstall()
{
if (!EditorPrefs.GetBool("ShiroginSDK_WelcomeShown_V2", false))
{
EditorPrefs.SetBool("ShiroginSDK_WelcomeShown_V2", true);
ShowWindow(true);
}
}
private static void UpmProgressUpdate()
{
if (_isInstallingUpm && _upmAddRequest != null)
{
if (_upmAddRequest.IsCompleted)
{
_isInstallingUpm = false;
var wnd = GetWindow<AssetRegistryEditor>();
if (_upmAddRequest.Status == StatusCode.Success)
{
Debug.Log($"[ShiroginSDK] UPM Package Installed: {_upmAddRequest.Result.packageId}");
if(wnd) wnd.RefreshFolderCache();
}
else
{
Debug.LogError($"[ShiroginSDK] UPM Install Failed: {_upmAddRequest.Error.message}");
}
if(wnd)
{
wnd.ProcessNextQueueItem();
wnd.Repaint();
}
_upmAddRequest = null;
}
}
}
[MenuItem("ShiroginSDK/Shirogin Asset Manager")]
public static void ShowWindow() => ShowWindow(false);
public static void ShowWindow(bool forceSetupMode)
{
AssetRegistryEditor wnd = GetWindow<AssetRegistryEditor>("Asset Manager");
wnd.minSize = new Vector2(650, 750);
if (forceSetupMode) wnd.SetSetupMode(true);
wnd.Show();
}
public void SetSetupMode(bool active)
{
_isSetupMode = active;
ApplyFilters();
}
private void OnEnable()
{
_config = ShiroginConfig.Load();
jsonListUrl = EditorPrefs.GetString("Shirogin_RepoUrl", "https://api.bitbucket.org/2.0/repositories/batud/studio-asset-registry/src/main/packages.json");
repoToken = EditorPrefs.GetString("Shirogin_RepoToken", _config != null ? _config.bitbucketAccessToken : "");
RefreshFolderCache();
if (!string.IsNullOrEmpty(jsonListUrl) && !string.IsNullOrEmpty(repoToken))
FetchPackageList();
else
showSettings = true;
}
private void OnDisable()
{
AbortDownload();
_downloadQueue.Clear();
_isQueueRunning = false;
}
private void OnFocus() => RefreshFolderCache();
private void RefreshFolderCache()
{
existingFolderNames.Clear();
string internalSdkPath = Path.Combine(Application.dataPath, "ShiroginSDK", "ThirdPartySDKs");
if (Directory.Exists(internalSdkPath))
{
try
{
string[] dirs = Directory.GetDirectories(internalSdkPath, "*", SearchOption.AllDirectories);
foreach (var d in dirs) existingFolderNames.Add(new DirectoryInfo(d).Name.ToLower());
} catch {}
}
try
{
string[] rootDirs = Directory.GetDirectories(Application.dataPath, "*", SearchOption.TopDirectoryOnly);
foreach (var d in rootDirs) existingFolderNames.Add(new DirectoryInfo(d).Name.ToLower());
} catch {}
}
// --- GUI ---
private void OnGUI()
{
EditorGUI.DrawRect(new Rect(0, 0, position.width, position.height), new Color(0.22f, 0.22f, 0.22f));
DrawModernHeader();
GUILayout.BeginVertical();
if (_isSetupMode) DrawSetupWizardUI();
else
{
DrawToolbar();
if (showSettings) DrawSettings();
GUILayout.Space(5);
DrawCategories();
GUILayout.Space(5);
DrawPackageList();
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
DrawStaticStatusBar();
}
private void DrawModernHeader()
{
Rect headerRect = new Rect(0, 0, position.width, 60);
Color headerColor = _isSetupMode ? new Color(0.1f, 0.3f, 0.5f) : new Color(0.15f, 0.15f, 0.15f);
EditorGUI.DrawRect(headerRect, headerColor);
GUILayout.BeginArea(headerRect);
GUILayout.BeginVertical();
GUILayout.FlexibleSpace();
GUIStyle titleStyle = new GUIStyle(EditorStyles.boldLabel) { fontSize = 18, normal = { textColor = new Color(0.95f, 0.95f, 0.95f) }, alignment = TextAnchor.MiddleCenter };
GUIStyle subStyle = new GUIStyle(EditorStyles.label) { fontSize = 11, normal = { textColor = new Color(0.8f, 0.8f, 0.8f) }, alignment = TextAnchor.MiddleCenter };
string title = _isSetupMode ? "WELCOME TO SHIROGIN SDK" : "SHIROGIN ASSET MANAGER";
string sub = _isSetupMode ? "Setup Wizard & Essential Packages" : "Package Management System";
GUILayout.Label(title, titleStyle);
GUILayout.Label(sub, subStyle);
GUILayout.FlexibleSpace();
GUILayout.EndVertical();
GUILayout.EndArea();
GUILayout.Space(65);
}
private void DrawSetupWizardUI()
{
GUILayout.Space(10);
// --- CORE MISSING CHECK ---
int missingCount = 0;
if(filteredPackages != null)
{
foreach(var p in filteredPackages)
{
// Find the REQUIRED version data
VersionData requiredVer = p.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName));
if(requiredVer != null && !IsPackageInstalled(p, requiredVer))
missingCount++;
}
}
if (missingCount > 0)
{
GUI.backgroundColor = new Color(1f, 0.6f, 0.6f);
EditorGUILayout.HelpBox($"⚠ SDK CORE MISSING ({missingCount} packages). Please install essential packages below.", MessageType.Error);
GUI.backgroundColor = Color.white;
}
else
{
GUI.backgroundColor = Color.green;
EditorGUILayout.HelpBox("✅ SDK Core is fully installed and ready!", MessageType.Info);
GUI.backgroundColor = Color.white;
}
if (string.IsNullOrEmpty(repoToken))
{
if(GUILayout.Button("Open Settings to Enter Token")) showSettings = !showSettings;
if(showSettings) DrawSettings();
}
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
bool canDownloadAll = !isWorking && !_isInstallingUpm && missingCount > 0;
EditorGUI.BeginDisabledGroup(!canDownloadAll);
GUI.backgroundColor = missingCount > 0 ? new Color(0.2f, 0.6f, 1f) : Color.gray;
string btnLabel = missingCount > 0 ? $"⬇ Download & Install All Core ({missingCount})" : "All Core Installed ✓";
if (GUILayout.Button(btnLabel, GUILayout.Height(35), GUILayout.Width(300)))
{
StartDownloadAllQueue();
}
GUI.backgroundColor = Color.white;
EditorGUI.EndDisabledGroup();
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(10);
DrawPackageList();
GUILayout.Space(15);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Continue to Full Dashboard ➔", GUILayout.Height(40), GUILayout.Width(250))) SetSetupMode(false);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void DrawToolbar()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
if (GUILayout.Button("⚙ Settings", EditorStyles.toolbarButton, GUILayout.Width(80))) showSettings = !showSettings;
if (GUILayout.Button("★ Setup Wizard", EditorStyles.toolbarButton, GUILayout.Width(100))) SetSetupMode(true);
if (GUILayout.Button("Refresh", EditorStyles.toolbarButton, GUILayout.Width(60))) { RefreshFolderCache(); FetchPackageList(); }
if (GUILayout.Button("Clear Cache", EditorStyles.toolbarButton, GUILayout.Width(80))) { ClearCache(); Repaint(); }
GUILayout.FlexibleSpace();
GUILayout.Label("Search:", GUILayout.Width(50));
string newSearch = EditorGUILayout.TextField(searchString, EditorStyles.toolbarTextField, GUILayout.Width(150));
if (newSearch != searchString) { searchString = newSearch; ApplyFilters(); }
if (GUILayout.Button("×", EditorStyles.toolbarButton, GUILayout.Width(25))) { searchString = ""; GUI.FocusControl(null); ApplyFilters(); }
GUILayout.EndHorizontal();
}
private void DrawCategories()
{
if (categories == null || categories.Length == 0) return;
GUILayout.BeginHorizontal();
GUILayout.Space(10);
foreach (var cat in categories)
{
bool isSelected = selectedCategory == cat;
GUIStyle btnStyle = new GUIStyle(EditorStyles.miniButton);
if (isSelected) GUI.backgroundColor = new Color(0.3f, 0.6f, 1f);
else if(cat.Contains("Core")) GUI.backgroundColor = new Color(1f, 0.8f, 0.8f);
if (GUILayout.Button(cat, btnStyle, GUILayout.MinWidth(80)))
{
selectedCategory = cat;
searchString = "";
GUI.FocusControl(null);
ApplyFilters();
}
GUI.backgroundColor = Color.white;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void DrawSettings()
{
GUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("Connection Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
jsonListUrl = EditorGUILayout.TextField("JSON List URL", jsonListUrl);
repoToken = EditorGUILayout.PasswordField("Access Token", repoToken);
if (EditorGUI.EndChangeCheck())
{
EditorPrefs.SetString("Shirogin_RepoUrl", jsonListUrl);
EditorPrefs.SetString("Shirogin_RepoToken", repoToken);
}
if (GUILayout.Button("Save Token to Config File"))
{
if (_config == null) _config = ShiroginConfig.Load();
if (_config != null)
{
_config.bitbucketAccessToken = repoToken;
EditorUtility.SetDirty(_config); AssetDatabase.SaveAssets();
}
}
GUILayout.EndVertical();
}
private void DrawStaticStatusBar()
{
Rect bar = GUILayoutUtility.GetRect(position.width, 30);
EditorGUI.DrawRect(bar, new Color(0.1f, 0.2f, 0.1f));
if (_isQueueRunning)
{
EditorGUI.DrawRect(bar, new Color(0.5f, 0.2f, 0.8f));
string qText = _currentQueueItem != null ? $"Batch Installing: {_currentQueueItem.displayName} ({_downloadQueue.Count} left)" : "Batch Processing...";
GUI.Label(bar, qText, new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } });
}
else if (_isInstallingUpm)
{
EditorGUI.DrawRect(bar, new Color(0.2f, 0.4f, 0.8f));
GUI.Label(bar, "Installing Package via Unity Package Manager...", new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } });
}
else if (isWorking && downloadProgress > 0)
{
float w = bar.width * downloadProgress;
EditorGUI.DrawRect(new Rect(bar.x, bar.y, w, bar.height), new Color(0.2f, 0.8f, 0.2f));
GUI.Label(bar, $"{statusMessage} {(downloadProgress * 100):F0}%", new GUIStyle(EditorStyles.boldLabel) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.white } });
}
else
{
GUI.Label(bar, "Ready", new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter, normal = { textColor = Color.gray } });
}
if (isWorking || _isQueueRunning)
{
Rect btnRect = new Rect(bar.width - 85, bar.y + 2, 80, 26);
GUI.backgroundColor = new Color(0.9f, 0.3f, 0.3f);
if (GUI.Button(btnRect, "CANCEL"))
{
AbortDownload();
_downloadQueue.Clear();
_isQueueRunning = false;
}
GUI.backgroundColor = Color.white;
}
}
private void DrawPackageList()
{
if (filteredPackages == null || filteredPackages.Count == 0)
{
GUILayout.Label("No packages found.", EditorStyles.centeredGreyMiniLabel);
return;
}
scrollPos = GUILayout.BeginScrollView(scrollPos);
foreach (var pkg in filteredPackages) DrawPackageItem(pkg);
GUILayout.EndScrollView();
}
private void DrawPackageItem(PackageData pkg)
{
if (pkg.versions == null || pkg.versions.Length == 0) return;
// SETUP MODE LOGIC: Force specific version
int selectedIndex = 0;
bool forceVersion = false;
if (_isSetupMode)
{
for (int i = 0; i < pkg.versions.Length; i++)
{
if (_essentialPackages.Contains(pkg.versions[i].packageName))
{
selectedIndex = i;
forceVersion = true;
break;
}
}
}
else
{
if (!packageVersionSelection.ContainsKey(pkg.uniqueId)) packageVersionSelection[pkg.uniqueId] = 0;
selectedIndex = packageVersionSelection[pkg.uniqueId];
if (selectedIndex >= pkg.versions.Length) selectedIndex = 0;
}
VersionData vData = pkg.versions[selectedIndex];
// Status Logic
bool isInstalled = IsPackageInstalled(pkg, vData);
string installedVer = EditorPrefs.GetString("RepoManager_Version_" + pkg.uniqueId, "");
bool isVersionMatch = isInstalled && (installedVer == vData.version);
// UPM Hack: Assume version match if folder exists because checking UPM version is slow
if (vData.isUpm && isInstalled) isVersionMatch = true;
string cachePath = Path.Combine(Application.temporaryCachePath, $"{pkg.uniqueId}_{vData.version}.unitypackage");
bool isCached = !vData.isUpm && File.Exists(cachePath);
// COLOR LOGIC
Color boxColor = Color.gray;
if (isInstalled)
{
if (isVersionMatch) boxColor = new Color(0.2f, 0.6f, 0.2f); // Green (Correct Version)
else boxColor = new Color(0.2f, 0.5f, 0.9f); // Blue (Wrong Version)
}
else if (isCached)
{
boxColor = new Color(0.2f, 0.8f, 0.8f); // Cyan (Cached)
}
else if (_isSetupMode)
{
boxColor = new Color(0.8f, 0.3f, 0.3f); // Red (Missing in Setup)
}
else
{
boxColor = new Color(0.3f, 0.3f, 0.3f); // Gray (Missing)
}
GUI.backgroundColor = boxColor;
GUILayout.BeginVertical(EditorStyles.helpBox);
GUI.backgroundColor = Color.white;
GUILayout.BeginHorizontal();
string icon = vData.isUpm ? "📦" : (vData.isExternal ? "🌍" : "📦");
GUIStyle labelStyle = new GUIStyle(EditorStyles.boldLabel);
labelStyle.normal.textColor = Color.white;
GUILayout.Label(new GUIContent($" {pkg.displayName}", icon), labelStyle, GUILayout.Width(200));
// Version Selection
if (forceVersion)
{
GUIStyle staticVerStyle = new GUIStyle(EditorStyles.label);
staticVerStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f);
GUILayout.Label($"v{vData.version} (Required)", staticVerStyle, GUILayout.Width(200));
}
else
{
string[] options = pkg.versions.Select(v => v.version).ToArray();
int newIndex = EditorGUILayout.Popup(selectedIndex, options, GUILayout.Width(80));
if (newIndex != selectedIndex) packageVersionSelection[pkg.uniqueId] = newIndex;
}
// Docs
if (!string.IsNullOrEmpty(vData.docsWebUrl))
{
if (GUILayout.Button("🌐", EditorStyles.miniButton, GUILayout.Width(30)))
Application.OpenURL(vData.docsWebUrl);
}
if (!string.IsNullOrEmpty(vData.docsDownloadUrl))
{
if (GUILayout.Button("⬇", EditorStyles.miniButton, GUILayout.Width(30)))
StartDownloadDocs(vData.docsDownloadUrl, pkg.category, pkg.displayName, vData.version);
}
GUILayout.FlexibleSpace();
// Status Label
GUIStyle statusStyle = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleRight, normal = { textColor = Color.white } };
if (isInstalled)
{
string statusText = isVersionMatch ? "✔ Installed" : $"⚠ v{installedVer}";
GUILayout.Label(statusText, statusStyle, GUILayout.Width(80));
}
else if (isCached)
{
GUILayout.Label("⬇ Cached", statusStyle, GUILayout.Width(80));
}
// ACTION BUTTON
EditorGUI.BeginDisabledGroup(isWorking || _isInstallingUpm);
string btnText = "Install";
if (isInstalled)
{
if (isVersionMatch) btnText = "Re-Install";
else btnText = "Update";
}
else if (isCached)
{
btnText = "Import (Cached)";
}
else
{
btnText = "Download";
}
if (GUILayout.Button(btnText, GUILayout.Width(110)))
{
_downloadQueue.Clear();
_isQueueRunning = false;
InstallSpecificPackage(pkg, vData);
}
EditorGUI.EndDisabledGroup();
// Delete / Verify
if (!vData.isUpm)
{
if (isInstalled || isCached)
{
if (GUILayout.Button("🗑", GUILayout.Width(25)))
{
if (isCached) try { File.Delete(cachePath); } catch {}
if (isInstalled) EditorPrefs.DeleteKey("RepoManager_Version_" + pkg.uniqueId);
RefreshFolderCache();
}
}
}
GUILayout.EndHorizontal();
if(!string.IsNullOrEmpty(vData.releaseNotes))
{
GUIStyle noteStyle = new GUIStyle(EditorStyles.miniLabel) { normal = { textColor = new Color(0.9f,0.9f,0.9f) } };
GUILayout.Label(vData.releaseNotes, noteStyle);
}
GUILayout.EndVertical();
}
// --- INSTALL LOGIC ---
private void InstallSpecificPackage(PackageData pkg, VersionData vData)
{
if (vData.isUpm)
{
InstallUpmPackage(vData.upmId, vData.version);
}
else
{
string cachePath = Path.Combine(Application.temporaryCachePath, $"{pkg.uniqueId}_{vData.version}.unitypackage");
if (File.Exists(cachePath)) InstallPackageLocal(cachePath, pkg.uniqueId, vData.version);
else StartDownloadPackage(vData.url, pkg.uniqueId, vData.version);
}
}
// --- QUEUE ---
private void StartDownloadAllQueue()
{
if (filteredPackages == null || filteredPackages.Count == 0) return;
_downloadQueue.Clear();
foreach (var pkg in filteredPackages)
{
VersionData targetVer = null;
// Setup Mode: Find exact version
if (_isSetupMode)
{
targetVer = pkg.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName));
}
else
{
// Normal Mode: Selected version
int index = 0;
if(packageVersionSelection.ContainsKey(pkg.uniqueId)) index = packageVersionSelection[pkg.uniqueId];
if(index < pkg.versions.Length) targetVer = pkg.versions[index];
}
if (targetVer != null && !IsPackageInstalled(pkg, targetVer))
{
_downloadQueue.Enqueue(pkg);
}
}
if (_downloadQueue.Count > 0)
{
_isQueueRunning = true;
ProcessNextQueueItem();
}
else
{
EditorUtility.DisplayDialog("Setup Wizard", "All required packages are already installed!", "OK");
}
}
private void ProcessNextQueueItem()
{
if (!_isQueueRunning) return;
if (_downloadQueue.Count > 0)
{
_currentQueueItem = _downloadQueue.Dequeue();
// Determine Version again
VersionData vData = null;
if (_isSetupMode)
{
vData = _currentQueueItem.versions.FirstOrDefault(v => _essentialPackages.Contains(v.packageName));
}
else
{
int index = 0;
if(packageVersionSelection.ContainsKey(_currentQueueItem.uniqueId)) index = packageVersionSelection[_currentQueueItem.uniqueId];
vData = _currentQueueItem.versions[index];
}
if (vData != null)
{
Debug.Log($"[Queue] Processing: {_currentQueueItem.displayName} ({vData.version})");
InstallSpecificPackage(_currentQueueItem, vData);
}
else
{
ProcessNextQueueItem(); // Skip if invalid
}
}
else
{
_isQueueRunning = false;
_currentQueueItem = null;
EditorUtility.DisplayDialog("Setup Wizard", "All packages have been installed!", "OK");
}
}
// --- HELPERS ---
private bool IsPackageInstalled(PackageData pkg, VersionData vData)
{
if (vData.isUpm && !string.IsNullOrEmpty(vData.upmId))
{
string upmPath = Path.Combine("Packages", vData.upmId);
if (AssetDatabase.IsValidFolder(upmPath)) return true;
string manifestPath = Path.Combine(Application.dataPath, "..", "Packages", "manifest.json");
if (File.Exists(manifestPath))
{
string content = File.ReadAllText(manifestPath);
if (content.Contains($"\"{vData.upmId}\"")) return true;
}
return false;
}
bool folderFound = IsPackageFolderFound(pkg);
string storedVer = EditorPrefs.GetString("RepoManager_Version_" + pkg.uniqueId, "");
return folderFound || !string.IsNullOrEmpty(storedVer);
}
private void InstallUpmPackage(string packageId, string version)
{
_isInstallingUpm = true;
string id = string.IsNullOrEmpty(version) ? packageId : $"{packageId}@{version}";
Debug.Log($"[Asset Manager] Requesting UPM Install: {id}");
_upmAddRequest = Client.Add(id);
}
private bool IsPackageFolderFound(PackageData pkg)
{
string simpleName = pkg.displayName.Replace(" SDK", "").Replace(" ", "").ToLower();
if (existingFolderNames.Contains(simpleName)) return true;
if (existingFolderNames.Contains(pkg.displayName.ToLower())) return true;
if (_rootPackages.Contains(pkg.displayName) && existingFolderNames.Contains(pkg.displayName.ToLower())) return true;
return false;
}
private void FetchPackageList()
{
if (isWorking) return;
StartWebOperation(jsonListUrl, OnJsonListDownloaded, null);
}
private void StartDownloadPackage(string url, string pkgId, string version)
{
if (isWorking) return;
string fileName = $"{pkgId}_{version}.unitypackage";
string tempPath = Path.Combine(Application.temporaryCachePath, fileName);
StartWebOperation(url, (data) => InstallPackageLocal(tempPath, pkgId, version), tempPath);
}
private void StartDownloadDocs(string url, string category, string pkgName, string version)
{
if (isWorking) return;
string safeName = string.Join("_", pkgName.Split(Path.GetInvalidFileNameChars()));
string docsRoot = Path.Combine(Application.dataPath, "ShiroginSDK", "ThirdPartySDKs");
string targetFolder = Path.Combine(docsRoot, category, safeName);
if (!Directory.Exists(targetFolder)) Directory.CreateDirectory(targetFolder);
string targetPath = Path.Combine(targetFolder, $"{safeName}_{version}.md");
StartWebOperation(url, (data) =>
{
if (data != null && data.Length > 0)
{
File.WriteAllBytes(targetPath, data);
EditorUtility.DisplayDialog("Download Complete", $"Documentation saved to:\n{targetPath}", "OK");
string relPath = "Assets" + targetPath.Substring(Application.dataPath.Length);
AssetDatabase.ImportAsset(relPath);
var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(relPath);
EditorGUIUtility.PingObject(obj);
}
}, targetPath);
}
private void InstallPackageLocal(string path, string pkgId, string version)
{
if (!File.Exists(path)) return;
AssetDatabase.ImportPackage(path, true);
EditorPrefs.SetString("RepoManager_Version_" + pkgId, version);
RefreshFolderCache();
Repaint();
if (_isQueueRunning) ProcessNextQueueItem();
}
private void AbortDownload()
{
if (currentRequest != null) { currentRequest.Abort(); currentRequest.Dispose(); currentRequest = null; }
if (downloadDelegate != null) { EditorApplication.update -= downloadDelegate; downloadDelegate = null; }
isWorking = false; statusMessage = "Waiting..."; downloadProgress = 0f; Repaint();
}
private void StartWebOperation(string url, Action<byte[]> onComplete, string savePath = null)
{
AbortDownload(); isWorking = true; statusMessage = "Connecting..."; downloadProgress = 0f;
downloadDelegate = () => WebRequestRoutine(url, savePath, onComplete);
EditorApplication.update += downloadDelegate;
}
private void WebRequestRoutine(string url, string savePath, Action<byte[]> onComplete)
{
if (currentRequest == null)
{
currentRequest = UnityWebRequest.Get(url);
if (url.Contains("bitbucket.org") && !string.IsNullOrEmpty(repoToken))
currentRequest.SetRequestHeader("Authorization", "Bearer " + repoToken);
if (!string.IsNullOrEmpty(savePath))
currentRequest.downloadHandler = new DownloadHandlerFile(savePath);
else
currentRequest.downloadHandler = new DownloadHandlerBuffer();
currentRequest.SendWebRequest();
}
if (!currentRequest.isDone)
{
downloadProgress = currentRequest.downloadProgress;
statusMessage = "Downloading...";
Repaint();
return;
}
bool success = currentRequest.result == UnityWebRequest.Result.Success;
byte[] data = null;
if (success)
{
if (string.IsNullOrEmpty(savePath) && currentRequest.downloadHandler is DownloadHandlerBuffer)
{
data = currentRequest.downloadHandler.data;
}
if(!string.IsNullOrEmpty(savePath)) data = new byte[1];
}
else if (currentRequest.error != "Request aborted")
{
Debug.LogError($"Network Error: {currentRequest.error}");
EditorUtility.DisplayDialog("Download Failed", currentRequest.error, "OK");
if (!string.IsNullOrEmpty(savePath) && File.Exists(savePath)) try { File.Delete(savePath); } catch {}
}
currentRequest.Dispose(); currentRequest = null;
EditorApplication.update -= downloadDelegate; downloadDelegate = null;
isWorking = false; Repaint();
if(success) onComplete?.Invoke(data);
}
private void OnJsonListDownloaded(byte[] data)
{
if (data == null) return;
try
{
PackageManifest manifest = JsonUtility.FromJson<PackageManifest>(System.Text.Encoding.UTF8.GetString(data));
if (manifest != null && manifest.packages != null)
{
allPackages = new List<PackageData>(manifest.packages);
var catList = new HashSet<string> { "All" };
foreach (var p in allPackages) if (!string.IsNullOrEmpty(p.category)) catList.Add(p.category);
categories = catList.OrderBy(c => c).ToArray();
ApplyFilters();
Repaint();
}
}
catch (Exception e) { Debug.LogError("JSON Error: " + e.Message); }
}
private void ApplyFilters()
{
if (allPackages == null) return;
if (_isSetupMode)
{
// Check against packageName instead of uniqueId
filteredPackages = allPackages.Where(p =>
p.versions.Any(v => _essentialPackages.Contains(v.packageName))
).ToList();
}
else
{
filteredPackages = allPackages.Where(p =>
{
bool catMatch = selectedCategory == "All" || p.category == selectedCategory;
bool searchMatch = string.IsNullOrEmpty(searchString) || p.displayName.ToLower().Contains(searchString.ToLower());
return catMatch && searchMatch;
}).ToList();
}
}
private void ClearCache()
{
string path = Application.temporaryCachePath;
var info = new DirectoryInfo(path);
foreach (var f in info.GetFiles("*.unitypackage")) f.Delete();
Debug.Log("Cache cleared.");
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e877a2529afa4573908befa02302e0dc
timeCreated: 1765485911

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f83da24293a44111ad4261ebd6abea37
timeCreated: 1765666221

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a8fc14d0843046eab7bada6257bf087d
timeCreated: 1765818013

View file

@ -0,0 +1,99 @@
using System.Reflection;
using ShiroginSDK.Editor.Bitbucket;
using ShiroginSDK.Runtime.Core.SDK;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.Core.SDK
{
[CustomEditor(typeof(ShiroginConfig))]
public class ShiroginConfigEditor : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
GUILayout.Space(20);
GUILayout.Label("SDK Compilation Control", EditorStyles.boldLabel);
// Apply Button
GUI.backgroundColor = new Color(0.2f, 0.8f, 0.2f);
if (GUILayout.Button("✅ Apply Changes & Compile", GUILayout.Height(40)))
{
bool confirm = EditorUtility.DisplayDialog("Apply SDK Defines?",
"This will trigger a script recompilation.\n\n" +
"Ensure you have downloaded the necessary packages (Firebase, AppLovin, etc.) " +
"via the Asset Manager BEFORE enabling them.\n\n" +
"If code breaks, use 'Tools > ShiroginSDK > Force Reset Symbols'.",
"Yes, Compile", "Cancel");
if (confirm)
{
ShiroginDefinesManager.UpdateDefines();
}
}
GUI.backgroundColor = Color.white;
GUILayout.Space(10);
GUIStyle bigButtonStyle = new GUIStyle(GUI.skin.button);
bigButtonStyle.fontSize = 14;
bigButtonStyle.fontStyle = FontStyle.Bold;
bigButtonStyle.fixedHeight = 40;
bigButtonStyle.normal.textColor = Color.white;
GUI.backgroundColor = new Color(0.2f, 0.6f, 1f);
if (GUILayout.Button("OPEN ASSET MANAGER", bigButtonStyle))
{
AssetRegistryEditor.ShowWindow();
}
GUI.backgroundColor = Color.white;
GUILayout.Space(5);
GUILayout.Label("Click above to manage SDK packages.", EditorStyles.centeredGreyMiniLabel);
}
public static class ShiroginConfigInspectorOpener
{
[MenuItem("ShiroginSDK/Config")]
public static void OpenConfigInNewLockedInspector()
{
string[] guids = AssetDatabase.FindAssets("t:ShiroginConfig");
if (guids.Length == 0)
{
Debug.LogError("ShiroginConfig asset not found.");
return;
}
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
var config = AssetDatabase.LoadAssetAtPath<Object>(path);
var inspectorType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InspectorWindow");
var before = Resources.FindObjectsOfTypeAll(inspectorType).Length;
Selection.activeObject = config;
EditorApplication.ExecuteMenuItem("Window/General/Inspector");
EditorApplication.delayCall += () =>
{
var inspectors = Resources.FindObjectsOfTypeAll(inspectorType);
if (inspectors.Length <= before)
return;
// Yeni açılan inspector = son eklenen
var newInspector = inspectors[inspectors.Length - 1];
var isLockedProp = inspectorType.GetProperty(
"isLocked",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
);
isLockedProp?.SetValue(newInspector, true);
};
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b4044634796944508551efb45ce358de
timeCreated: 1765818022

View file

@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Linq;
using ShiroginSDK.Runtime.Core.SDK;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.Core
{
public static class ShiroginDefinesManager
{
// Define Symbols
private const string DEFINE_SDK = "SHIROGIN_SDK";
private const string DEFINE_IAP = "SHIROGIN_IAP";
private const string DEFINE_AD_SERVICE = "SHIROGIN_AD_SERVICE";
private const string DEFINE_ANALYTICS = "SHIROGIN_ANALYTICS";
private const string DEFINE_FIREBASE_ANALYTICS = "SHIROGIN_FIREBASE_ANALYTICS";
private const string DEFINE_FIREBASE_REMOTE_CONFIG = "SHIROGIN_FIREBASE_REMOTE_CONFIG";
private const string DEFINE_APPSFLYER = "SHIROGIN_APPSFLYER";
private const string DEFINE_FACEBOOK = "SHIROGIN_FACEBOOK";
/// <summary>
/// Updates scripting define symbols based on ShiroginConfig settings.
/// Call this manually from the Config Editor.
/// </summary>
public static void UpdateDefines()
{
var config = ShiroginConfig.Load();
if (config == null)
{
Debug.LogError("ShiroginConfig not found! Cannot update defines.");
return;
}
var activeDefines = new List<string>();
// --- Enable Flags ---
if (config.enableSdk)
activeDefines.Add(DEFINE_SDK);
if (config.enableIAP)
activeDefines.Add(DEFINE_IAP);
if (config.enableAdService)
activeDefines.Add(DEFINE_AD_SERVICE);
if (config.enableAnalytics)
activeDefines.Add(DEFINE_ANALYTICS);
if (config.enableFirebaseAnalytics)
activeDefines.Add(DEFINE_FIREBASE_ANALYTICS);
if (config.enableFirebaseRemoteConfig)
activeDefines.Add(DEFINE_FIREBASE_REMOTE_CONFIG);
if (config.enableAppsFlyerAnalytics)
activeDefines.Add(DEFINE_APPSFLYER);
if (config.enableFacebook)
activeDefines.Add(DEFINE_FACEBOOK);
SetDefines(activeDefines);
}
/// <summary>
/// Emergency tool to clear all SDK symbols if compilation breaks.
/// Access via: Tools -> ShiroginSDK -> Force Reset Symbols
/// </summary>
[MenuItem("ShiroginSDK/🔧 Force Reset Symbols (Fix Errors)", false, 99)]
public static void ForceReset()
{
SetDefines(new List<string>());
Debug.Log("ShiroginSDK: All symbols cleared. You should be able to compile now.");
}
private static void SetDefines(List<string> targetDefines)
{
var targetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
if (targetGroup == BuildTargetGroup.Unknown) return;
string definesString = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup);
var allDefines = definesString.Split(';').ToList();
bool changed = false;
var managedDefines = new List<string>
{
DEFINE_SDK,
DEFINE_IAP,
DEFINE_AD_SERVICE,
DEFINE_ANALYTICS,
DEFINE_FIREBASE_ANALYTICS,
DEFINE_FIREBASE_REMOTE_CONFIG,
DEFINE_APPSFLYER,
DEFINE_FACEBOOK
};
// Add required
foreach (var define in targetDefines)
{
if (!allDefines.Contains(define))
{
allDefines.Add(define);
changed = true;
}
}
// Remove unused
foreach (var define in managedDefines)
{
if (!targetDefines.Contains(define) && allDefines.Contains(define))
{
allDefines.Remove(define);
changed = true;
}
}
if (changed)
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, string.Join(";", allDefines));
#if UNITY_2020_1_OR_NEWER
UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
#endif
Debug.Log($"[ShiroginSDK] 🔄 Defines Updated: {string.Join(", ", targetDefines)}");
}
else
{
Debug.Log("[ShiroginSDK] No changes in defines.");
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c16173692394a179aa515a4773768f4
timeCreated: 1765666256

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 323e6e9bf4835764eb53fb63261d6d3e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,28 @@
using System.Collections.Generic;
using ShiroginSDK.Runtime.Core.SDK;
using ShiroginSDK.Runtime.Shared.Constants;
using UnityEngine;
namespace ShiroginSDK.Editor.IAP
{
public static class IAPNameProvider
{
/// <summary>
/// Returns all productIds from the active StoreRepository in SDKConfig.
/// </summary>
public static IEnumerable<string> GetIAPNames()
{
var config = Resources.Load<ShiroginConfig>(SDKConstants.ConfigResourcePath);
if (config == null || config.storeRepository == null) return new List<string> { "⚠ No Repository Found" };
var list = new List<string>();
var allItems = config.storeRepository.GetAll(); // ✅
foreach (var item in allItems)
if (item != null && !string.IsNullOrEmpty(item.productId))
list.Add(item.productId);
return list;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc886bf85ac14aed80a646e4abb126e4
timeCreated: 1759412104

View file

@ -0,0 +1,39 @@
#if UNITY_EDITOR
using System;
using ShiroginSDK.Runtime.Core.Editor.Attributes;
using ShiroginSDK.Runtime.Core.SDK;
using ShiroginSDK.Runtime.Shared.Constants;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.IAP
{
[CustomPropertyDrawer(typeof(IAPProductDropdownAttribute))]
public class IAPProductDropdownDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.propertyType != SerializedPropertyType.String)
{
EditorGUI.LabelField(position, label.text, "Use with string only.");
return;
}
var config = Resources.Load<ShiroginConfig>(SDKConstants.ConfigResourcePath);
if (config == null || config.storeRepository == null)
{
EditorGUI.PropertyField(position, property, label);
return;
}
var items = config.storeRepository.GetAll();
var options = new string[items.Count];
for (var i = 0; i < items.Count; i++) options[i] = items[i] != null ? items[i].productId : "<null>";
var index = Mathf.Max(0, Array.IndexOf(options, property.stringValue));
index = EditorGUI.Popup(position, label.text, index, options);
if (index >= 0 && index < options.Length) property.stringValue = options[index];
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 328d5079597b43289c6892ef6d86cf8b
timeCreated: 1759412301

View file

@ -0,0 +1,182 @@
// #if UNITY_EDITOR
// using UnityEditor;
// using UnityEngine;
// using UnityEngine.UIElements;
// using ShiroginSDK.Runtime.IAP.SO;
// using ShiroginSDK.Runtime.IAP.Enums;
// using System.IO;
// using System.Collections.Generic;
//
// namespace ShiroginSDK.Editor.IAP
// {
// [CustomEditor(typeof(StoreItem))]
// public class StoreItemEditor : UnityEditor.Editor
// {
// private const string SaveFolder = "Assets/ShiroginSDK/Generated/StoreItemIcons";
// private const string JsonPath = "Assets/ShiroginSDK/Generated/storeitemeditor.json";
//
// [System.Serializable]
// private class IconData { public string itemPath; public string iconPath; }
//
// [System.Serializable]
// private class IconDataList { public List<IconData> list = new(); }
//
// public override VisualElement CreateInspectorGUI()
// {
// var root = new VisualElement { style = { paddingTop = 6, paddingBottom = 8 } };
// var item = (StoreItem)target;
//
// // Default inspector (IMGUIContainer ile izole)
// var defaultInspector = new IMGUIContainer(() => DrawDefaultInspector());
// root.Add(defaultInspector);
//
// // Separator
// var line = new VisualElement();
// line.style.height = 1;
// line.style.marginTop = 8;
// line.style.marginBottom = 8;
// line.style.backgroundColor = new Color(0.25f, 0.25f, 0.25f, 0.6f);
// root.Add(line);
//
// // Box container
// var box = new VisualElement();
// box.style.borderTopLeftRadius = 6;
// box.style.borderTopRightRadius = 6;
// box.style.borderBottomLeftRadius = 6;
// box.style.borderBottomRightRadius = 6;
// box.style.paddingTop = 8;
// box.style.paddingBottom = 8;
// box.style.paddingLeft = 10;
// box.style.paddingRight = 10;
// box.style.backgroundColor = new Color(0.12f, 0.12f, 0.12f, 0.6f);
// root.Add(box);
//
// var header = new Label("🧩 Shirogin IAP Tools");
// header.style.unityFontStyleAndWeight = FontStyle.Bold;
// header.style.marginBottom = 6;
// box.Add(header);
//
// if (item.icon == null)
// {
// var help = new HelpBox("Please assign an icon sprite before applying.", HelpBoxMessageType.Warning);
// box.Add(help);
// }
//
// var button = new Button(() => GenerateAndSave(item))
// {
// text = "🖼 Generate & Save Icon",
// style =
// {
// height = 28,
// unityTextAlign = TextAnchor.MiddleCenter,
// marginTop = 4
// }
// };
// box.Add(button);
//
// return root;
// }
//
// private void GenerateAndSave(StoreItem item)
// {
// Directory.CreateDirectory(SaveFolder);
//
// Texture2D baseTex = AssetPreview.GetAssetPreview(item.icon);
// if (baseTex == null)
// {
// Debug.LogWarning("[ShiroginSDK] Couldn't create preview texture.");
// return;
// }
//
// string category = item.category.ToString();
// Texture2D label = Resources.Load<Texture2D>($"Shirogin/IAP_Labels/{category}") ??
// Resources.Load<Texture2D>("Shirogin/IAP_Labels/Unknown");
//
// Texture2D final = new Texture2D(baseTex.width, baseTex.height, TextureFormat.RGBA32, false);
// Graphics.CopyTexture(baseTex, final);
// if (label != null) Overlay(final, label);
// final.Apply();
//
// string savePath = $"{SaveFolder}/{item.name}_Icon.png";
// File.WriteAllBytes(savePath, final.EncodeToPNG());
// AssetDatabase.ImportAsset(savePath);
// AssetDatabase.Refresh();
//
// Texture2D savedTex = AssetDatabase.LoadAssetAtPath<Texture2D>(savePath);
// if (savedTex != null)
// EditorGUIUtility.SetIconForObject(item, savedTex);
//
// EditorUtility.SetDirty(item);
// AssetDatabase.SaveAssets();
// SaveJson(AssetDatabase.GetAssetPath(item), savePath);
// Debug.Log($"[ShiroginSDK] Icon saved → {savePath}");
// }
//
// private void Overlay(Texture2D baseTex, Texture2D labelTex)
// {
// int w = baseTex.width;
// int h = baseTex.height;
// int lh = Mathf.RoundToInt(h * 0.2f);
// Texture2D scaled = Scale(labelTex, w, lh);
//
// for (int y = 0; y < lh; y++)
// {
// for (int x = 0; x < w; x++)
// {
// Color a = baseTex.GetPixel(x, h - lh + y);
// Color b = scaled.GetPixel(x, y);
// baseTex.SetPixel(x, h - lh + y, Color.Lerp(a, b, b.a));
// }
// }
// }
//
// private Texture2D Scale(Texture2D src, int w, int h)
// {
// RenderTexture rt = RenderTexture.GetTemporary(w, h);
// Graphics.Blit(src, rt);
// RenderTexture.active = rt;
// Texture2D result = new Texture2D(w, h, TextureFormat.RGBA32, false);
// result.ReadPixels(new Rect(0, 0, w, h), 0, 0);
// result.Apply();
// RenderTexture.ReleaseTemporary(rt);
// RenderTexture.active = null;
// return result;
// }
//
// private void SaveJson(string itemPath, string iconPath)
// {
// IconDataList data = File.Exists(JsonPath)
// ? JsonUtility.FromJson<IconDataList>(File.ReadAllText(JsonPath))
// : new IconDataList();
//
// data.list.RemoveAll(x => x.itemPath == itemPath);
// data.list.Add(new IconData { itemPath = itemPath, iconPath = iconPath });
//
// File.WriteAllText(JsonPath, JsonUtility.ToJson(data, true));
// AssetDatabase.Refresh();
// }
//
// [InitializeOnLoadMethod]
// private static void RestoreIconsOnLoad()
// {
// EditorApplication.delayCall += () =>
// {
// if (!File.Exists(JsonPath)) return;
// var data = JsonUtility.FromJson<IconDataList>(File.ReadAllText(JsonPath));
// if (data == null || data.list.Count == 0) return;
//
// foreach (var entry in data.list)
// {
// var item = AssetDatabase.LoadAssetAtPath<StoreItem>(entry.itemPath);
// var icon = AssetDatabase.LoadAssetAtPath<Texture2D>(entry.iconPath);
// if (item != null && icon != null)
// EditorGUIUtility.SetIconForObject(item, icon);
// }
//
// Debug.Log($"[ShiroginSDK] Restored {data.list.Count} StoreItem icons (Unity 6 compatible).");
// };
// }
// }
// }
// #endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fbea0c77393e48038e5bbb46246e38e3
timeCreated: 1760535014

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 83a5dfe9c55d4ea38699ffad6bf306bd
timeCreated: 1760346182

View file

@ -0,0 +1,72 @@
#if UNITY_EDITOR
using ShiroginSDK.Runtime.Modules.RemoteConfig.Scripts.SO;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.RemoteConfig
{
[CustomPropertyDrawer(typeof(RemoteConfigDefinition.Entry))]
public class EntryDrawer : PropertyDrawer
{
private static readonly Color StringColor = new(0.55f, 0.9f, 0.6f);
private static readonly Color NumberColor = new(0.55f, 0.75f, 1f);
private static readonly Color BoolColor = new(1f, 0.85f, 0.55f);
private static readonly Color BorderColor = new(0.35f, 0.35f, 0.35f);
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 3 * EditorGUIUtility.singleLineHeight + 24f;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
var typeProp = property.FindPropertyRelative("Type");
var valueType = (RemoteConfigDefinition.ValueType)typeProp.enumValueIndex;
var bgColor = valueType switch
{
RemoteConfigDefinition.ValueType.String => StringColor,
RemoteConfigDefinition.ValueType.Number => NumberColor,
RemoteConfigDefinition.ValueType.Boolean => BoolColor,
_ => Color.gray
};
var boxRect = new Rect(position.x + 4, position.y + 2, position.width - 8, position.height - 4);
EditorGUI.DrawRect(boxRect, new Color(bgColor.r, bgColor.g, bgColor.b, 0.25f));
Handles.color = BorderColor;
Handles.DrawAAPolyLine(1.5f, new Vector3(boxRect.x, boxRect.yMax), new Vector3(boxRect.xMax, boxRect.yMax));
var y = boxRect.y + 4;
var lh = EditorGUIUtility.singleLineHeight + 6;
EditorGUI.PropertyField(new Rect(boxRect.x + 6, y, boxRect.width - 12, lh - 4),
property.FindPropertyRelative("Key"), new GUIContent("🔑 Key"));
y += lh;
EditorGUI.PropertyField(new Rect(boxRect.x + 6, y, boxRect.width - 12, lh - 4),
typeProp, new GUIContent("🎛️ Type"));
y += lh;
switch (valueType)
{
case RemoteConfigDefinition.ValueType.String:
EditorGUI.PropertyField(new Rect(boxRect.x + 6, y, boxRect.width - 12, lh - 4),
property.FindPropertyRelative("StringValue"), new GUIContent("🧩 String Value"));
break;
case RemoteConfigDefinition.ValueType.Number:
EditorGUI.PropertyField(new Rect(boxRect.x + 6, y, boxRect.width - 12, lh - 4),
property.FindPropertyRelative("NumberValue"), new GUIContent("🔢 Number Value"));
break;
case RemoteConfigDefinition.ValueType.Boolean:
EditorGUI.PropertyField(new Rect(boxRect.x + 6, y, boxRect.width - 12, lh - 4),
property.FindPropertyRelative("BoolValue"), new GUIContent("✅ Boolean Value"));
break;
}
EditorGUI.EndProperty();
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c1d17a52bda042929c7ef52153edfd98
timeCreated: 1760348378

View file

@ -0,0 +1,254 @@
#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ShiroginSDK.Runtime.Core.SDK;
using ShiroginSDK.Runtime.Modules.Data.Scripts;
using ShiroginSDK.Runtime.Modules.RemoteConfig.Scripts.SO;
using ShiroginSDK.Runtime.Shared.Constants;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor.RemoteConfig
{
[CustomEditor(typeof(RemoteConfigDefinition))]
public class RemoteConfigDefinitionEditor : UnityEditor.Editor
{
private bool showRemotePreview;
public override void OnInspectorGUI()
{
var def = (RemoteConfigDefinition)target;
// Auto sync group name with SDKConfig
var sdkConfig = Resources.Load<ShiroginConfig>(SDKConstants.ConfigResourcePath);
if (sdkConfig != null && def.GroupName != sdkConfig.selectedRemoteGroup)
{
def.SetGroupName(sdkConfig.selectedRemoteGroup);
EditorUtility.SetDirty(def);
}
EditorGUILayout.Space(6);
EditorGUILayout.LabelField("Remote Config Info", EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.TextField("Group Name", def.GroupName);
}
EditorGUILayout.Space(8);
DrawDefaultInspector();
EditorGUILayout.Space(8);
EditorGUILayout.LabelField("Remote Config Tools", EditorStyles.boldLabel);
using (new EditorGUILayout.HorizontalScope())
{
if (GUILayout.Button("✅ Validate Keys"))
{
if (def.ValidateKeys(out var err))
EditorUtility.DisplayDialog("Validation", "All keys valid ✅", "OK");
else
EditorUtility.DisplayDialog("Validation Failed", err, "OK");
}
if (GUILayout.Button("🔤 Sort by Key"))
{
Undo.RecordObject(def, "Sort Remote Config Entries");
def.Entries = def.Entries.OrderBy(e => e.Key).ToList();
EditorUtility.SetDirty(def);
}
}
if (GUILayout.Button("📋 Copy JSON to Clipboard"))
{
if (!def.ValidateKeys(out var err))
{
EditorUtility.DisplayDialog("Error", err, "OK");
return;
}
var json = def.GenerateJson();
EditorGUIUtility.systemCopyBuffer = json;
Debug.Log($"[RemoteConfig] JSON copied to clipboard ({json.Length} chars)");
EditorUtility.DisplayDialog("Success", "JSON copied to clipboard ✅", "OK");
}
// 🔸 Import button
if (GUILayout.Button("⬆️ Import from RemoteConfigData")) ImportFromRemoteConfigData(def);
// 🌐 Remote Data Preview (Cached)
EditorGUILayout.Space(12);
showRemotePreview = EditorGUILayout.Foldout(showRemotePreview, "🌐 Remote Data Preview (Cached)", true);
if (showRemotePreview)
{
if (GUILayout.Button("🔄 Refresh from Cache"))
{
def.RefreshFromCache();
Debug.Log("[RemoteConfigDefinition] 🔄 Refreshed from cached RemoteConfigData.");
}
if (def.RemoteEntries == null || def.RemoteEntries.Count == 0)
{
EditorGUILayout.HelpBox("No cached remote data found.", MessageType.Info);
}
else
{
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("Cached Entries:", EditorStyles.boldLabel);
EditorGUILayout.BeginVertical("box");
foreach (var entry in def.RemoteEntries)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(entry.Key, GUILayout.Width(160));
switch (entry.Type)
{
case RemoteConfigDefinition.ValueType.Boolean:
EditorGUILayout.Toggle(entry.BoolValue);
break;
case RemoteConfigDefinition.ValueType.Number:
EditorGUILayout.LabelField(entry.NumberValue.ToString(CultureInfo.InvariantCulture));
break;
default:
EditorGUILayout.LabelField(entry.StringValue);
break;
}
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndVertical();
}
}
if (GUI.changed)
EditorUtility.SetDirty(def);
}
/// <summary>
/// Imports instance fields from RemoteConfigData as RemoteConfigDefinition entries.
/// Works with BaseData-based RemoteConfigData (non-static).
/// </summary>
private void ImportFromRemoteConfigData(RemoteConfigDefinition def)
{
// Onay penceresi
var proceed = EditorUtility.DisplayDialog(
"Confirm Import",
"Are you sure you want to import all fields from RemoteConfigData?\n\nThis will overwrite current entries.",
"Yes, Import",
"Cancel"
);
if (!proceed)
{
Debug.Log("[RemoteConfigDefinitionEditor] 🚫 Import canceled by user.");
return;
}
Undo.RecordObject(def, "Import from RemoteConfigData");
def.Entries.Clear();
var remoteDataType = typeof(RemoteConfigData);
// Public INSTANCE fields (BaseData yapısıyla uyumlu)
var fields = remoteDataType.GetFields(BindingFlags.Public |
BindingFlags.Instance);
if (fields == null || fields.Length == 0)
{
Debug.LogWarning(
"[RemoteConfigDefinitionEditor] ⚠️ RemoteConfigData içinde public instance field bulunamadı.");
return;
}
// Geçici örnek yarat (BaseData default değerlerini okumak için)
var instance = Activator.CreateInstance(remoteDataType);
var ci = CultureInfo.InvariantCulture;
foreach (var f in fields)
{
if (f.IsLiteral || f.IsInitOnly) continue; // const/readonly atla
var entry = new RemoteConfigDefinition.Entry();
entry.Key = ToSnakeCasePreserveUnderscores(f.Name); // 🔸 snake_case + '_' korunur
var val = f.GetValue(instance);
if (val == null)
{
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Empty;
}
else if (val is bool b)
{
entry.Type = RemoteConfigDefinition.ValueType.Boolean;
entry.BoolValue = b;
}
else if (val is int i)
{
entry.Type = RemoteConfigDefinition.ValueType.Number;
entry.NumberValue = i;
}
else if (val is float fl)
{
entry.Type = RemoteConfigDefinition.ValueType.Number;
entry.NumberValue = fl;
}
else if (val is Array arr)
{
// Dizileri "1,1.4,2" şeklinde string'e çevir
var values = arr.Cast<object>().Select(x => Convert.ToString(x, ci)).ToArray();
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Join(",", values);
}
else if (val is IEnumerable list && !(val is string))
{
// Listeleri string'e çevir
var values = new List<string>();
foreach (var item in list)
values.Add(Convert.ToString(item, ci));
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = string.Join(",", values);
}
else
{
entry.Type = RemoteConfigDefinition.ValueType.String;
entry.StringValue = val.ToString();
}
def.Entries.Add(entry);
}
def.Entries = def.Entries.OrderBy(e => e.Key).ToList();
EditorUtility.SetDirty(def);
Debug.Log($"[RemoteConfigDefinitionEditor] ✅ Imported {def.Entries.Count} entries from RemoteConfigData.");
EditorUtility.DisplayDialog("Import Completed",
$"Successfully imported {def.Entries.Count} entries from RemoteConfigData.", "OK");
}
// Pascal/camelCase → snake_case, mevcut '_' karakterlerini KORUR
// Örn: "TargetFps" -> "target_fps", "Enemy_HpMultiplier" -> "enemy_hp_multiplier"
private static string ToSnakeCasePreserveUnderscores(string input)
{
if (string.IsNullOrEmpty(input)) return input;
// Büyük harf grupları ve geçişleri doğru bölmek için regex
// (mevcut '_' karakterlerine dokunmuyoruz)
var s = Regex.Replace(input, "([A-Z]+)([A-Z][a-z])", "$1_$2");
s = Regex.Replace(s, "([a-z0-9])([A-Z])", "$1_$2");
// Çift alt çizgileri tek'e indir (varsa)
while (s.Contains("__")) s = s.Replace("__", "_");
return s.ToLowerInvariant();
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a7f932636f994e74a05bd163c0d62dc8
timeCreated: 1760346189

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 81cfc5cc491b31c47a3b391a5f090c1d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 88388748fbd09b24eada8e3dbdc574a8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0268502886f1ef24591dbc7b41930bf1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 4529796387140d847b0a13f3623fe0c9
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5c9c7b49e4716a44ab88294e4a7138dc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 07e56e8de2b1f1e4684c80e7ddf8dd77
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 0804f6ef69f8f0b4e8d1963c1865e109
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 8e40403edd8c8ae4da2bed8ed60c293f
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: c8a83af7ebfad3e4ab67ae882f6f2f80
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 32968a781eeaead4e9d1fb5363ac17ee
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 100
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 4ddcf1578be1f79429b66f2a7a8108ff
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 4c21dbdfeb306694cade94ee94012095
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

View file

@ -0,0 +1,156 @@
fileFormatVersion: 2
guid: 15798e41d3533de4ba4816a5f6242982
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 4
buildTarget: DefaultTexturePlatform
maxTextureSize: 256
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 0
crunchedCompression: 1
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 4
buildTarget: iOS
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
customData:
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
{
"name": "ShiroginSDK.Editor",
"rootNamespace": "",
"references": [
"Unity.TextMeshPro",
"ShiroginSDK.Runtime"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: be300420007eaf54aacf878fb13463a8
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,35 @@
#if UNITY_EDITOR
using ShiroginSDK.Runtime.Core.Editor.Attributes;
using UnityEditor;
using UnityEngine;
namespace ShiroginSDK.Editor
{
[CustomPropertyDrawer(typeof(SubHeaderAttribute))]
public class SubHeaderDrawer : DecoratorDrawer
{
public override float GetHeight()
{
var subHeader = (SubHeaderAttribute)attribute;
return EditorGUIUtility.singleLineHeight + subHeader.spaceAbove;
}
public override void OnGUI(Rect position)
{
var subHeader = (SubHeaderAttribute)attribute;
// Adjust vertical offset
position.y += subHeader.spaceAbove;
position.height = EditorGUIUtility.singleLineHeight;
// Prevent null or empty label text from crashing Unity 6 UIElements backend
var headerText = subHeader.header ?? string.Empty;
if (string.IsNullOrWhiteSpace(headerText))
return;
// Draw the label safely
EditorGUI.LabelField(position, headerText, EditorStyles.boldLabel);
}
}
}
#endif

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38daa8b300134e2580f2eb4e6159d2ae
timeCreated: 1759310055

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5bc4b28b77644a1091082bb220294f10
timeCreated: 1765824754

View file

@ -0,0 +1,335 @@
using UnityEngine;
using UnityEditor;
using ShiroginSDK.Editor.Bitbucket;
using ShiroginSDK.Runtime.Core.SDK; // Config için
using ShiroginSDK.Runtime.Shared.Constants;
namespace ShiroginSDK.Editor.Windows
{
[InitializeOnLoad]
public class ShiroginWelcomeWindow : EditorWindow
{
private Texture2D _logo;
// --- LINKS ---
private const string DOCS_URL =
"https://bitbucket.org/batud/shiroginsdk/src/Version_1/Assets/ShiroginSDK/README.md";
private const string CHANGELOG_URL =
"https://bitbucket.org/batud/shiroginsdk/src/Version_1/Assets/ShiroginSDK/CHANGELOG.md";
private const string DISCORD_URL = "https://discord.gg/wgq3tc7h";
private const string WEBSITE_URL = "https://shirogin.com";
// --- PREFS ---
private const string PREF_SHOW_AT_STARTUP = "ShiroginSDK_ShowWelcome_V2";
static ShiroginWelcomeWindow()
{
EditorApplication.delayCall += () =>
{
if (EditorPrefs.GetBool(PREF_SHOW_AT_STARTUP, true))
{
ShowWindow();
}
};
}
[MenuItem("ShiroginSDK/👋 Welcome", false, 0)]
public static void ShowWindow()
{
// Pencere boyutunu biraz artırdım ki sığsın
var window = GetWindow<ShiroginWelcomeWindow>(true, "Welcome to Shirogin SDK", true);
window.minSize = new Vector2(500, 520);
window.maxSize = new Vector2(500, 520);
window.Show();
}
private void OnEnable()
{
string[] guids = AssetDatabase.FindAssets(
"shirogin_logo t:Texture2D",
new[] { "Assets/ShiroginSDK" }
);
if (guids.Length > 0)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_logo = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
}
}
private void OnGUI()
{
DrawBackground();
GUILayout.BeginVertical();
// 1. LOGO & BAŞLIK
DrawHeader();
GUILayout.Space(15);
// 2. HOŞGELDİN METNİ (Düzeltildi)
DrawWelcomeMessage();
GUILayout.Space(20);
// 3. BUTONLAR (Setup, Manager, Config)
DrawActions();
GUILayout.FlexibleSpace();
// 4. ALT BİLGİ & LİNKLER
DrawFooter();
GUILayout.EndVertical();
}
private void DrawBackground()
{
EditorGUI.DrawRect(new Rect(0, 0, position.width, position.height), new Color(0.18f, 0.18f, 0.18f));
}
private void DrawHeader()
{
GUILayout.Space(20);
if (_logo != null)
{
var aspect = (float)_logo.width / _logo.height;
var width = 120f;
var height = width / aspect;
var rect = GUILayoutUtility.GetRect(width, height);
float xPos = (position.width - width) / 2;
GUI.DrawTexture(new Rect(xPos, rect.y, width, height), _logo);
}
else
{
var titleStyle = new GUIStyle(EditorStyles.boldLabel)
{
fontSize = 28,
alignment = TextAnchor.MiddleCenter,
normal = { textColor = new Color(0.9f, 0.9f, 0.9f) }
};
GUILayout.Label("SHIROGIN SDK", titleStyle);
}
var subStyle = new GUIStyle(EditorStyles.label)
{
fontSize = 12,
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Italic,
normal = { textColor = new Color(0.6f, 0.6f, 0.6f) }
};
// GUILayout.Label("Professional Unity Solutions", subStyle);
}
private void DrawWelcomeMessage()
{
var style = new GUIStyle(EditorStyles.label)
{
wordWrap = true,
alignment = TextAnchor.MiddleCenter,
fontSize = 14,
richText = true,
normal = { textColor = new Color(0.85f, 0.85f, 0.85f) }
};
// GUILayout.Label kullanarak otomatik boyutlandırma sağladık
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// GUILayout.Label(
// "Thank you for choosing <b>ShiroginSDK</b>.\n\n" +
// "This toolkit provides everything you need to build scalable games,\n" +
// "from Ads and IAP to Analytics and UI Frameworks.",
// style, GUILayout.Width(420));
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private void DrawActions()
{
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
// --- SETUP WIZARD ---
if (DrawBigButton("🚀 Setup Wizard", "Install essential packages", new Color(0.2f, 0.6f, 0.3f)))
{
AssetRegistryEditor.ShowWindow(true);
Close();
}
GUILayout.Space(15);
// --- ASSET MANAGER ---
if (DrawBigButton("📦 Asset Manager", "Manage all modules", new Color(0.2f, 0.4f, 0.8f)))
{
AssetRegistryEditor.ShowWindow(false);
Close();
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(15);
// --- OPEN CONFIG BUTTON (YENİ) ---
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var configBtnStyle = new GUIStyle(GUI.skin.button)
{ fixedHeight = 35, fontSize = 12, fontStyle = FontStyle.Bold };
GUI.backgroundColor = new Color(0.9f, 0.6f, 0.2f); // Turuncu ton
if (GUILayout.Button("⚙ Open SDK Config Settings", configBtnStyle, GUILayout.Width(250)))
{
SelectConfig();
Close();
}
GUI.backgroundColor = Color.white;
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
}
private bool DrawBigButton(string title, string subtitle, Color color)
{
var btnStyle = new GUIStyle(GUI.skin.button);
var width = 200f;
var height = 60f;
var oldColor = GUI.backgroundColor;
GUI.backgroundColor = color;
bool clicked = GUILayout.Button("", btnStyle, GUILayout.Width(width), GUILayout.Height(height));
if (Event.current.type == EventType.Repaint)
{
var rect = GUILayoutUtility.GetLastRect();
var titleStyle = new GUIStyle(EditorStyles.boldLabel)
{
alignment = TextAnchor.MiddleCenter,
fontSize = 15,
normal = { textColor = Color.white }
};
var subStyle = new GUIStyle(EditorStyles.label)
{
alignment = TextAnchor.MiddleCenter,
fontSize = 10,
normal = { textColor = new Color(1f, 1f, 1f, 0.85f) }
};
// Pozisyonları biraz daha hassas ayarladım
var titleRect = new Rect(rect.x, rect.y + 10, rect.width, 20);
var subRect = new Rect(rect.x, rect.y + 32, rect.width, 20);
GUI.Label(titleRect, title, titleStyle);
GUI.Label(subRect, subtitle, subStyle);
}
GUI.backgroundColor = oldColor;
return clicked;
}
private void DrawFooter()
{
GUILayout.BeginVertical();
// Çizgi
var lineRect = GUILayoutUtility.GetRect(position.width - 40, 1);
lineRect.x += 20;
lineRect.width -= 40;
EditorGUI.DrawRect(lineRect, new Color(0.3f, 0.3f, 0.3f));
GUILayout.Space(10);
// Linkler
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (LinkButton("📚 Documentation", DOCS_URL))
{
}
GUILayout.Space(15);
if (LinkButton("📚 Changelog", CHANGELOG_URL))
{
}
GUILayout.Space(15);
if (LinkButton("💬 Discord", DISCORD_URL))
{
}
GUILayout.Space(15);
if (LinkButton("🌐 Website", WEBSITE_URL))
{
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(10);
// Toggle
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
bool show = EditorPrefs.GetBool(PREF_SHOW_AT_STARTUP, true);
bool newShow = GUILayout.Toggle(show, "Show this window on startup");
if (newShow != show) EditorPrefs.SetBool(PREF_SHOW_AT_STARTUP, newShow);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.EndVertical();
}
private bool LinkButton(string label, string url)
{
var style = new GUIStyle(EditorStyles.label)
{
normal = { textColor = new Color(0.4f, 0.7f, 1f) },
hover = { textColor = Color.white }
};
if (GUILayout.Button(label, style, GUILayout.ExpandWidth(false)))
{
Application.OpenURL(url);
return true;
}
return false;
}
// --- HELPER: CONFIG BULMA VE SEÇME ---
private void SelectConfig()
{
var config = Resources.Load<ShiroginConfig>(SDKConstants.ConfigResourcePath);
if (config != null)
{
Selection.activeObject = config;
EditorGUIUtility.PingObject(config);
Debug.Log("[ShiroginSDK] Config file selected.");
}
else
{
// Resources'da yoksa projede ara
string[] guids = AssetDatabase.FindAssets("t:ShiroginConfig");
if (guids.Length > 0)
{
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
var obj = AssetDatabase.LoadAssetAtPath<ShiroginConfig>(path);
Selection.activeObject = obj;
EditorGUIUtility.PingObject(obj);
}
else
{
EditorUtility.DisplayDialog("Config Not Found",
"ShiroginConfig file could not be found.\nPlease create it via 'Create > ShiroginSDK > ShiroginConfig'.",
"OK");
}
}
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 64ce90d2a71e4357b30e125402ffe27d
timeCreated: 1765824761

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3e9d1725d6084809ab0bb09455a17321
timeCreated: 1760612286

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 914368bd284d4719b40a88b739e5c2fb
timeCreated: 1760612304

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 963a9768220bd324c92754ac9921612d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more