Página 1 de 1

M.2. Script/Thread Memory

Enviado: 17 Ago 2018, 16:56
por Junior_Djjr
Concluindo esta parte você irá:
Aprender a usar e saber dizer as utilidades do Thread Memory, mesmo que não aprenderá completamente todas as utilidades.
Já aprendeu sobre manipulação de memória? Já brincou um pouco? Que tal agora nos aprofundarmos um pouco mais?

Do mesmo modo que você pode ler/escrever memórias do jogo, você pode escrever memórias do seu próprio script... ou de tudo mais o que você imaginar.


O que é "Thread Memory"?

Primeiramente, o nome não é tão correto. Leia o que é este "thread" no glossário do início do tutorial. Seria mais correto chamar de "Script Memory", mas o nome "Thread" pegou.

Tal coisa só é útil devido à SCM/CLEO ter limite de variáveis, além de umas limitações no uso de escopos que impossibilita criar variáveis válidas por todo o código.

Thread Memory nada mais é do que um espaço extra para armazenar dados dentro do seu próprio script, assim resolvendo tais limitações. De fato, este é um tópico que você percebe o quão SCM/CLEO é fraco comparado ao MoonLoader.

Quando a CLEO ser atualizada com variáveis ilimitadas (fazemos a mínima ideia de quando isso acontecerá), este tópico ficará quase inútil.

Se você curte um lado mais "low level" de programação, gostará disto, principalmente se você deseja desafio e é masoquista.


Como criar "Thread Memory"

É super comum em códigos mais avançados serem criados os chamados por nós de "thread memory".

Thread memory é um espaço de dados que você reserva dentro do seu próprio script:

Código: Selecionar tudo

Memory:
DUMP
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
ENDDUMP
Pronto, isso é um thread memory.

O exemplo acima eu criei um thread memory de 4 x 4 bytes, ou seja, total de 16 bytes. Basta contar a quantidade de 00, onde cada 00 é 1 byte.

Organizei eles com 4 linhas de 4 bytes, não por questão de funcionamento, e sim por legibilidade / organização.
Ou seja, eu poderia ter organizado estes 16 bytes de qualquer maneira que ainda assim iria funcionar, por exemplo tudo em uma só linha.

O que eu fiz aqui foi inserir um código hex dentro do meu próprio script. Mas por enquanto pense nisso simplesmente como "um espaço de memória onde podemos armazenar informações".

Veja um exemplo onde eu simplesmente escrevi o valor "100" nos primeiros 4 bytes daquela minha thread memory:

Código: Selecionar tudo

SCRIPT_START
{
LVAR_INT pThreadMemory

GET_LABEL_POINTER Memory (pThreadMemory)
WRITE_MEMORY pThreadMemory 4 (100) FALSE
}
SCRIPT_END

Memory:
DUMP
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
ENDDUMP
Agora, eu posso ir lá ler o valor novamente. Simples!

Código: Selecionar tudo

GET_LABEL_POINTER Memory (pThreadMemory)
READ_MEMORY pThreadMemory 4 FALSE (iValue)
O princípio do thread memory é simplesmente isso.

E então? Eu tenho 4 linhas de 4 bytes, eu só usei uma. Como faço para usar as próximas linhas? Pense.

Olhe para o código, tente imaginar. Você deve saber.

...

Sim, simplesmente aumentando 4 bytes no endereço da label para ir próxima linha, e mais 4 para ir pra próxima.
Assim podemos escrever diferentes números lá em diferentes linhas:

Código: Selecionar tudo

SCRIPT_START
{
LVAR_INT pThreadMemory

GET_LABEL_POINTER Memory (pThreadMemory)
WRITE_MEMORY pThreadMemory 4 (100) FALSE
pThreadMemory += 4
GET_LABEL_POINTER Memory (pThreadMemory)
WRITE_MEMORY pThreadMemory 4 (53022) FALSE
pThreadMemory += 8
GET_LABEL_POINTER Memory (pThreadMemory)
WRITE_MEMORY pThreadMemory 4 (500.0) FALSE
}
SCRIPT_END

