[RÉSOLU] Crash assez étrange avec mon Monkey Patch du MapLinker

12 Replies • 696 Views

Metaiko

Graphiste

Bonjour,
Je me suis lancé dans l'ajout des maps diagonales au MapLinker, le script est quasiment opérationnel mais je me retrouve à un crash pour lequel je ne comprends pas la cause. Voici le script en question :
module Yuki
    module MapLinker
        remove_const :LINK_TYPES
        LINK_TYPES = %i[north east south west]
        DIAG_LINK_TYPES = %i[north_east north_west south_east south_west]

        class << self
            # Load a map and its linked map
            # @param map_id [Integer] the map ID
            # @return [RPG::Map] the map adjusted
            def load_map(map_id)
                Yuki::ElapsedTime.start(:maplinker)
                map_datas.first&.map&.events = @last_events if @last_events
                # @type [Array<Yuki::Tilemap::MapData>]
                map_datas = [@map_datas.find { |map| map.map_id == map_id } || Tilemap::MapData.new(load_map_data(map_id), map_id)]
                current_map = map_datas.first.map
                map_datas.first.load_position(current_map, :self, 0)

                if $game_switches[Sw::MapLinkerDisabled]
                reset
                elsif (link_data = $game_data_maplinks[map_id])
                (link_data.size / 2).times do |i|
                    sub_map_id = link_data[i * 2]
                    next if sub_map_id == 0

                    map_data = @map_datas.find { |map| map.map_id == sub_map_id } || Tilemap::MapData.new(load_map_data(sub_map_id), sub_map_id)
                    map_data.load_position(current_map, LINK_TYPES[i % 4], link_data[i * 2 + 1])
                    map_datas << map_data
                    #load digonal maps
                    if [0,2].include?(i)
                        2.times do |j|
                            digaonal_map_id = $game_data_maplinks[sub_map_id][4*j+2]
                            next if digaonal_map_id == 0
                           
                            diag_map_data = Tilemap::MapData.new(load_map_data(digaonal_map_id), digaonal_map_id)
                            diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[j+i], link_data[i * 2 + 1], $game_data_maplinks[sub_map_id][4*j+3])
                            map_datas << diag_map_data
                        end
                    elsif [1,3].include?(i)
                        2.times do |j|
                            digaonal_map_id = $game_data_maplinks[sub_map_id][4*j]
                            next if digaonal_map_id == 0

                            diag_map_data = Tilemap::MapData.new(load_map_data(digaonal_map_id), digaonal_map_id) #i * 2 + 1 bug !
                            diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[(i-1)/2 + 2*j], link_data[i * 2 + 1], $game_data_maplinks[sub_map_id][4*j+1])
                            map_datas << diag_map_data
                        end
                    end
                end
                end

                @map_datas = map_datas
                @added_events.delete(map_id)
                load_events

                Yuki::ElapsedTime.show(:maplinker, 'Loading the tileset & priority took')
                return current_map
            end
        end
    end

    class Tilemap
        class MapData
            DIAG_POSITION_LOADERS = {
                north_east: :load_position_north_east,
                north_west: :load_position_north_west,
                south_east: :load_position_south_east,
                south_west: :load_position_south_west
            }

            # Sets the position of the diagonal map in the 2D Space (for MapLinker)
            # @param map [RPG::Map] current map
            # @param side [Symbol] which side the map is (:north, :south, :east, :west)
            # @param offset_x [Integer] x offset relative to the side of the map in the positive perpendicular position
            # @param offset_y [Integer] y offset relative to the side of the map in the positive perpendicular position
            def load_position_diag(map, side, offset_x, offset_y)
                maker_offset = MapLinker::DELTA_MAKER
                send(DIAG_POSITION_LOADERS[side], map, offset_x, maker_offset, offset_y)
                @side = side
            end

            private

            def load_position_north_east(map, offset_x, maker_offset, offset_y)
                @offset_x = -map.width + offset_x + maker_offset
                @offset_y = @map.height - offset_y - maker_offset
                @x_range = map.width...(@map.width - @offset_x)
                @y_range = -@offset_y...0
            end

            def load_position_north_west(map, offset_x, maker_offset, offset_y)
                @offset_x = @map.width - offset_x - maker_offset
                @offset_y = @map.height - offset_y - maker_offset
                @x_range = -@offset_x...0
                @y_range = -@offset_y...0
            end

            def load_position_south_east(map, offset_x, maker_offset, offset_y)
                @offset_x = -map.width + offset_x + maker_offset
                @offset_y = -map.height - offset_y + maker_offset
                @x_range = map.width...(@map.width - @offset_x)
                @y_range = 0...(map.height + @map.height - offset_y - maker_offset)
            end

            def load_position_south_west(map, offset_x, maker_offset, offset_y)
                @offset_x = @map.width - offset_x - maker_offset
                @offset_y = -map.height - offset_y + maker_offset
                @x_range = -@offset_x...0
                @y_range = 0...(map.height + @map.height - offset_y - maker_offset)
            end
        end
    end
