Streamer Life Simulator 2 için hazırladığım 82 özellikli mod, geliştirme açısından şimdiye kadar ORGA Hub’a eklediğim daha kapsamlı Unity / Mono projelerinden biri oldu.
Bu konuda özelliklerin ne işe yaradığını tekrar anlatmayacağım. Onların duyuru ve mod notları konuları zaten var.
Burada asıl anlatmak istediğim şey şu:
82 farklı özelliği oyuna nasıl bağladım ve neden hepsinde aynı yöntemi kullanmadım?
Çünkü bu modda klasik:
Adresi bul → değeri değiştir → bitti.
yaklaşımı yeterli değildi.
Bazı sistemlerde değeri takip etmek, bazılarında yalnızca artışları yakalamak, bazılarında oyunun kendi fonksiyonunu çağırmak, bazılarında bir listedeki bütün nesneleri işlemek ve bazılarında doğrudan belirli bir oyun metodunun davranışını değiştirmek gerekti.
🧩 Önce Oyunun Yapısını Anlamak Gerekiyordu
Streamer Life Simulator 2, Unity kullanıyor ve oyun kodlarının önemli bölümü Mono runtime üzerinden çalışıyor.
Bu benim açımdan önemli bir avantaj sağlıyor.
Native bir oyunda çoğu zaman:
Assembly instruction
Pointer
AOB
RVA
Code cave
üzerinden ilerlemek gerekirken Mono tarafında oyunun mantıksal yapısına çok daha yakın çalışabiliyorum.
Örneğin bellekte rastgele bir adres görmek yerine:
GameManager
içerisindeki:
playerMotor
ve onun içerisindeki:
energy
alanına ulaşabiliyorum.
Yani hedef:
0x1A73F820
gibi anlamsız bir adres olmaktan çıkıp:
GameManager → PlayerMotor → Energy
gibi oyun mantığını anlatan bir yapıya dönüşüyor.
Bu da büyük trainerlarda ciddi fark yaratıyor.
🧠 ORGA Hub İçin Mono Katmanını Genişlettim
Bu projede yalnızca Mono field okuyup yazmak yeterli değildi.
Bu nedenle ORGA Hub trainer runtime’ında birden fazla Mono operasyonu kullanıyorum.
SLS2 tarafında temel olarak şu tarz işlemler var:
Değeri sürekli sabitleme
Tek seferlik değer değiştirme
Değerin yalnızca azalmasını engelleme
Yalnızca pozitif artışları çarpma
Bir listedeki bütün nesneleri takip etme
Mono metodunun davranışını değiştirme
Oyunun kendi metodunu çağırma
Birden fazla nesne üzerinde toplu işlem yapma
İşlemi oyunun ana thread’i üzerinden yürütme
82 özelliğin farklı sistemlerde düzgün davranabilmesinin asıl nedeni bu.
📍 Instance Bulmak: Önce Gerçek Oyuncuya Ulaşmak
Mono class’ını bulmak tek başına yeterli değil.
Örneğin:
PlayerMotor
class’ının bellekte bulunması, şu anda oyuncuyu temsil eden gerçek PlayerMotor nesnesini otomatik olarak verdiği anlamına gelmiyor.
Bu nedenle birçok özellik için oyunun merkezi singleton yapısını anchor olarak kullanıyorum.
Genel mantık şu şekilde:
GameManager.Instance
↓
ilgili sistem
↓
ilgili nesne
↓
hedef field
Örneğin enerji için mantık:
GameManager.Instance
↓
playerMotor
↓
energy
şeklinde ilerliyor.
Bu yaklaşımın avantajı sabit pointer zincirlerine mümkün olduğunca az bağımlı kalmak.
❄️ Mono Freeze: En Basit Ama En Çok Kullanılan Yapılardan Biri
Bazı özelliklerin ihtiyacı gerçekten basit:
Bu değer sürekli X olarak kalsın.
Bu durumda monoFreeze kullanıyorum.
Runtime belirli aralıklarla gerçek instance’ı çözüyor ve hedef field’ı istenen değerde tutuyor.
Örneğin enerji sisteminin çalışma mantığı kabaca:
GameManager
↓
PlayerMotor
↓
energy
↓
100’de tut
şeklinde.
Aynı yaklaşım farklı ihtiyaç, hareket veya minigame değerlerinde de kullanılabiliyor.
Ama önemli nokta şu:
Her şeye freeze uygulamak doğru çözüm değil.
SLS2 projesinde bunu özellikle gördüm.
💰 Neden Para İçin Sadece Freeze Kullanmadım?
Örneğin:
Sonsuz ve Sabit Para
için freeze mantıklı.
Kullanıcı bir değer seçiyor ve para sürekli o değerde tutuluyor.
Ama:
Harcama Yapınca Para Eksilmesin
tamamen farklı bir özellik.
Burada parayı sabitlersem oyuncunun normal şekilde para kazanmasını da engellemiş olurum.
Diyelim kullanıcının:
10.000
parası var.
Freeze açarsam:
500 harcadığında tekrar 10.000 olur ✅
2.000 kazandığında da tekrar 10.000 olur ❌
İstediğim davranış bu değildi.
🛡️ Non-Decreasing Guard Sistemi
Bu problem için ayrı bir monoGuard mantığı kullanıyorum.
Sistem önce mevcut değeri takip ediyor.
Yeni değer:
Eskisinden düşükse → eski değeri geri getir
Eskisinden yüksekse → yeni değeri kabul et
Böylece para:
10.000 → 9.500
olursa tekrar:
10.000
yapılıyor.
Ama:
10.000 → 12.000
olursa:
12.000
korunuyor.
Bu sayede:
harcamayı engelleyip kazancı koruyabiliyorum.
Küçük bir fark gibi görünüyor ama trainer özelliğinin oyunda doğal hissettirmesi açısından önemli.
📈 Pozitif Kazanç Çarpanı
Benzer bir problem:
Mining
İzleyici
Takipçi
Abone
gibi sistemlerde ortaya çıktı.
Örneğin izleyici sayısını doğrudan sürekli çarpmak istemiyorum.
Çünkü izleyici:
100 → 110
olduğunda gelen +10 artışı çarpmak istiyorum.
Ama:
110 → 90
olduğunda kaybedilen 20 izleyiciyi de pozitif kazançmış gibi işlemek istemiyorum.
Bu yüzden ayrı bir gain multiplier yapısı kullanıyorum.
Mantık:
Önceki değer = 100
Yeni değer = 110
Fark:
+10
Kullanıcı:
5x
seçtiyse gerçek artış:
+50
olarak uygulanıyor.
Ama değer:
110 → 90
olursa fark negatif olduğu için çarpan uygulanmıyor.
Bu sistem oyunun doğal artış ve kayıp mantığını tamamen yok etmeden yalnızca kazancı güçlendirmeme izin veriyor.
₿ Mining Sisteminde Aynı Mantık
Bitcoin mining tarafında da doğrudan BTC bakiyesini sürekli yüksek bir değerde tutmak yerine gerçek kazançları takip edebiliyorum.
Oyunun mining sistemi:
+0.001 BTC
verirse sistem bu pozitif farkı yakalıyor.
Örneğin:
10x
çarpan seçildiyse kazanç buna göre artırılıyor.
Ama kullanıcı BTC harcadığında bu düşüş kazanç olarak değerlendirilip tekrar çarpılmıyor.
Bu yüzden:
BTC Miktarını Ayarla
Sabit BTC
ve
Mining Kazanç Çarpanı
aslında üç ayrı çalışma mantığına sahip.
📋 Listeler Tek Bir Field’dan Daha Zor
Bazı oyun sistemleri tek bir değer değil.
Örneğin aktif işler veya benzeri içerikler bir List içerisinde tutulabiliyor.
Bu durumda:
Şu field’ı bul ve değiştir.
yeterli değil.
Önce gerçek liste instance’ına ulaşıp ardından içerisindeki bütün elemanları dolaşmak gerekiyor.
Bu amaçla ORGA Hub tarafında liste tabanlı Mono işlemleri kullanıyorum.
Mantık kabaca:
Manager
↓
List
↓
Item 1 → field
Item 2 → field
Item 3 → field
...
şeklinde.
Üstelik oyun sırasında listeye yeni eleman eklenebileceği için yalnızca trainer açıldığında bulunan nesneleri değiştirmek yeterli değil.
Liste runtime boyunca takip edilmeli.
✉️ Mail İşlerinde Bunun Gerçek Bir Örneği Var
Kabul edilmiş mail işleri oyunda bir collection içerisinde tutuluyor.
Anında tamamlama sistemi yalnızca:
Şu anda bulunan ilk işin timer’ını değiştir.
şeklinde çalışmıyor.
Aktif iş listesini takip edip içerisindeki ilgili timer değerlerini tamamlanma eşiğinde tutuyor.
Böylece trainer açıldıktan sonra kabul edilen işler de sisteme dahil olabiliyor.
Bu tarz dinamik oyun sistemlerinde liste takibi önemli.
🧩 Bazı Şeyleri Field Değiştirerek Yapmak Yanlış
Bu projede dikkat ettiğim konulardan biri de şu oldu:
Bir oyun işleminin sonucunu bellekte zorla oluşturmak yerine mümkünse oyunun kendi fonksiyonunu kullandırmak.
Bunun en güzel örneklerinden biri faturalar.
Teorik olarak faturaları:
Listeden silebilirim
Fiyatlarını sıfırlayabilirim
Ödenmiş flag’lerini değiştirebilirim
Ama bunların her biri oyunun doğal akışını atlar.
Bunun yerine:
Tüm Faturaları Öde
özelliğinde mümkün olduğunca oyunun kendi ödeme fonksiyonunu kullanıyorum.
🧾 Faturalarda Oyunun Kendi Pay Akışı
Sistem mevcut fatura listesini alıyor.
Ardından her fatura için oyunun normalde kullanıcı butona bastığında çalıştırdığı ödeme metodunu çağırıyor.
Bunun önemli sonucu:
Bakiye yetmiyorsa fatura gerçekten ödenmiyor.
Çünkü oyunun kendi ödeme kontrolü devrede kalıyor.
Ben yalnızca:
Kullanıcı her faturanın ödeme butonuna tek tek basmış gibi işle.
diyorum.
Bu bana göre trainer geliştirmede önemli bir ayrım.
Mümkün olduğunda:
Oyunun sonucunu taklit etmek yerine oyunun kendi mekanizmasını çalıştırmak
daha güvenli ve daha doğal.
🧵 Burada Yeni Bir Problem Çıktı: Main Thread
Unity tarafında her metodu istediğiniz thread’den güvenli şekilde çağıramazsınız.
Özellikle:
GameObject
UI
Unity object
Scene içerisindeki nesneler
üzerinde işlem yapan fonksiyonlar çoğu zaman Unity'nin ana thread’inde çalışmayı bekliyor.
Bu nedenle:
Metodun adresini buldum, dışarıdan çağırayım.
yaklaşımı bazı fonksiyonlarda crash veya garip runtime sorunları oluşturabilir.
SLS2 için bu yüzden main-thread batch yapısını kullandım.
🔄 Oyunun Update Döngüsünü Güvenli Host Olarak Kullanmak
Bazı işlemlerde çağrıyı oyunun kendi çalışan ana thread akışına bırakıyorum.
Örneğin uygun bir:
Update
metodu host olarak kullanılıyor.
ORGA Hub yapılacak işlemi hazırlıyor.
Ardından oyun kendi ana thread’inde ilgili noktaya geldiğinde gerekli fonksiyon çağrılıyor.
Bu sayede Unity API’sine bağlı işlemler oyunun beklediği thread üzerinde gerçekleşiyor.
📹 Anında Video Upload Nasıl Çalışıyor?
Bunun güzel örneklerinden biri Anında Video Upload.
Burada upload yüzdesini bellekte 100 yapmakla yetinmiyorum.
Önce aktif bir upload olup olmadığını kontrol ediyorum.
Ardından upload işleminin gerçekten başlamış olması gerekiyor.
Bu şartlar sağlandıysa oyunun kendi:
upload completed
mantığı ana thread üzerinden çağrılıyor.
Sonuçta oyun:
Video gerçekten upload işlemini tamamladı.
diye davranıyor.
Bu yaklaşım, yalnızca progress değerini değiştirmekten çok daha düzgün.
🏠 Evi Anında Temizlemek Neden İlginçti?
Temizlik sistemi de tek bir:
cleanPercent = 100
field’ından ibaret değil.
Evlerin içerisinde gerçek kir nesneleri bulunuyor.
Ayrıca oyuncunun bulunduğu eve göre farklı nesne listeleri kullanılıyor.
Bu nedenle sistem önce:
CleanManager
üzerinden mevcut ev numarasını belirliyor.
Ardından o eve ait kir nesnelerinin bulunduğu array seçiliyor.
Sonra bu nesneler tek tek işleniyor.
🧹 Gerçek Kir Nesnelerini Kapatıyorum
Temizlik işleminde doğrudan yüzdeyi 100 yapmak yerine kir objelerini oyun dünyasında devre dışı bırakıyorum.
Bunun için Unity'nin kendi:
GameObject.SetActive
fonksiyonu kullanılıyor.
Bu işlem yine ana thread üzerinde gerçekleştiriliyor.
Bütün ilgili nesneler kapatıldıktan sonra oyunun kendi:
CalculateCleaning
fonksiyonu çağrılıyor.
Böylece temizlik yüzdesini de oyun kendisi yeniden hesaplıyor.
Akış:
Bulunduğun evi tespit et
↓
O eve ait kir objelerini bul
↓
GameObject'leri kapat
↓
Oyunun temizlik hesaplamasını tekrar çalıştır
şeklinde.
Bu, benim bu trainerda en sevdiğim uygulamalardan biri oldu.
🚫 Yeni Kir Oluşmasını Engellemek İse Tamamen Başka Bir Problem
Evi Anında Temizle
mevcut kirlerle ilgileniyor.
Ama:
Yeni Kir Oluşmasın
oyunun gelecekte yeni kir üretmesini engellemeli.
Burada sürekli evleri temizlemek gereksiz.
Bunun yerine kir oluşturma metodunun davranışını runtime sırasında devre dışı bırakıyorum.
Bunun için Mono method patch kullanıyorum.
Trainer kapatıldığında özgün metod davranışı geri getirilebiliyor.
Yani iki benzer görünen özellik aslında teknik olarak tamamen farklı:
Evi Anında Temizle → mevcut nesneleri işle
Yeni Kir Oluşmasın → kir oluşturma metodunu engelle
🩹 Mono Method Patch
Mono'nun güzel taraflarından biri ilgili metodun JIT edilmiş runtime adresine ulaşabilmek.
Bu sayede belirli bir fonksiyonun çalışma davranışını geçici olarak değiştirebiliyorum.
Bu yöntemi yalnızca gerçekten gerekli olduğu yerlerde kullanıyorum.
Çünkü field değiştirmeye göre daha müdahaleci bir yöntem.
Bir değer üzerinden çözülebilen problemi gereksiz yere metoda patch atarak çözmeye çalışmak istemiyorum.
🎰 Minigamelerde de Tek Yöntem Yok
Minigame sistemleri de bunun güzel örneği.
Örneğin Blackjack tarafında oyunun dealer toplamı üzerinden sonuç sistemini etkileyebilmek mümkün.
Scratch Card ise farklı alanlara sahip.
Burada:
Kazanma durumu
Ödül miktarı
ayrı ayrı takip ediliyor.
Yani aynı kategori içerisinde bulunan iki özellik bile aynı teknik yapıya sahip olmak zorunda değil.
🧠 82 Özellikte En Büyük Ders: Her Probleme Aynı Çözümü Kullanma
Bu projede benim için en önemli nokta bu oldu.
Bir trainer geliştirirken elinizde sadece:
Freeze
varsa her problemi freeze ile çözmeye çalışırsınız.
Elinizde sadece:
AOB patch
varsa her şeyi patchlemeye çalışırsınız.
Ama sonuç çoğu zaman oyunun doğal sistemlerini bozan özellikler olur.
Streamer Life Simulator 2’de bunun yerine problemi sınıflandırdım.
Değer sabit kalacaksa
Freeze
Tek seferlik değişecekse
Set
Yalnız düşüş engellenecekse
Guard
Yalnız pozitif kazanç büyütülecekse
Gain Multiplier
Bir listedeki bütün nesneler işlenecekse
List/Batch
Oyunun kendi fonksiyonu kullanılacaksa
Method Call
Unity nesnelerine dokunulacaksa
Main Thread Call
Bir metodun çalışması tamamen engellenecekse
Method Patch
kullanıyorum.
Bu ayrım trainerın büyüdükçe yönetilebilir kalmasını sağlıyor.
⚙️ Neden Her Özelliğe Ayrı Kod Yazmadım?
82 özellik için tamamen bağımsız 82 farklı sistem yazmak da doğru değil.
Bunun yerine ORGA Hub trainer runtime’ında tekrar kullanılabilir operasyonlar oluşturdum.
Trainer tanımı temel olarak:
Hangi class?
Hangi field?
Hangi yöntem?
Hangi değer?
Hangi zincir?
bilgisini tarif ediyor.
Asıl karmaşık işleri runtime yapıyor.
Bu sayede aynı monoFreeze motoru:
Enerjide
Hareket değerinde
Minigamede
Başka bir oyunda
tekrar kullanılabiliyor.
Sadece hedef tanımı değişiyor.
🧱 Bunun ORGA Hub Açısından Avantajı
Bu mimarinin amacı yalnız SLS2 geliştirmek değil.
Streamer Life Simulator 2 sırasında eklediğim veya geliştirdiğim bazı genel Mono operasyonları ileride başka Unity oyunlarında da kullanılabilir.
Yani yaptığım iş:
SLS2’ye özel 82 hack yazmak
yerine büyük ölçüde:
ORGA Hub’a genel trainer yapı taşları eklemek
ve ardından SLS2’yi bunların üzerine tanımlamak oldu.
Uzun vadede ORGA Hub’ın trainer tarafını geliştirmek için daha sağlıklı bir yaklaşım olduğunu düşünüyorum.
🔬 Neden Mono Bu Oyun İçin Uygun Bir Yaklaşım?
Çünkü Mono sayesinde oyun içerisindeki mantıksal bağlantıları doğrudan takip edebiliyorum.
Örneğin:
GameManager → BillPanel → Bills
veya:
GameManager → CleanManager
gibi yapılarla çalışmak;
her sürümde rastgele adresleri tekrar bulmaya çalışmaktan hem daha anlaşılır hem de bakım açısından daha yönetilebilir.
Tabii bu:
Mono trainer hiçbir zaman bozulmaz.
anlamına gelmiyor.
Geliştirici class, field veya method yapısını değiştirirse yine güncelleme gerekir.
Ama trainer projesinin neye bağlandığını çok daha açık şekilde görebiliyorum.
🎯 Sonuç
Streamer Life Simulator 2’nin 82 özellikli modunda en zor taraf özellik sayısını yükseltmek değildi.
Asıl çalışma:
Her özellik için doğru müdahale biçimini seçmekti.
Bu proje içerisinde:
Field takibi
Runtime instance çözümleme
Değer sabitleme
Tek seferlik değer yazma
Non-decreasing guard
Pozitif kazanç çarpanı
Dinamik liste takibi
Mono method patch
Oyunun kendi metodlarını çağırma
Main-thread üzerinden toplu işlem
Unity GameObject yönetimi
gibi farklı yöntemleri aynı trainer yapısı içerisinde kullandım.
Bence bunun sonucu da yalnızca:
82 seçenekli büyük bir trainer
değil,
aynı zamanda ORGA Hub’ın Unity / Mono tarafını daha yetenekli hale getiren bir geliştirme oldu.
Bir sonraki benzer Unity projesinde artık bu problemlerin çoğu için sıfırdan sistem geliştirmem gerekmeyecek. 🛠️
Gringos