Jump to content

Optimizare


Mister

Recommended Posts

Legat de optimizare.

Stiu ca WiDuAlK  a facut un tutorial despre asta a fost foarte util si folositor

 

 

 

nu ii pot cere sa mai faca altul pentru ca este destul de greu dar rog membrii sa posteze chestii legate de optimizare.

Am citit prin cateva topicuri ceva de gamemode ca iti seteaza frame rate si ajuta, ce alte metode de optimizare mai cunoasteti si daca stiti sa le impartasiti aici. Da spun asta pentru ca si eu vreau sa cunosc metodele sau am nevoie :) 

Dar tot odata si alti utilizatori vor afla de la altii  ce ei nu stiu.

 

Sa folosesti

  • mysql
  • Alt procesor de comenzi
  • Sscanf
  • Foreach-ul 
  • Streamer
  • Array-urile sa fie cat mai mici

Sunt cateva din ele,

Sper sa aflu si altele si cum seteaza serverul  tickrate rate sau despre ce e vorba  

 

 

 

    __  ____      __           
   /  |/  (_)____/ /____  _____
  / /|_/ / / ___/ __/ _ \/ ___/
 / /  / / (__  ) /_/  __/ /    
/_/  /_/_/____/\__/\___/_/     
SERVICII SCRIPTING DE CALITATE
Pagina     Scripting     pawn
Link to comment
Share on other sites

  • WopsS pinned this topic

Bun , sa clarificam:

OPTIMIZÁRE, optimizări, s. f. 1. Alegerea și aplicarea soluției (economice) optime (dintre mai multe posibile). 2. (Mat.) Raționament sau calcul care permite găsirea valorilor unuia sau mai multor parametri corespunzând maximului unei funcții. – Cf. fr. optimiser, optimisation.

In sa-mp optimizarea este procesul prin care faci un cod sa fie mai rapid la executare.

Am facut niste teste:

1) Variabila normala este mai rapida decat array.

Sa zicem ca avem nevoie de 3 variabile de pozitie pentru GetPlayerPos, sunt 2 cazuri:

fie definim un array: new Float:Pos[3];  GetPlayerPos(playerid, Pos[0], Pos[1], Pos[2]);

fie definim 3 variabile: new Float:PosX, Float:PosY, Float:PosZ; GetPlayerPos(playerid, PosX, PosY, PosZ);

La prima vedere, e mai simplu sa ne folosim de array(cel putin asa am crezut eu), un mic test pe care l-am facut foarte recent legat de viteza array si viteza variabilei normale este:

[21:32:49] Speed Test NORMAL VARIABLE 31550
[21:34:07] Speed Test ARRAY TEST 77967 

[21:34:31] Speed Test NORMAL VARIABLE 24034
[21:35:11] Speed Test ARRAY TEST 39904 

[21:35:35] Speed Test NORMAL VARIABLE 24056
[21:36:15] Speed Test ARRAY TEST 39867 

[21:36:39] Speed Test NORMAL VARIABLE 24163
[21:37:14] Speed Test ARRAY TEST 35011 

[21:37:36] Speed Test NORMAL VARIABLE 22375
[21:38:13] Speed Test ARRAY TEST 36687 

Concluzie: Array este mai lent decat variabila normala.

2) SWITCH este mai rapid decat if(ceva = altceva)

O sa dau cateva exemple:

Sa presupunem ca avem nevoie sa ne folosim de OnDialogResponse:
 

enum
{
	Dialog1,
	Dialog2
};

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	
	return 1;
}

putem accesa cele 2 dialoguri in felul urmator:

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	if(dialogid == Dialog1)
	{
	
	}
	if(dialogid == Dialog2)
	{
	
	}
	return 1;
}

sau

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	switch(dialogid)
	{
		case Dialog1:
		{
		
		}
		case Dialog2:
		{
		
		}
	}
	return 1;
}

putem accesa in acest fel si lista dialogurilor de la tipul: DIALOG_STYLE_LIST

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	switch(dialogid)
	{
		case Dialog1:
		{
			switch(listitem)
			{
			
			}
		}
		case Dialog2:
		{
			switch(listitem)
			{
			
			}
		}
	}
	return 1;
}

