Pengujian di open.mp
Pengujian di SA:MP itu tidak sangat mudah. Kebanyakan hanyalah melakukan koneksi ke server dan menulis print dimana-mana. Library seperti y_testing membuat penulisan unit pengujian lebih mudah, tetapi hanya untuk kode yang "murni" yaitu, kode yang berjalan di server tanpa banyak interaksi pemain. Kamu dapat melakukan banyak pengujian dengan keadaan seperti ini, tetapi hal ini masih terbatas dan tidak membantu mempersempit masalah ketika mereka bermunculan.
Jadi bagaimana open.mp mengatasi ini? Jadi untuk memulainya, kodenya akan menjadi open-source, Jadi kamu memiliki berbagai macam alat pengawakutuan(debugging) yang tersedia seperti GDB dan Visual Studio untuk melihat dan memeriksa kode. Kami juga berencana untuk menambah alat yang serupa untuk skrip pawn sendiri.
Tetapi penambahan terbesar adalah library "tiruan". Mudahnya, library ini akan memudahkan kamu untuk membuat pemain tiruan yang bertindak persis seperti pemain asli, tetapi tanpa klien yang terkoneksi. Jadi apapun aksi yang dilakukan mereka melalui skrip dapat dibaca dan dianalisa oleh skrip lainnya. Mari ambil contoh termudah yang dapat dilakukan pertama kali - memperlihatkan sebuah checkpoint dari seorang pemain, dan mengkonfirmasi bahwa hal ini sudah dikirim. Dengan y_testing kamu melakukan aksi dan selanjutnya kamu akan bertanya kepada pemain apakah hal itu sudah benar-benar terjadi, dimana hal ini lambat, tidak dapat diulang, dan membosankan:
PTEST__ SetPlayerCheckpoint(playerid)
{
SetPlayerCheckpoint(playerid, 0.0, 0.0, 4.0, 5.0);
ASK("Do you see a checkpoint in the middle of the world?");
}
Menggunakan sebuah library seperti "Pawn.RakNet" dapat menghapus pertanyaan itu, dengan mengotomatiskan pengecekan paket keluar, tetapi masih membutukan seorang pemain yang terkoneksi maka tidak dapat diotomatiskan dan diulan:
static
gCheckpointPlayer,
Float:gX,
Float:gY,
Float:gZ,
Float:gS;
PTEST__ SetPlayerCheckpoint(playerid)
{
SetPlayerCheckpoint(playerid, 0.0, 0.0, 4.0, 5.0);
ASSERT(gX == 0.0);
ASSERT(gY == 0.0);
ASSERT(gZ == 4.0);
ASSERT(gS == 5.0);
ASSERT(gCheckpointPlayer == playerid);
}
OPacket:107(playerid, BitStream:bs)
{
gCheckpointPlayer = playerid;
BS_IgnoreBits(bs, 8);
BS_ReadFloat(bs, gX);
BS_ReadFloat(bs, gY);
BS_ReadFloat(bs, gZ);
BS_ReadFloat(bs, gS);
return 1;
}
Dengan open.mp, kami memiliki API yang mirip seperti Pawn.Raknet, tetapi dengan pemain tiruan. Jadi kamu membuat seorang pemain, tanpa instansi game, dan menggunakannya persis seperti pemain normal:
TEST__ SetPlayerCheckpoint()
{
new playerid = Mock_CreatePlayer("Mr Mock");
// Hapus semua paket yang ada, untuk kemudahan pencarian.
Mock_PurgeSent(playerid);
// Menampilkan sebuah checkpoint seperti biasanya.
SetPlayerCheckpoint(playerid, 0.0, 0.0, 4.0, 5.0);
// Periksa apakah sebuah paket "SetCheckpoint" sudah dilihat.
new MockPacket:packet = Mock_GetPacket(playerid, "SetCheckpoint");
ASSERT(packet);
if (!packet) return;
// Perika paket tersebut memiliki 4 komponen, masing-masing 32 bits.
ASSERT(MockPacket_Components(packet) == 4);
ASSERT(MockPacket_Bits(packet) == 4 * 32);
// Periksa berbagai komponen
new Float:tmp;
ASSERT(MockPacket_ReadFloat(packet, 0, tmp));
ASSERT(tmp == 0.0);
ASSERT(MockPacket_ReadFloat(packet, 1, tmp));
ASSERT(tmp == 0.0);
ASSERT(MockPacket_ReadFloat(packet, 2, tmp));
ASSERT(tmp == 4.0);
ASSERT(MockPacket_ReadFloat(packet, 3, tmp));
ASSERT(tmp == 5.0);
}
Kode ini seluruhnya dapat diuji berulang kali, mandiri (tanpa variabel global dan panggilan tambahan), dan sederhana untuk dikembangkan.
Tetapi terkadang tidak selalu semudah ini. Jika kamu membuat sebuah object kamu tidak dapat mengecek secara instant apakah object tersebut sudah dikirim ke pemain, karena mungkin pemain tidak berada disekitar object sehingga streamer bawaan belum mengirimkan object kepada mereka. Untuk mengatasi hal ini, pertama kita harus sedikit menjelaskan bagaimana cara kerja dari sinrkonisasi untuk hal-hal seperti SetPlayerPos
. server TIDAK memperbarui posisi mereka secara internal dan untuk semua pemain lain secara langsung. Sebagai gantinya sebuah perintah SET POSITION
dikirimkan untuk satu pemain itu yang sedang dipindahkan, mereka di teleportasikan ke posisi baru, dan lalu melaporkan posisi baru mereka kepada server di dalam paket sinkronisasi selanjutnya. Pada dasarnya alasannya adalah lag - disitu ada kemungkinan satu atau beberapa paket sinkronisasi masih berada di posisi yang lama setelah mengatur posisi barunya. Jadi apa artinya ini bagi pemain tiruan? Ini berarti kamu tidak dapat mengeset posisi mereka, setidaknya melalui cara yang normal. Tidak ada klien yang nyata, maka disitu tidak ada paket masuk (contoh : peintah set posisi), dan tidak ada sinkronisasi data yang kembali ke server lagi.
Kita dapat menghasilkan data sinkronisasi tiruan apapun untuk server dari pemain tiruan, maka dari itu ada satu cara untuk memperbarui posisi mereka, tetapi hal ini sedikit merepotkan hanya untuk suatu operasi umum seperti ini, jadi ada Mock_SetPlayerPos
untuk kasus penggunaan ini. Tetapi hal ini masih belum keseluruhan cerita, karena kita membutuhkan streamer untuk menjalankan dan memperbarui dengan posisi baru mereka, yang hanya terjadi secara berkala (setiap beberapa mili detik, berdasarkan tick rate dari server). Untuk ini kita memiliki fungsi tiruan lain - Mock_Tick
, yang dimana melompati waktu server berdasarkan waktu yang diberikan per mikrodetik (BUKAN mili detik). Hal ini HARUS TIDAK digunakan di live server dikarenakan dapat membuat berbagai macam keanehan dengan timer dan kode yang sensitif dengan waktu, tetapi dapat digunakan untuk memalsukan waktu yang berlalu di dalam pengujian. Catat bahwa Mock_Tick(10000)
tidak akan mendelay selama 10ms, itu malah akan melompati waktu 10 milidetik secara instan.
TEST__ SetPlayerCheckpoint()
{
new playerid = Mock_CreatePlayer("Mr Mock");
// Hapus semua paket yang ada, untuk kemudahan pencarian.
Mock_PurgeSent(playerid);
// Buat sebuah objek
CreateObject(1337, 100.0, 100.0, 4.0);
// Palsukan posisi pemain tiruan yang diperbarui ke server, dekat dengan objek
Mock_SetPlayerPos(playerid, 105.0, 105.0, 4.0);
// Sekarang buat seolah-olah sudah melewati beberapa waktu, sehingga streamer dapat berjalan(50ms seharusnya cukup).
Mock_Tick(50000);
// Periksa apakah paket "CreateObject" sudah dilihat.
new MockPacket:packet = Mock_GetPacket(playerid, "CreateObject");
ASSERT(packet);
if (!packet) return;
// Perika paket tersebut memiliki 4 komponen, masing-masing 32 bits.
ASSERT(MockPacket_Components(packet) > 5);
// Periksa beberapa komponen dari paket
new int;
new Float:tmp;
ASSERT(MockPacket_ReadInt32(packet, 1, int));
ASSERT(int == 1337);
ASSERT(MockPacket_ReadFloat(packet, 2, tmp));
ASSERT(tmp == 100.0);
ASSERT(MockPacket_ReadFloat(packet, 3, tmp));
ASSERT(tmp == 100.0);
ASSERT(MockPacket_ReadFloat(packet, 4, tmp));
ASSERT(tmp == 4.0);
}
Sebuah catatan penting tentang Mock_Tick
. Jika kamu memanggilnya, misalkan Mock_Tick(100000)
dan menjalankan timer selama 1ms, hal ini akan memicu timer untuk berjalan secara instan, tetapi timer akan berpikir bahwa telah kelewatan sejak lama (100ms) dan akan mengantri lagi. Saat pemanggilan ke Mock_Tick
timer akan berjalan sekali, tetapi setelah pengujian sekarang telah selesai timer akan mengantri kembali sebanyak 99 kali, satu per tick karena timer berpikir sudah melewatkan waktu eksekusi 100 kali.
Nantinya kami akan membahas beberapa pengawakutuan(debugging) yang lebih tinggi sehingga kamu dapat membaca dan menulis paket palsu lebih mudah, sebagai ganti dari satu komponen dalam satu waktu seperti kode diatas, tetapi ini akan mengenalkan dasar dari segalanya yang dibuat. Semoga hal ini akan membuat pengembangan dan pengujian kode menjadi lebih mudah untuk semuanya (dan memastikan server lebih stabil juga).
Kami juga akan ingin membahas lebih mengenai desain API untuk open.mp besok. Itu sepenuhnya akan kompatibel dengan versi lama SA:MP, tetapi hal itu bukan berarti tidak ada ruang untuk menjadi lebih baik.