Memory:
DUMP
00 00 00 00 // <- 100
00 00 00 00 // <- 53022
00 00 00 00
00 00 00 00 // <- 500.0
ENDDUMP
Leia atentamente a este exemplo acima pois ele é uma boa demonstração do que pode ser feito.


Como evitar Thread Memory: Allocate Memory

Thread memory é ótimo para casos menores, mas para maiores quantidades de bytes, principalmente se você precisa de uma quantidade variável de bytes, prefira usar ALLOCATE_MEMORY, que irá criar um espaço de determinada quantidade de bytes para você depois poder limpar com FREE_MEMORY (quando o script ser encerrado, jogo reiniciado etc também limpa).

ALLOCATE_MEMORY é bom mas causa fragmentação da memória RAM, portanto evite usar demais em pedaços pequenos a não ser que realmente necessário.


Utilidades

Certo, mas qual a real utilidade disso tudo?

Agora vamos focar mais nas utilidades e aprender mais na prática. Depois voltamos com mais explicações.

Manipulação de strings com mais de 16 bytes.

Um dos usos mais comuns é o uso de strings longas demais para caber em variáveis, assim, você pode improvisar:

Código: Selecionar tudo

SCRIPT_START
{
LVAR_INT pThreadMemory

GET_LABEL_POINTER Memory (pThreadMemory)

STRING_FORMAT (pThreadMemory) "O pato anda de costas quando o carro capota".

PRINT_STRING_NOW $pThreadMemory 5000
}
SCRIPT_END

Memory:
DUMP
// 64 bytes
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ENDDUMP
Este é um "hack" muito legal e útil. É simplesmente a criação de um buffer.

O que eu fiz foi pegar o endereço da thread memory, usar o comando STRING_FORMAT para escrever o valor do texto lá, e, pronto. Agora eu posso usar a variável com o endereço como se fosse uma string. O que é bem interessante, não?
Perceba que no STRING_FORMAT você tem que colocar a variável de retorno no início e não no fim como você está acostumado, é porque é um comando da CLEO 4 e somos muito desorganizados!

Perceba também que não é sempre que você poderá usar strings desta forma. Há comandos que aceitam, e outros não.
Por exemplo, para ler e escrever arquivos .ini usando o endereço do arquivo como variável, só é possível usar isto a partir da versão 4.3.23 (onde o Fabio que deixou atualizou à pedido meu).

Por último, saiba que o STRING_FORMAT adiciona um null terminator (00) no fim da string normalmente, você não precisa ter um 00 sobrando para isso. Por outro lado, cuidado! Se você tem uma thread memory de 16 bytes, se você escrever uma string de 16 caracteres lá, o null terminator será o 17º, assim sobrescrevendo o próximo byte do seu script! Sempre tenha bytes sobrando para evitar isso.
Na real, com thread memory você pode ter strings de tamanhos ilimitados! — mas se o jogo / cleo aceita qualquer tamanho é outro assunto :)
Lembre-se que entradas GXT só suportam 128 bytes (127 caracteres, sendo o último byte um null terminator).

Abaixo, um exemplo disto para criar uma entrada GXT, e mostrá-la na tela por um comando que só aceita entrada GXT (DISPLAY_TEXT):

Código: Selecionar tudo

SCRIPT_START
{
LVAR_INT scplayer
LVAR_INT pThreadMemory
LVAR_FLOAT x y z

GET_PLAYER_CHAR 0 scplayer


main_loop:
WAIT 0

// Peguei a coordenada do player
GET_CHAR_COORDINATES scplayer (x y z)

// Peguei o ponteiro pra thread memory
GET_LABEL_POINTER Memory pThreadMemory

// Formatei uma string com os valores das coordenadas.
// Usei "%f" para indicar que quero adicionar um float ali.
// mais especificamente, "%.3f" para dizer que eu quero 3 números de precisão pra direita.
STRING_FORMAT (pThreadMemory) "%.3f %.3f %.3f" x y z

// Apliquei a string que criei. Agora quando eu usar a entrada GXT "TEMPGXT" vou ter o meu texto.
ADD_TEXT_LABEL (TEMPGXT) $pThreadMemory

// Apliquei algumas formatações de texto, para o texto aparecer mais bonito na tela.
SET_TEXT_CENTRE TRUE
SET_TEXT_CENTRE_SIZE 640.0
SET_TEXT_EDGE 1 (0 0 0 255)

// Mostrei a minha entrada GXT "TEMPGXT" na tela.
DISPLAY_TEXT (320.0 240.0) TEMPGXT

// Não podemos esquecer deste comando sempre que desenharmos coisas na tela em loop!
USE_TEXT_COMMANDS 0

GOTO main_loop
}
SCRIPT_END