end
Le crash survient à la ligne 45 à cause du link_data[i * 2 + 1], ce dernier est également présent à la ligne 36 mais ne pose aucun soucis.
J'ai essayé de print la valeur de i * 2 + 1, qui me retournait 7 dans mon cas précis (7 étant un index valide dans le link_data). Le truc étant qu'en remplaçant le link_data[i * 2 + 1] par link_data[7] à cette ligne précise, le crash n'apparait plus. Je suis du coup dans l'incompréhension totale 😅
Voici le contenu de mon fichier Error.log :
================================Erreur de script================================
Message :
no implicit conversion from nil to integer

Type : TypeError
Script : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK)
Ligne : 176
Date : 24/08/2021 01:22:58
Game Version : 256
Logiciel : Pokémon SDK 25.4
Script used by eval command :
# Set default avatar if not defined
if !$game_player.charset_base
  $game_player.set_appearance_set(
    $game_switches[1] ?
'hero_01_white' : 'hero_01_red'
  )
end
$game_player.update_appearance


===================================Backtraces===================================
[28] : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK) | ligne 176 draw_map
[27] : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK) | ligne 176 draw
[26] : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK) | ligne 176 each
[25] : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK) | ligne 176 draw
[24] : 01100 Yuki/00300 Tilemap/00001 Tilemap.rb (PSDK) | ligne 44 update
[23] : 00600 Script_RMXP/03500 Spriteset_Map.rb (PSDK) | ligne 238 update
[22] : 00600 Script_RMXP/03500 Spriteset_Map.rb (PSDK) | ligne 75 finish_init
[21] : 00600 Script_RMXP/03500 Spriteset_Map.rb (PSDK) | ligne 65 reload
[20] : 01400 GamePlay/00202 Scene_Map warp.rb (PSDK) | ligne 38 transfer_player
[19] : 01400 GamePlay/00200 Scene_Map.rb (PSDK) | ligne 175 auto_transfert_update
[18] : 01400 GamePlay/00200 Scene_Map.rb (PSDK) | ligne 164 loop
[17] : 01400 GamePlay/00200 Scene_Map.rb (PSDK) | ligne 164 auto_transfert_update
[16] : 01400 GamePlay/00200 Scene_Map.rb (PSDK) | ligne 18 update
[15] : 00700 Ajout_PSDK/00002 FPSBalancer.rb (PSDK) | ligne 44 times
[14] : 00700 Ajout_PSDK/00002 FPSBalancer.rb (PSDK) | ligne 44 run
[13] : 01400 GamePlay/00200 Scene_Map.rb (PSDK) | ligne 18 update
[12] : 01400 GamePlay/00000 GamePlay__Base.rb (PSDK) | ligne 313 main_process
[11] : 01400 GamePlay/00000 GamePlay__Base.rb (PSDK) | ligne 158 main
[10] : tools/GameLoader/Z_main.rb (PSDK) | ligne 17 <top (required)>
[9] : tools/GameLoader/3_load_extensions.rb (PSDK) | ligne 34 rgss_main
[8] : tools/GameLoader/50_load_game_uncompiled.rb (PSDK) | ligne 32 <top (required)>
[7] : <internal:/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb> (ruby) | ligne 85 require
[6] : <internal:/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb> (ruby) | ligne 85 require
[5] : ScriptLoad.rb (PSDK) | ligne 130 load_tool
[4] : tools/GameLoader/Z_load_uncompiled.rb (PSDK) | ligne 10 <top (required)>
[3] : <internal:/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb> (ruby) | ligne 85 require
[2] : <internal:/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb> (ruby) | ligne 85 require
[1] : ScriptLoad.rb (PSDK) | ligne 130 load_tool
[0] : Game.rb (RMXP) | ligne 8 <main>
===================================Fin du log===================================

« Last Edit: 03 September 2021, 17:16:34 by SirMalo »