tot in acest fel putem accesa si butonul selectat:

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
	switch(dialogid)
	{
		case Dialog1:
		{
			switch(response)
			{
				switch(listitem)
				{
				
				}
			}
		}
		case Dialog2:
		{
			switch(response)
			{
				switch(listitem)
				{
				
				}
			}
		}
	}
	return 1;
}

Nu doar OnDialogResponse poate fi accesat asa:

varianta 1:
forward Functie(parametru, tip)
public Functie(parametru, tip)
{
	if(tip == ceva)
	{
	
	}
	if(tip == ceva2)
	{
	
	}
	return 1;
}
varianta 2:
forward Functie(parametru, tip)
public Functie(parametru, tip)
{
	switch(tip)
	{
	
	}
	return 1;
}

Am testat diferenta intre switch si if(ceva == altceva)

Rezultatele testelor mele sunt:

[21:59:24] Speed Test if(i = ceva) 7713
[21:59:27] Speed Test switch 2537 

[21:59:34] Speed Test if(i = ceva) 7475
[21:59:37] Speed Test switch 2430 

[21:59:44] Speed Test if(i = ceva) 7471
[21:59:47] Speed Test switch 2431 

[21:59:54] Speed Test if(i = ceva) 7558
[21:59:57] Speed Test switch 2423 

[22:00:05] Speed Test if(i = ceva) 8645
[22:00:09] Speed Test switch 3683 

CONCLUZIE: SWITCH ESTE MAI RAPID DECAT IF(CEVA == ALTCEVA)

3)SetPVarInt vs variabila normala

Am vorbit cu stuntman candva si imi spuse ca poti optimiza scriptul cu SetPVarInt/GetPVarInt

Am testat si teoria lui, iar rezultatul testelor mi-a dat:

[22:17:11] SetPVarInt TEST : 4743
[22:17:15] normal variabile TEST : 4119

[22:17:19] SetPVarInt TEST : 4078
[22:17:22] normal variabile TEST : 3282

[22:17:26] SetPVarInt TEST : 3937
[22:17:29] normal variabile TEST : 3065

[22:17:33] SetPVarInt TEST : 3643
[22:17:36] normal variabile TEST : 2869

[22:17:40] SetPVarInt TEST : 4734
[22:17:44] normal variabile TEST : 3464

[22:17:47] SetPVarInt TEST : 3545
[22:17:50] normal variabile TEST : 2708

[22:17:55] SetPVarInt TEST : 4792
[22:17:59] normal variabile TEST : 4543

Am testat si marimea fisierului, iar SetPVarInt stocheaza mai multa memorie decat variabila definita cu new.

4)ENUM VS #DEFINE

Am testat si cele 2 functii ale limbajului pawn:

[23:11:19] Timp scurs(enum): 2086
[23:11:21] Timp scurs(define): 1808 


[23:11:23] Timp scurs(enum): 2050
[23:11:25] Timp scurs(define): 1937 


[23:11:27] Timp scurs(enum): 1953
[23:11:29] Timp scurs(define): 1868 


[23:11:31] Timp scurs(enum): 2034
[23:11:33] Timp scurs(define): 1837 


[23:11:35] Timp scurs(enum): 2150
[23:11:37] Timp scurs(define): 1937 

Desi define este mai rapid putin decat enum, recomandat pentru dialoguri este enum pentru a nu se incurca  dialogurile intre ele.

5)Diferenta intre small si large array

Am testat si diferentele intre 2 array-uri, unul de 2000 si unul de 200:

new array[2000]; vs new array[200];

Timpul de executie pentru cele 2 array-uri:

[23:14:50] Timp scurs(array large): 3254
[23:14:51] Timp scurs(array small): 616 


[23:14:54] Timp scurs(array large): 3469
[23:14:55] Timp scurs(array small): 876 


[23:14:59] Timp scurs(array large): 3856
[23:15:00] Timp scurs(array small): 746 


[23:15:12] Timp scurs(array large): 12562
[23:15:15] Timp scurs(array small): 3190 


[23:15:38] Timp scurs(array large): 22416
[23:15:43] Timp scurs(array small): 5344 

Pe langa timpul scurs, array large(cel de 2000) are marimea mai mare decat array small(array 200).