Memory:
DUMP
// 32 Bytes
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
ENDDUMP

Resultado:
Imagem

Isso não seria possível no jogo sem utilizar o thread memory, pois precisamos de uma variável com mais de 16 bytes para criar este texto com a coordenada. Neste caso, utilizei de uma thread memory com 32 bytes, ou seja, posso escrever até 31 letras lá (sombrando 1 "00" como null terminator).

É claro, você pode personalizar o texto, por exemplo:

Código: Selecionar tudo

STRING_FORMAT (pThreadMemory) "X=%.3f Y=%.3f Z=%.3f" x y z

Perceba que há um problema aqui:
Se você for numa coordenada MUITO longe, irá criar um número tão grande que ultrapassará os 32 bytes, assim sobrescrevendo o que está abaixo desta thread memory, e o que tem embaixo? No nosso código, nada, mas durante o gameplay, na sua memória RAM tem — sobrescreverá informações do jogo (acredito que informações desta ou outras threads), assim causando bugs e crashes, o chamado "stack overflow".


Variáveis adicionais

O jogo originalmente suporta somente 32 variáveis em threads externas (+2 timers). Mas tudo tem solução!

Eu nem preciso explicar pois logo no início desta parte você já viu isso, só lembre-se: você pode guardar informações no script sem usar variáveis!

Mas vamos para um exemplo prático:

Código: Selecionar tudo

SCRIPT_START
{
    LVAR_INT scplayer char chars_created i
    LVAR_FLOAT x y z y_distance

    GET_PLAYER_CHAR 0 scplayer

    main_loop:
    WAIT 0

    IF TEST_CHEAT CREATE
        y_distance = 1.0
        chars_created = 0
        WHILE chars_created < 40
            GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS scplayer (0.0 y_distance 0.0) (x y z)
            CREATE_RANDOM_CHAR (x y z) (char)
            CLEO_CALL StoreChar 0 (char, chars_created)
            y_distance += 1.0
            chars_created++
        ENDWHILE
    ENDIF

    GOTO main_loop
}
SCRIPT_END

{
    LVAR_INT char // In
    LVAR_INT slot // In
    LVAR_INT memory offset

    StoreChar:
    GET_LABEL_POINTER Memory memory
    offset = slot * 4
    memory += offset
    WRITE_MEMORY memory 4 char FALSE
    CLEO_RETURN 0
}


Memory:
DUMP
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
ENDDUMP
Este script cria 40 chars e guarda cada um deles na memória — não precisei guardá-los em variáveis!!! Bastou eu usar um espaço de 40 x 4 bytes.
Imagem
(usei o comando CREATE_RANDOM_CHAR mas criou somente o MALE01.dff/txd pois apliquei o script sem esperar o jogo carregar os modelos de peds, veja aqui)

Agora eu posso criar uma outra função que pega novamente os chars que eu guardei lá na memória, e assim controlá-los novamente:

Código: Selecionar tudo

SCRIPT_START
{
    LVAR_INT scplayer char chars_created i
    LVAR_FLOAT x y z y_distance

    GET_PLAYER_CHAR 0 scplayer

    main_loop:
    WAIT 0

    IF TEST_CHEAT CREATE
        y_distance = 1.0
        chars_created = 0
        WHILE chars_created < 40
            GET_OFFSET_FROM_CHAR_IN_WORLD_COORDS scplayer (0.0 y_distance 0.0) (x y z)
            CREATE_RANDOM_CHAR (x y z) (char)
            CLEO_CALL StoreChar 0 (char, chars_created)
            y_distance += 1.0
            chars_created++
        ENDWHILE
    ENDIF

    IF TEST_CHEAT APPLY
        REPEAT 40 i
            CLEO_CALL GetChar 0 (i)(char)
            TASK_KILL_CHAR_ON_FOOT char scplayer
        ENDREPEAT
    ENDIF

    GOTO main_loop
}
SCRIPT_END