Bug Résolu Bug PSDK Support Général

Aerun

Modérateur

Utilise la fameuse technique des puts pour afficher le contenu de link_data. Si ça se trouve tes calculs renvoient un link_data[n] qui vaut nil.

Metaiko

Graphiste

Je l'avais également tenté en ajoutant ça au-dessus de la ligne 45 :
p link_data
p i * 2 + 1
p link_data[i * 2 + 1]
p link_data[7]
Mais aucun soucis dans la console
[0, 0, 35, 42, 0, 0, 16, 10]
7
10
10
[Yuki::EXC] #<TypeError: no implicit conversion from nil to integer>
The game crashed!
The error is stored in Error.log.
J'avais également tenté de remplacer la ligne 45 par ça :
index = i * 2 + 1
diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[(i-1)/2 + 2*j], link_data[index], $game_data_maplinks[sub_map_id][4*j+1])
mais aussi par ça :
offset_x = link_data[i * 2 + 1]
diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[(i-1)/2 + 2*j], offset_x, $game_data_maplinks[sub_map_id][4*j+1])
mais rien ne fait. Le seul moment où ça marche c'est quand je fais :
diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[(i-1)/2 + 2*j], link_data[7], $game_data_maplinks[sub_map_id][4*j+1])
Alors qu'on voit bien au-dessus que dans cette situation, link_data[7] et link_data[i * 2 + 1] valent tous les deux 10 :/
Et tu es certain que ta dernière valeur de i * 2 + 1 ne sort pas de link_data déjà ? ça me paraît étrange parce que si tu divises la taille par deux que tu remultiplies par deux et que tu ajoutes 1 tu as un risque. (EDIT : non pourtant ça m'a l'air correct)
Et au passage, c'est normal ça ?
 elsif (link_data = $game_data_maplinks[map_id]) 
Il manque un égal non ?

« Last Edit: 24 August 2021, 15:48:15 by yyyyj »

"On ne voit bien qu'avec le coeur, l'essentiel est invisible pour les yeux",  Antoine de St-Exupéry

Metaiko

Graphiste