Am testat si for vs while, si dupa ce le-am comparat dar dupa ce le-am comparat, am vazut ca variaza timpul intre cele 2, si pe rand timpul pentru for devine mai mare si mai mic decat cel al lui while, deci sunt instabile, si cam egale ca viteza.

Am adus dovezi concrete legate de optimizare.

Nu am reusit sa compar iteratia facuta cu foreach si loop-ul normal facut de for, dar din cate am inteles foreach e mai rapid decat for.

Acestea fiind spuse, sper ca am mai lamurit, daca aveti intrebari sau vreti sa compar ceva dati reply iar eu voi face cu mare placere.

 

  • Upvote 2
Link to comment
Share on other sites

foreach() e tot un loop bazat pe for(;;). Timpul de executie e identic in cazul in care ambele au acelasi numar de iteme prin care trebuie sa treaca.

Spre exemplu, daca ai 500 / 500 jucatori conectati, timpul de procesare va fi acelasi in ambele situatii (exceptand in cazul in care folosesti si functia IsPlayerConnected(playerid) intr-un loop normal, atunci foreach devine mai rapid deoarece acea verificare lipseste, validarea jucatorului facandu-se la conectarea / deconectarea sa in foreach).

Totusi, ca exemplu, sa zicem ca sunt 500 / 500 de jucatori conectati si nu exista niciun call suplimentar la o alta functie in cazul unui loop in detrimentul celeilalte:

new l, x = GetPlayerPoolSize();

for(l = 0; l != x; l++) //etc.

foreach(Player, i) // etc.

Astfel, timpul ar trebui sa fie acelasi.

Foreach e mai rapid in anumite situatii, nu in toate. Exista situatii precum cea din exemplul de mai sus, care cred ca e si singura situatia :)), in care timpul de procesare va fi acelasi in cazul ambelor abordari.

 

Chestiile care fac lag pe un server (pe langa cele deja mentionate mai sus de ceilalti) sunt in general:

1. Codul inutil din functii si comenzi (gen IsPlayerConnected(playerid) intr-o comanda... obicei stupid preluat de prin GF-uri... un jucator neconectat nu va putea folosi o comanda...) si nu numai... exemple sunt numeroase, asta mi-a venit prima in minte deoarece o vad extrem de des in 99% din scripturile RP.

2. Proasta optimizare a unor functii apelate des, gen:

-> OnPlayerUpdate(playerid) - prefer sa nici nu folosesc functia asta in scripturile mele pentru ca exista zeci de abordari mult mai eficiente. Functia e apelata la fiecare update al unui jucator, adica extrem de des intr-o singura secunda... daca ai un numar foarte mare de jucatori si ceva cod in ea... server-ul o sa ajunga sa nu mai proceseze mare lucru.

-> OnPlayerKeyStateChange(playerid, newkeys, oldkeys) - si asta e o functie des apelata. Cea mai ok abordare aici mi se pare aceea in care verifici ce key a fost apasat mai intai, iar abia apoi verifici alte variabile si rulezi restul codului ce ar trebui executat in functie de tasta apasata, dupa returnezi (return 0/1;) o valoare pentru a oprii celelalte verificari, care oricum vor fi reluate cand jucatorul apasa din nou o tasta...

-> OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ) - functia asta e destul de des apelata. Cea mai ok abordare pentru mine e de-a verifica arma cu care jucatorul trage printr-un switch(weaponid) deoarece sunt anumite arme pe care nu am nevoie sa le detectez. In functie de arma cu care trage jucatorul, rulez restul verificarilor si codului... altfel, trec peste (default: return 1;).

3. Folosirea variabilelor si/sau a unor array-uri in detrimentul chiar si a functiilor native. Nu o sa exemplific exact cum, dar in majoritatea cazurilor, e mai ok sa stochezi intr-o variabila ce returneaza o functie precum GetPlayerVehicleID(playerid) si sa folosesti variabila in detrimentul functiei daca oricum codul acela s-ar repeta de o groaza de ori. Asta deoarece:

Viteza de procesare in cazul unui server de SA-MP e urmatoarea (ordinea e de la cea mai rapida la cea mai inceata):