{
    LVAR_INT char // In
    LVAR_INT slot // In
    LVAR_INT memory offset

    StoreChar:
    GET_LABEL_POINTER Memory memory
    offset = slot * 4
    memory += offset
    WRITE_MEMORY memory 4 char FALSE
    CLEO_RETURN 0
}

{
    LVAR_INT slot // In
    LVAR_INT memory offset char

    GetChar:
    GET_LABEL_POINTER Memory memory
    offset = slot * 4
    memory += offset
    READ_MEMORY memory 4 FALSE (char)
    CLEO_RETURN 0 (char)
}


Memory:
DUMP
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
ENDDUMP
Estamos batendo recordes aqui! O maior e mais complexo script do tutorial, até agora. E mesmo assim você está conseguindo entender tudo, né?

Acredito que o CJ não gostou muito do exemplo...
Imagem

Mas vamos entender o que diabos estamos fazendo...

Quando eu digito CREATE o script entra em um loop criando e guardando 40 chars na memória.
Os chars são guardados na memória por uma função super simples:

Código: Selecionar tudo

{ // CLEO_CALL StoreChar 0 (char_handle, slot_id)()
  LVAR_INT char // In
  LVAR_INT slot // In
  LVAR_INT memory offset

  StoreChar:
  GET_LABEL_POINTER Memory memory
  offset = slot * 4
  memory += offset
  WRITE_MEMORY memory 4 char FALSE
  CLEO_RETURN 0
}
Você pode querer reutilizá-la em seus mods.

É fácil entender. Perceba que o 4 é a quantidade de bytes para cada slot (cada "espaço").

Código: Selecionar tudo

offset = slot * 4
Ou seja, se estou guardando um char no slot 0, fará 0 * 4, ou seja, guardará em memory + 0, ou seja, nos primeiros bytes da minha thread memory (na primeira linha);
Se estou guardando no slot 1, fará 1 * 4, ou seja, o offset será 4, portanto guardará em memory + 4, ou seja, na nossa segunda linha;
Se estou guardando no slot 2, fará 2 * 4, ou seja, memory + 8, ou seja, na terceira linha de bytes.

É um sistema super simples de implementar!

Para pegar o char devolta, é só fazer a exata mesma coisa, mas, ao invés de escrever, vamos ler e retornar o valor:

Código: Selecionar tudo

{ // CLEO_CALL GetChar 0 (slot_id)(char_handle)
  LVAR_INT slot // In
  LVAR_INT memory offset char

  GetChar:
  GET_LABEL_POINTER Memory memory
  offset = slot * 4
  memory += offset
  READ_MEMORY memory 4 FALSE (char)
  CLEO_RETURN 0 (char)
}

Viu o que conseguimos fazer aqui? Nós criamos e temos o controle de 40 chars, sem gastar variáveis! Todos eles estão seguros, guardados na memória, onde a qualquer momento você pode ir lá pegá-los.


Valores compartilhados globalmente

Em mods grandes, é muito comum isso ser usado.

A explicação é simples: Os valores estão guardados na memória, e você tem acesso ao endereço daquela label facilmente com GET_LABEL_POINTER. Você pode ler e escrever valores lá de onde você estiver.

Um dos exemplos é o meu mod MDPMv5, onde eu utilizo vários scripts que se comunicam entre si... ...por thread memory!

Eu utilizo um script chamado por mim de "raiz", onde lá você ativa, e quando ativa, o carro é guardado na memória (para no futuro eu saber se aquele carro já está ativo ou não), e um novo script diferente é chamado. No novo script, eu recebo o endereço da thread memory onde está guardado todos os carros ativos. Quando eu desativo um tal carro, o script do carro remove ele mesmo da memória, assim liberando espaço para um novo carro e dizendo para o script raiz que o carro não está mais ativado e poderá ser ativado novamente.