Oui c'est normal, ça vient du code de SDK, ça initialise la variable link_data tout en vérifiant si elle ne vaut pas nil
Et avec mes puts sur mon message précédent, on voit bien que ça ne sort pas de link_data (sinon il m'aurait affiché nil dans la console avant le crash ^^)

« Last Edit: 24 August 2021, 16:21:00 by Metaiko »

Nuri Yuri

HostMaster

PSDK supporte déjà d'avoir plus que 4 maps en link. Le tableau nord, ouest, sud, est (enfin selon le bon ordre) est rotatif et les map_id 0 sont ignorées. De ce fait ça te fait virtuellement des liens diagonaux car une map au nord avec un offset devient une map diagonale.

Cela dit pour que le map link supporte bien les diagonales il faut à tout prix utiliser le maplink de .25 et non celui de .24 car les 3 colonnes communes causent des soucis de TP quand il y a des maps diagonales.

Edit: remplacement de nils par map_id 0 (car c'est une erreur)

« Last Edit: 01 September 2021, 18:28:11 by Nuri Yuri »

ln(yo) = <3

Metaiko

Graphiste

Yep j'ai bien le maplink de la .25 ^^
J'ai essayé de jouer avec l'intervalle de @x_range dans les méthodes load_position_<north.south,east,west>, les maps diagonales ne sont pas affichées mais les téléportations vers ces maps fonctionnent. Je vais fouiller ça pour voir si les maps adjacentes aux maps liées sont bien chargées ou si seulement les téléportations sont gérées. Si elles sont bien chargées, il doit du coup bien y avoir un moyen de les afficher ^^

Nuri Yuri

HostMaster

J'ai testé le maplinker avec 12 maps, tout fonctionne bien.
ln(yo) = <3

Metaiko

Graphiste

Je ne sais pas si c'est moi qui n'ai pas bien compris ce que tu dis, mais j'ai tenté une configuration avec quatre maps liées entre elles en carré. Et par défaut, avec la maplink .25, la map diagonale n'est pas affichée.

La seule solution que je vois actuellement sans toucher au code, c'est de fusionner la map Ouest et la map Sud-Ouest (ou Sud-Ouest et Sud) mais cela risque d'agrandir inutilement certaines de mes maps tout en bloquant le link Ouest (ou Sud) au cas où je voudrait ajouter une map attachée à une autre zone.

D'ailleurs, pour en revenir sur mon précédent message concernant l'intervalle de @x_range dans le MapData (je l'ai fait sur le load_position_south dans mes tests), lorsque j'entre sur la map diagonale dans le void, il me téléporte en fait sur la map au Sud à travers le MapLink. Je suis alors obligé de faire un pas supplémentaire pour me téléporter sur la map diagonale :


Edit : Je viens de tester sur cette configuration en carré avec mon code plus haut et je n'ai pas de problème à part l'impossibilité de me téléporter sur la map Nord-Ouest à partir de la map Nord-Est. Cela veut dire que mon crash sur le premier post doit venir d'un facteur extérieur à ce script là. Je vais essayer de chercher d'où ça peut venir ^^


Edit2 : Bon après avoir pas mal bidouillé mon script, j'ai trouvé la cause du soucis ! Lors du chargement des maps diagonales, j'avais inversé les valeurs de offset_x et offset_y. La map ne se trouvait donc pas à la position souhaitée et cela provoquait un crash sous certaines conditions. Le script est maintenant 100% fonctionnel, j'en ai profité pour corriger également le calcul du décalage pour que la map diagonale soit toujours en contact avec la map à laquelle elle est liée. Voilà le script fonctionnel :
module Yuki
    module MapLinker
        remove_const :LINK_TYPES
        LINK_TYPES = %i[north east south west]
        DIAG_LINK_TYPES = %i[north_east north_west south_east south_west]
 
        class << self
            # Load a map and its linked map
            # @param map_id [Integer] the map ID
            # @return [RPG::Map] the map adjusted
            def load_map(map_id)
                Yuki::ElapsedTime.start(:maplinker)
                map_datas.first&.map&.events = @last_events if @last_events
                # @type [Array<Yuki::Tilemap::MapData>]
                map_datas = [@map_datas.find { |map| map.map_id == map_id } || Tilemap::MapData.new(load_map_data(map_id), map_id)]
                current_map = map_datas.first.map
                map_datas.first.load_position(current_map, :self, 0)
 
                if $game_switches[Sw::MapLinkerDisabled]
                reset
                elsif (link_data = $game_data_maplinks[map_id])
                diag_map_loaded = Array.new(0)
                (link_data.size / 2).times do |i|
                    sub_map_id = link_data[i * 2]
                    next if sub_map_id == 0
 
                    map_data = @map_datas.find { |map| map.map_id == sub_map_id } || Tilemap::MapData.new(load_map_data(sub_map_id), sub_map_id)
                    map_data.load_position(current_map, LINK_TYPES[i % 4], link_data[i * 2 + 1])
                    map_datas << map_data
                    #load digonal maps
                    if [0,2].include?(i)
                        2.times do |j|
                            digaonal_map_id = $game_data_maplinks[sub_map_id][4*j+2]
                            next if digaonal_map_id == 0 || diag_map_loaded.include?(digaonal_map_id)
                            diag_map_loaded.push(digaonal_map_id)
                           
                            diag_map_data = Tilemap::MapData.new(load_map_data(digaonal_map_id), digaonal_map_id)
                            diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[j+i], -link_data[i * 2 + 1], -$game_data_maplinks[sub_map_id][4*j+3])
                            map_datas << diag_map_data
                        end
                    elsif [1,3].include?(i)
                        2.times do |j|
                            digaonal_map_id = $game_data_maplinks[sub_map_id][4*j]
                            next if digaonal_map_id == 0 # || diag_map_loaded.include?(digaonal_map_id)
                            diag_map_loaded.push(digaonal_map_id)
 
                            diag_map_data = Tilemap::MapData.new(load_map_data(digaonal_map_id), digaonal_map_id) #i * 2 + 1 bug !
                            diag_map_data.load_position_diag(current_map, DIAG_LINK_TYPES[(i-1)/2 + 2*j], $game_data_maplinks[sub_map_id][4*j+1], link_data[i * 2 + 1])
                            map_datas << diag_map_data
                        end
                    end
                end
                end
                @map_datas = map_datas
                @added_events.delete(map_id)
                load_events
 
                Yuki::ElapsedTime.show(:maplinker, 'Loading the tileset & priority took')
                return current_map
            end
        end
    end
 
    class Tilemap
        class MapData
            DIAG_POSITION_LOADERS = {
                north_east: :load_position_north_east,
                north_west: :load_position_north_west,
                south_east: :load_position_south_east,
                south_west: :load_position_south_west
            }
 
            # Sets the position of the diagonal map in the 2D Space (for MapLinker)
            # @param map [RPG::Map] current map
            # @param side [Symbol] which side the map is (:north, :south, :east, :west)
            # @param offset_x [Integer] x offset relative to the side of the map in the positive perpendicular position
            # @param offset_y [Integer] y offset relative to the side of the map in the positive perpendicular position
            def load_position_diag(map, side, offset_x, offset_y)
                maker_offset = MapLinker::DELTA_MAKER
                send(DIAG_POSITION_LOADERS[side], map, offset_x, maker_offset, offset_y)
                @side = side
            end
 
            private
 
            def load_position_north_east(map, offset_x, maker_offset, offset_y)
                @offset_x = -map.width - offset_x + maker_offset
                @offset_y = @map.height - offset_y - maker_offset
                @x_range = map.width-offset_x...(@map.width - @offset_x)
                @y_range = -@offset_y...offset_y
            end
 
            def load_position_north_west(map, offset_x, maker_offset, offset_y)
                @offset_x = @map.width + offset_x - maker_offset
                @offset_y = @map.height - offset_y - maker_offset
                @x_range = -@offset_x...-offset_x
                @y_range = -@offset_y...offset_y
            end
 
            def load_position_south_east(map, offset_x, maker_offset, offset_y)
                @offset_x = -map.width - offset_x + maker_offset
                @offset_y = -map.height + offset_y + maker_offset
                @x_range = map.width+offset_x...(@map.width - @offset_x)
                @y_range = -@offset_y...(@map.height-@offset_y)
            end
 
            def load_position_south_west(map, offset_x, maker_offset, offset_y)
                @offset_x = @map.width + offset_x - maker_offset
                @offset_y = -map.height + offset_y + maker_offset
                @x_range = -@offset_x...(-offset_x + maker_offset)
                @y_range = -@offset_y...(@map.height-@offset_y)
            end
        end
    end
end
Après, si c'est géré de base par SDK les maps diagonales et qu'il y a un moyen plus simple et potentiellement moins gourmand pour les afficher, je suis preneur :)
Sinon, je partagerai ce script dans la section dédiée ^^

« Last Edit: 01 September 2021, 18:17:37 by Metaiko »

Nuri Yuri

HostMaster

Oui, t'as pas vraiment compris. PSDK supporte ça nativement sans le moindre ajout de script.
Voici la configuration (voir image liée)
Les config de maplink:
# [n_id, n_addx, e_id, e_addy, s_id, s_addx, o_id, o_addy, ...repeating]
$game_data_maplinks[10] = [
  11, 0, 16, 0, 0, 0, 0, 0,
  15, 20]
$game_data_maplinks[15] = [
  0, 0, 0, 0, 16, 0, 11, 0,
  0, 0, 0, 0, 10, -20
]
$game_data_maplinks[11] = [
  0, 0, 15, 0, 10, 0, 0, 0,
  0, 0, 16, 15
]
$game_data_maplinks[16] = [
  15, 0, 0, 0, 0, 0, 10, 0,
  11, -20
]
Testé et fonctionnel. Code à coller dans la console pour tester:
$game_data_maplinks[10] = [11, 0, 16, 0, 0, 0, 0, 0,15, 20]
$game_data_maplinks[15] = [0, 0, 0, 0, 16, 0, 11, 0, 0, 0, 0, 0, 10, -20]
$game_data_maplinks[11] = [0, 0, 15, 0, 10, 0, 0, 0, 0, 0, 16, 15]
$game_data_maplinks[16] = [15, 0, 0, 0, 0, 0, 10, 0, 11, -20]
Debugger.warp(10, 19, 0)
ln(yo) = <3

Metaiko

Graphiste

Ah c'est trop bien ça ! Par contre, je vois qu'il y a une alternance entre les offset x et y. Il y a moyen de changer l'offset x d'une map liée au Sud-Est par exemple ? Si ce n'est pas le cas, je me pencherai sur la question ^^

Nuri Yuri

HostMaster

Pas de double offset. Les map liés doivent tj être collés à une des bordures de la map (x = 0, x = width, y = 0 ou y = height).
Tu dois adapter to mapping au maplink et pas l'inverse. C'est pour ça que mapper avec des chunk de 32x32 est très important.
ln(yo) = <3

Metaiko

Graphiste

D'acc, dans ce cas je referai un export de mes maps en prenant les chunks complets :D
Merci ^^

There was an error while thanking
Thanking...