-> 1. Nothing

-> 2. Constants

-> 3. Variables

-> 4. Arrays

-> 5. Native functions

->6. Custom functions

-> 7. Remote functions

4. Evitarea folosirii unor functii custom in cazul in care nu vei folosii acea functie decat o singura data.

5. Folosirea unor timere si loops atunci cand poti face altfel, fara ele. Exista metode foarte simple prin care poti evita folosirea unor timere, si chiar a unor loops. In script-ul meu nu ma folosesc de niciun loop la intrarea intr-o casa/afacere/gang house sau la schimbarea hostname-ului in mod aleator. Tot ce folosesc e mysql si trei variabile.

6. Multe altele, dar ma opresc aici :)).

Pawn e single threaded, ceea ce inseamna ca si server-ul de SA-MP e tot single threaded. O aplicatie single-threaded ruleaza codul in ordinea in care e accesat. Asta inseamna ca in cazul in care sunt accesate de server functiile: GetPlayerPos(...), TeleportPlayer(...) si comanda /ban, prima functie procesata va fi GetPlayerPos(...) iar abia apoi celelalte doua si tot asa... daca intr-una dintre functii exista foarte mult cod care mai e si prost optimizat, atunci poate aparea lag-ul, deoarece server-ul trebuie sa astepte pana ce functia aceea este executata complet, pentru a putea continua sa proceseze restul actiunilor precum update-urile trimise de jucatori.

Sunt multe exemple si sfaturi ce ti le pot da din experienta. Iti vei da seama insa in timp pe masura ce inaintezi cum ruleaza codul... care sunt abordarile cele mai ok si asa mai departe... totul depinde de la caz la caz... de la script la script... fiecare e script e diferit si are nevoie de abordari diferite pentru a fi eficient.

Edited by [XSS]MaxXx
  • Upvote 2

3r4AlPA.png

Link to comment
Share on other sites

  • 2 weeks later...

Cum credeti ca este mai eficient, mai optim sa cream un textdraw

 

VARIANTA 1:

la onplayerconnect

 

	    DateClock[playerid] = TextDrawCreate(547.000000,11.000000,"--");//time clock


 

 

iar la onplayerdisconnect

 

 

 TextDraawDestroy(DateClock[playerid]);

 

 

VARIANTA 2:

Sau direct

la ongamemodeinit

 

 

for(new i;i<MAX_PLAYERS;i++)

  DateClock = TextDrawCreate(547.000000,11.000000,"--");//time clock

 

 

Si ce trebuie sa folosim?

CreatePlayerTextDraw

sau TextDrawCreate

pentru a face gmul cat mai optimizat

 

Edited by Mister

 

    __  ____      __           
   /  |/  (_)____/ /____  _____
  / /|_/ / / ___/ __/ _ \/ ___/
 / /  / / (__  ) /_/  __/ /    
/_/  /_/_/____/\__/\___/_/     
SERVICII SCRIPTING DE CALITATE
Pagina     Scripting     pawn
Link to comment
Share on other sites

  • 3 months later...
1 hour ago, Armyw0w said:

aici cu optimizarea is multe, dar ia testeaza si bitwise operation, is curios cat e diferenta.

+ la mysql ar fii indicat folosirea subquery-urilor

Descrie unpic aceste bitwise si sub query uri sa vedem despre ce e vorba si sa invatam sa le folosim:)

 

    __  ____      __           
   /  |/  (_)____/ /____  _____
  / /|_/ / / ___/ __/ _ \/ ___/
 / /  / / (__  ) /_/  __/ /    
/_/  /_/_/____/\__/\___/_/     
SERVICII SCRIPTING DE CALITATE
Pagina     Scripting     pawn
Link to comment
Share on other sites

1 hour ago, Mister said:

Descrie unpic aceste bitwise si sub query uri sa vedem despre ce e vorba si sa invatam sa le folosim:)

greu sa cauti pe google?

Subquery:

De ex vrei sa iei datele de la o factiune, dar mai intai vrei sa iei Id-ul factiuni de la un jucator, in loc sa aloci variabile, sa faci inca un query, poti face ceva gen

SELECT * FROM `Factions` WHERE FactID = (SELECT Member FROM `players` WHERE Name = '%s')

 