Também neste script raiz eu armazeno informações globais, como qual é o atual carro que você está com o "controle remoto" selecionado, e cada script de cada carro também tem acesso à mesma thread memory com este valor lá para saber se o atual "controle remoto" é o tal carro ou não.
O MDPMv5 é um exemplo tão bom, que, para você ter noção, eu uso até thread memory para guardar ponteiros para outras thread memories. É louco.
Nota: o mesmo poderia ser feito pelas chamadas "CLEO VARS" (comandos SET_CLEO_SHARED_VAR e GET_CLEO_SHARED_VAR) mas elas têm um limite de 1000 e podem causar conflitos entre mods que também usam, portanto eu não gosto de usá-las. O lado bom delas é que elas são armazenadas junto com o jogo salvo, isto sim pode ser um pouco útil, pois você pode guardar informações do seu mod no jogo salvo.

Também há outros modos, como usar manipulamento de memória para um script ler as variáveis do outro (quando estamos falando de manipulamento de memória, tudo é possível, meu jovem). Pra você ter noção, funções do MixSets editam os scripts do main.scm in-game para alterar o funcionamento dele, trocando os comandos, tudo isso com WRITE_MEMORY!

E não só entre scripts, mas, simplesmente, CLEO_CALLs, diferentes escopos etc.

E como sempre, eu tenho exemplos:
Em todos os mods que eu já fiz onde são multilíngues, eu fiz do mesmo modo:

Tenho uma função que lê o idioma configurado no arquivo .ini:

Código: Selecionar tudo

{
    LVAR_INT iTempVar1 iTempVar2
    CONST_INT LANG_EN 1 // English
    CONST_INT LANG_PT 2 // Portuguese
    CONST_INT LANG_ES 3 // Spanish

    StoreLanguage:
    IF NOT READ_INT_FROM_INI_FILE ("CLEO\MDPM.ini" "Settings" "Language") iTempVar1
    OR iTempVar1 > LANG_ES
        iTempVar1 = LANG_EN
    ENDIF
    GET_LABEL_POINTER Language iTempVar2
    WRITE_MEMORY iTempVar2 1 iTempVar1 FALSE
    CLEO_RETURN 0

    IsEnglishLanguage:
    GET_LABEL_POINTER Language iTempVar1
    READ_MEMORY iTempVar1 1 FALSE iTempVar1
    IF iTempVar1 = LANG_EN
        RETURN_TRUE
    ELSE
        RETURN_FALSE
    ENDIF
    CLEO_RETURN 0

    IsPortugueseLanguage:
    GET_LABEL_POINTER Language iTempVar1
    READ_MEMORY iTempVar1 1 FALSE iTempVar1
    IF iTempVar1 = LANG_PT
        RETURN_TRUE
    ELSE
        RETURN_FALSE
    ENDIF
    CLEO_RETURN 0

    Language:
    DUMP
    00
    ENDUMP
}

Assim, basta eu simplesmente chamar esta função com um IF para checar se é o tal idioma:

Código: Selecionar tudo

IF CLEO_CALL IsEnglishLanguage 0
    PRINT_STRING_NOW "Hi" 1000
ELSE
    IF CLEO_CALL IsPortugueseLanguage 0
        PRINT_STRING_NOW "Oi" 1000
    ELSE // Spanish
        PRINT_STRING_NOW "ARRIBA MUCHACHO!!!" 2000
    ENDIF
ENDIF

O ponto é: o valor ID do idioma está armazenado na memória, e não numa variável, portanto, eu posso fazer a checagem de idioma em absolutamente qualquer lugar do script!
Isto é muitíssimo bom para mods grandes, pois você não precisa ficar enviando uma variável com o idioma pra todo o lado, basta você chamar estas funções que elas pegarão o valor do idioma na memória.


Sinta-se livre!

Você pode criar Buffer, simular Class, criar Tables ou simplesmente guardar 1 byte de alguma informação para ser lido depois.

