Streamer Life Simulator 2 Modunu Nasıl Geliştirdim? – Unity Mono ve 82 Özelliğin Teknik Yapısı

Araç & yöntem #streamer-life-simulator-2#unity-mono#trainer-gelistirme#orga-hub#mod-gelistirme 0 cevap 12 okunma

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. 🛠️

Cevap yazmak için giriş yapmalısın. Okumak için giriş gerekmez.

Giriş yap