Bitwise operation: 

http://forum.sa-mp.com/showthread.php?t=177523

nu am aprofundat nici eu bitwise operation, cu toate ca cica e bun pt optimizare, mi se pare cam 'daunator' momentan, poate dupa ce ai un proiect finalizat merge optimizat cu bitwise.

dar w/e, ca sa testezi a facut Ryder ceva:

http://forum.sa-mp.com/showthread.php?t=275142

Nu prea ajut la categoria RP/GF.

Link to comment
Share on other sites

Mie nu imi e greu sa caut pe google dar acest topic despre asta e facut iar utilizatorii care citesc vor sa vada metodele de optimizare si nu cred ca stau sa caute pe google ce ai spus tu

 

    __  ____      __           
   /  |/  (_)____/ /____  _____
  / /|_/ / / ___/ __/ _ \/ ___/
 / /  / / (__  ) /_/  __/ /    
/_/  /_/_/____/\__/\___/_/     
SERVICII SCRIPTING DE CALITATE
Pagina     Scripting     pawn
Link to comment
Share on other sites

Eu de exemplu prefer sa folosesc gvar asa cum a precizat si stuntman ca este mai eficient. Folosesti mult mai putina memorie si ajuti serverul sa ruleze mai fluent si sa consume mai putine resurse deci rezulta intr-un "Server Tick Count" mult mai ridicat :)

Sinner.png

Link to comment
Share on other sites

  • 3 weeks later...
3 hours ago, WiDuAlK said:

testat... si la viteza si la marime variabila normala bate gvar.

Deci sa inteleg ca ce spune el mai sus nu este adevarat? Ca decat i se pare ca ii merge mai  bine de cand foloseste  gvar?

 

    __  ____      __           
   /  |/  (_)____/ /____  _____
  / /|_/ / / ___/ __/ _ \/ ___/
 / /  / / (__  ) /_/  __/ /    
/_/  /_/_/____/\__/\___/_/     
SERVICII SCRIPTING DE CALITATE
Pagina     Scripting     pawn
Link to comment
Share on other sites

Cand accesezi o variabila normala, accesezi direct adresa unde se afla valoarea.

Cand accesezi o variabila declarata cu (S/G)etPVar*, (S/G)etSVar*, (S/G)etGVar* etc, AMX-ul trimite detaliile cerute catre server/plugin, serverul/pluginul cauta intr-o lista numele variabilei pentru a gasi adresa asociata, citeste/scrie valoarea, apoi trimite inapoi datele catre AMX.

TL;DR: Foloseste variabilele normale pt ca sunt mai rapide. La un server SA-MP nu trebuie sa te intereseze memoria alocata daca este cat de cat optimizat.

Link to comment
Share on other sites

Utilizarea in sine a gvar-ului face serverul mai optimizat. Atata timp cat serverul tau mananca mai putine resurse va rula si mai fluent ;) Un .amx de 20mb plin cu variabile sa inteleg ca va fi net superior unui .amx de 1.5-2mb nu?

EDIT: Se pare ca diferenta de viteza este si uriasa nu?... Si in ce mod ai testat tu memoria utilizata de un pvar? Hai fa-mi un house system cu 2000 de case pe gvar si pe variabile si facem o comparatie ce zici? Proiectul meu roleplay facut de la 0 si bazat pe gvar cu sisteme de: case,business,clanuri,factiuni,teleports,dealership,jobs,atms -> 745kb Pawno / 1,322MB .amx si se putea si mai bine dar nu am avut timp sa optimizez unele includes.

[22:17:11] SetPVarInt TEST : 4743
[22:17:15] normal variabile TEST : 4119

 

Edited by SoNNy.sys

Sinner.png

Link to comment
Share on other sites

3 hours ago, SoNNy.sys said:

Utilizarea in sine a gvar-ului face serverul mai optimizat. Atata timp cat serverul tau mananca mai putine resurse va rula si mai fluent ;) Un .amx de 20mb plin cu variabile sa inteleg ca va fi net superior unui .amx de 1.5-2mb nu?