Em assunto de thread memory, você tem que se sentir totalmente livre.

Código: Selecionar tudo

Buffer16Bytes:
DUMP
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ENDDUMP

Language:
DUMP
00
ENDDUMP

IniValues:
DUMP
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
ENDDUMP

Entenda o que temos aqui:
Nós temos, não só um espaço para armazenar informações, mas um código escrito em hexadecimal dentro do nosso script! (simplesmente, vários bytes de dados).

Você pode até mesmo pegar todo o hexadecimal de um arquivo de áudio e adicionar dentro de um DUMP...ENDDUMP, quando necessário, você pode fazer seu script criar um novo arquivo e escrever todo o conteúdo daquele thread memory dentro do arquivo que você criou. Pronto! Seu script criou um arquivo de áudio! É como se você tivesse acoplado um arquivo dentro do seu .cs.

Mas uma das coisas mais fodas que podemos fazer é adicionar códigos de outras programações dentro do seu script cleo, e neste caso, o Fabio nos fez este tutorial, onde inclui um auxiliador criado por ele: http://brmodstudio.forumeiros.com/t4721 ... s-cleo-scm

Veja por exemplo o script do mod Dynamic Tram Signs do Wesser (escrito no Sanny Builder, mas em GTA3script ficaria quase igual):


Você pode até criar scripts inteiros com thread memory. Na real, se você pegar todo o hexadecimal de um arquivo .cs e colocar dentro de um novo script com nenhum comando, somente uma thread memory, irá funcionar!

Veja este exemplo não muito útil, mas interessante:

Código: Selecionar tudo

SCRIPT_START
{
NOP

main_loop:
WAIT 0

IF IS_KEY_PRESSED VK_KEY_Y
  DUMP
  CD 0A 0E 08 55 6D 20 70 61 74 6F 2E 05 88 13
  ENDDUMP
ENDIF

GOTO main_loop
}
SCRIPT_END
O código hexadecimal

Código: Selecionar tudo

CD 0A 0E 08 55 6D 20 70 61 74 6F 2E 05 88 13
é o equivalente a:

Código: Selecionar tudo

PRINT_STRING_NOW "Um pato." 5000

Veja:
CD 0A = Opcode 0ACD que indica o PRINT_STRING_NOW (quem mexe com Sanny Builder está acostumado com isso! Veja)
0E = Data type do próximo argumento ("long string")
08 = Quantidade de bytes da nossa long string.
55 6D 20 70 61 74 6F 2E = Caracteres da string Um pato. (perceba que não tem null terminator (00) pois quem diz a quantidade de bytes da string é o 08 acima, portanto, null terminator aqui seria inútil.
05 = Data type do próximo argumento ("integer")
88 13 = Valor 5000 — Experimente colocar 13 88 na calculadora do Windows no modo "Programador" com HEX (hexadecimal) selecionado e olhe no DEC (decimal), você verá o valor 5000! Lembrando que 88 13 é ao contrário pois a leitura e escrita de dados na memória é inversa.

Perceba que nós começamos este tutorial com uma coisa super-ultra-extremamente simples, e olha até onde chegamos...

Thread memory vai desde uma "variável extra" a até acoplar arquivos e códigos de outra programação dentro do seu script.
Thread memory é uma coisa que você provavelmente usará tanto em mods complexos, quanto em mods mais simples.


Próxima parte:
Classes

Re: M.2. Thread Memory

Enviado: 28 Ago 2020, 10:21
por kurt
porquê inverter ?

88 13 = Valor 5000 — Experimente colocar 13 88

Re: M.2. Thread Memory

Enviado: 28 Ago 2020, 19:28
por Junior_Djjr
kurt escreveu:
28 Ago 2020, 10:21
porquê inverter ?

88 13 = Valor 5000 — Experimente colocar 13 88
Porque a leitura e escrita de dados na memória é ao contrário, acho que já citei numa parte anterior sobre memória, vou botar de novo aqui.

Re: M.2. Thread Memory

Enviado: 12 Jun 2021, 21:52
por Yoshin
Agora só falta um tutorial mais aprofundado de como por assembly em cleo