EDIT: Se pare ca diferenta de viteza este si uriasa nu?... Si in ce mod ai testat tu memoria utilizata de un pvar? Hai fa-mi un house system cu 2000 de case pe gvar si pe variabile si facem o comparatie ce zici? Proiectul meu roleplay facut de la 0 si bazat pe gvar cu sisteme de: case,business,clanuri,factiuni,teleports,dealership,jobs,atms -> 745kb Pawno / 1,322MB .amx si se putea si mai bine dar nu am avut timp sa optimizez unele includes.


[22:17:11] SetPVarInt TEST : 4743
[22:17:15] normal variabile TEST : 4119

 

Memoria dedicata variabilelor globale e alocata la compilare, asadar cand se deschide serverul de SA-MP, AMX-ul este incarcat in intregime memorie, iar spatiul pentru variabilele globale e deja alocat. Marimea fisierului nu trebuie sa te deranjeze pentru ca AMX-ul e impartit pe sectoare, deci serverul stie unde sa caute o informatie cand are nevoie de ea, indiferent daca fisierul e de 1MB sau 10MB.

De altfel, variabilele dinamice ([G/P/S]Vars) sunt alocate direct in memorie la momentul primei folosiri in runtime, acesta fiind motivul pentru care AMX-ul nu creste la declararea lor. Dar, iarasi, memoria RAM per total folosita este aproximativ aceeasi, fiind putin mai mare in cazul variabilelor [G/P/S]Vars.

Verificarea vitezei facuta de tine presupun ca ai facut-o cu o singura variabila PVar, nu? Incearca sa declari 2000 de variabile, cu un jucator conectat (altfel functia nici nu va incerca sa caute variabila in lista), apoi acceseaza-le, pe rand, pe fiecare, sa vezi ca viteza nu mai ramane constanta, ci depinde de numarul lor. Fa acelasi test cu variabilele normale.

 

P.S: 20MB pentru un AMX e destul de mult, nu stiu cum ai ajuns la marimea asta. Un gamemode RPG folosit de mine pe un server de ~500 playeri avea 10MB compilat cu -d3, si crede-ma ca nu prea m-am obosit sa optimizez pe partea de variabile globale.

Edited by Spman
Link to comment
Share on other sites

N-am zis ca am eu .amx de 20 de mb. Daca citeai mai atent am zis ca are 1.2-1.3mb la ora actuala si e cam pus la punct cu toate sistemele ce tin de un server rpg. Eu ma refeream la toate jegurile de GM-uri care n-au auzit de optimizare si nici de gvar. Foloseste in continuare variabile daca tu crezi ca acest plugin esti inutil. Nu stiu daca ai vazut totusi un benchmark facut de Incognito(dezvolatorul plugin-ului): http://forum.sa-mp.com/showpost.php?p=703181&postcount=34

Sinner.png

Link to comment
Share on other sites

Nu am spus nicaieri ca e inutil, ba din contra, il folosesc la un nou proiect unde vreau sa fac un gamemode 100% modular pe filterscript-uri unde folosirea variabilelor normale nu e posibila.

Tot ce eu am spus este ca folosirea GVar-urilor in detrimentul variabilelor globale nu e o optimizare.

Link to comment
Share on other sites

Fiind folosit la sisteme de case/bizz de exemplu cred ca reprezinta o optimizare deoarece economisesti memorie si viteza de executie nu reprezinta chiar o problema. In continuare folosesc si eu variabile simple pentru playeri unde sunt mult mai frecvent utilizate si viteza devine o necesitate.

Sinner.png

Link to comment
Share on other sites

  • 7 months later...

E un plugin pe .com cu care poti testa de cate ori este folosita o functie si cat dureaza sa fie executata: http://forum.sa-mp.com/showthread.php?t=271129
Folosind acel plugin si GetServerTickRate poti sa iti dai seama ce ar trebui sa optimizezi si daca e nevoie sa optimizezi ceva.

Daca vrei sa optimizezi ceva, cred ca ar trebui sa te uiti la time, nu if/switch sau marimea array-urilor, tipul de variabile utilizate sau ZCMD vs Y_CMD. 
Dar optimizarea cand ai 50-100-200 playeri online nu prea conteaza daca e gazduit pe un host ok. 
 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more details you can also review our Terms of Use and Privacy Policy.