Server Side Map Data

187 views
Skip to first unread message

Zylia

unread,
Jun 18, 2011, 11:03:58 AM6/18/11
to l1j-en
The map.txt files on the server are a bit out of data, I am
regenerating the new map.txt based on the latest US client release.
This should correct a lot of issues we were seeing with LinRet such as
the Ice Cave changes, mob spawn errors and players not being able to
move properly. The old maps folder should be archived and the new one
added to replace the legacy data. This way if someone is using the
older clients, they can easily restore those map data entries for use
again.

Bruce C. Miller

unread,
Jun 18, 2011, 1:10:44 PM6/18/11
to l1j-en
Note that I did fix CC and Giran dungeon maps. The ones I still know
of that are messed up are SKT caves (at least 2 and 3, with a couple
bad cells on 4) and MLC4f. I hear some of the ant cave maps are
messed up too, but haven't checked myself. If you diff the jp2
overland map (4.txt) against ours, there are a few differences, but
I've never seen any bad cells on it.

There's a program out there to convert the client map files into the
txt files needed for the server, but I've only seen it once a long
time ago, and it didn't work back then. That'd be the ideal setup, of
course. :) If you're able to do that, that'd be awesome.

Chris Wall

unread,
Jun 18, 2011, 3:17:57 PM6/18/11
to l1j...@googlegroups.com
This is something I've been meaning to do some work on for a while...

I figured out the file format for .seg and .s32 files several years
back, and have some C++ code sitting around that can read those files.
I also have A* pathfinding code which is tailored to work with
Lineage's map format.

The interesting thing about the cells is that a cell does not just
have a "wall" flag set or not - passthrough is determined by each of
the 4 edges of each cell. However each cell owns only 2 edges - the
other 2 are owned by adjacent cells. It makes for funky pathfinding
logic, but avoids potential redundancy errors (situations where you
could walk one way through a particular border, but not back the other
way). It also allows for thin fences such as the fences typically
around towns.

L1JEN's pathfinding needs a bit of work anyway - a separate thread for
mob pathfinding using A* would be ideal IMO.

special_...@hotmail.com

unread,
Jun 19, 2011, 10:07:48 AM6/19/11
to l1j-en
i know i have the c++ code writen bymyself and gaved along time to
bruce and proberly you have it too>? maybe i gaved it too you longtime
ago. because no one else has it and i know how exactly how it works
and how it should be done but i dont know howto confert it too java.
and longtime ago i told bruce that we need to use a*star for monsters
a old friend of my had it working on hes c++ server and really it
works perfect every mmo use astar.
the .s32 are the cells the seg files are the objects. you need to get
them both readed from the lineage map folder i allready had it done
some for reconize each cell. only thing i had to do whas loading all
maps i only had talking island fully working.
maybe we can work together onit to get it working on the java
project ?
about the mapfiles we use now. those are not the reall cells.

doors have a cell like 0x128
fences has a cell like 0x10
zones have a cell like 0x1024
mapnames have a cell like 0x28
then you have a blockcell like 0x00 not able to walk (like water)
then you have a passable cell like 0x01 so you can walk there
trees have a cell.
buildings have cells.

every thing in game has hes own cell and if you check the txt mapfiles
how many cells do you see ? only most of the time only 0 and 1 and 15.
that will never and ever work.
the reall lineage use a*star compared with there maps i know this
because i talked with people from ncsoft alongtime ago.
> >> again.- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -

Lysandria Zylaxyma

unread,
Jun 19, 2011, 10:19:02 AM6/19/11
to l1j...@googlegroups.com
I have a utility that lets me view the map data, tho not the graphic
itself, more of a bit flag viewer. It does create the map.txt data so
I have gone thru each map by hand and recreated them, that project is
complete and has been passed to rick and bruce for submission. We will
need to test that in game, I also sent them a copy of my database
rebuild work, essential to map testing is the updated maps table,
please add that table and the new map.txt content in to the server,
let me know and I'll test the map changes directly. The big changes
are the new Crystal Caves, Ant Caves, SKT map corrections and also
adds teh new maps the client has. Once I can verify the new maps are
working correctly, I can incorporate the db content to go with them

special_...@hotmail.com

unread,
Jun 19, 2011, 10:26:57 AM6/19/11
to l1j-en
CELL_NORMAL = 0,
CELL_BLOCK = 1,
CELL_UNK1 = 2,
CELL_SAFETY = 4,
CELL_COMBAT = 8,
CELL_UNK2 = 16,
CELL_UNK3 = 32,
CELL_UNK4 = 64,
CELL_UNK5 = 128,
CELL_UNK6 = 256,
CELL_OBJECT = 512,
CELL_DOOR = 1024,
these are the reall cells for this objects

special_...@hotmail.com

unread,
Jun 19, 2011, 10:28:06 AM6/19/11
to l1j-en
this is the c# code. maybe someone can transer it to java?

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Data;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Collections;
using System.ComponentModel;

using LSE.LoginServer.Logger;
using LSE.LoginServer.UI;

namespace LSE.LoginServer
{
public class linMap
{
public int mapId;
public int LocX;
public int EndX;
public int LocY;
public int EndY;
public int[][] MapData;
}

public class MapFiles
{
public string dir;
public string filename;
}

public class Map
{
public enum map_area
{
CELL_NORMAL = 0,
CELL_BLOCK = 1,
CELL_UNK1 = 2,
CELL_SAFETY = 4,
CELL_COMBAT = 8,
CELL_UNK2 = 16,
CELL_UNK3 = 32,
CELL_UNK4 = 64,
CELL_UNK5 = 128,
CELL_UNK6 = 256,
CELL_OBJECT = 512,
CELL_DOOR = 1024,
}

public int files;
public int amountfound;
public int skipped;

public int highX;
public int lowX;
public int highY;
public int lowY;

string[] szDirs;
string[] szFiles;

public int totalbytes;
string homeDir = null;

private bool isdone = true;

public static List<linMap> LinMap = new List<linMap>();
public static List<MapFiles> mapLoc = new List<MapFiles>();
MapFiles mapfile;

private static FileStream fs;

public Map()
{
files = 0;
amountfound = 0;
skipped = 0;
totalbytes = 0;
}

public void InitMaps()
{
Logger.Logger.Log(true, "Loading map data from client,
location: "+ConfigReader.LinLocation+".");
Form1.AddLogText(Form1.SERVER, "Loading map data from
client, location: " + ConfigReader.LinLocation + ".");

homeDir = Directory.GetCurrentDirectory();

LinMap = new List<linMap>();
mapLoc = new List<MapFiles>();

szDirs =
Directory.GetDirectories(ConfigReader.LinLocation);
LoadS32();
LoadSeg();

Form1.AddMapsLoaded(mapLoc.Count.ToString());
Form1.AddMapsSkipped(skipped.ToString());

LinMap = new List<linMap>();

Maploader frm = new Maploader();
frm.ShowDialog();

Logger.Logger.Log(true, "Map files loaded : " +
amountfound + ", skipped : " + skipped);
Logger.Logger.Log(true, "Map data stored in memory : " +
LinMap.Count);
}

#region LoadMaps S32
public void LoadS32()
{
for (int i = 0; i < szDirs.Length; i++)
{
LoadMap32(szDirs[i]);
}
}

public void LoadMap32(string dir)
{
szFiles = null;
szFiles = Directory.GetFiles(dir, "*.s32");

if (szFiles.Length > 0)
{
for (int i = 0; i < szFiles.Length; i++)
{
string filename = szFiles[i].Substring(dir.Length
+1);
if (!filename.ToLower().Equals("tempseg.s32"))
{
int loc = dir.Length;
int locX = FromString(filename.Substring(0,
4));
int locY = FromString(filename.Substring(4,
4));

string mapID =
dir.Substring(ConfigReader.LinLocation.Length+1);
int mapid = Convert.ToInt32(mapID);

linMap map = new linMap();
map.mapId = mapid;
map.LocX = locX;
map.LocY = locY;
LinMap.Add(map);

mapfile = new MapFiles();
mapfile.dir = dir;
mapfile.filename = filename;

mapLoc.Add(mapfile);
}
}
}
}

#endregion

#region LoadMaps SEG

public void LoadSeg()
{
for (int i = 0; i < szDirs.Length; i++)
{
LoadMapSeg(szDirs[i]);
}

}

public void LoadMapSeg(string dir)
{
szFiles = null;
szFiles = Directory.GetFiles(dir, "*.seg");

if (szFiles.Length > 0)
{
for (int i = 0; i < szFiles.Length; i++)
{
string filename = szFiles[i].Substring(dir.Length
+ 1);
if (!filename.ToLower().Equals("tempseg.seg"))
{
int loc = dir.Length;
int locX = FromString(filename.Substring(0,
4));
int locY = FromString(filename.Substring(4,
4));

string mapID =
dir.Substring(ConfigReader.LinLocation.Length + 1);
int mapid = Convert.ToInt32(mapID);

if (!CheckFile(locX, locY, mapid))
{
mapfile = new MapFiles();
mapfile.dir = dir;
mapfile.filename = filename;
mapLoc.Add(mapfile);
}
else
{
skipped ++;
}
}
}
}
}

#endregion

#region Load Data into buffer, hard part :)

public static void LoadData(string dir, string filename)
{
Maploader.Loaded++;
string file = dir + "\\" + filename;
fs = new FileStream(@file, FileMode.Open);
BinaryReader br = new BinaryReader(fs);

// add common data to list
string mapID =
dir.Substring(ConfigReader.LinLocation.Length + 1);
int mapid = Convert.ToInt32(mapID);

// get map locx, locy from filename
int locX = FromString(filename.Substring(0, 4));
int locY = FromString(filename.Substring(4, 4));
string filetype = filename.Substring(9);

if (mapid == 1)
{
//******
// LOAD
//******
if (filetype.Equals("s32"))
{
//Logger.Logger.Log(true, "Loading "+filename);

int xdif = (32767 - locX) *64;
int ydif = (32767 - locY) *64;

//Logger.Logger.Log(true, "X Diff " + xdif + "
ydiff "+ ydif);

//*LOAD S32 MAP
DATA**********************************************
// MAP SIZE 128x128
//
// skip intro bytes
fs.Seek(32768, SeekOrigin.Begin);
// skip gfx part
ushort gfxLen = br.ReadUInt16();
long len = gfxLen * 6;
fs.Seek(len + 32768 + 2, SeekOrigin.Begin);

for (int x = 0; x < 64; x++)
for (int y = 0; y < 64; y++)
{
linMap map = new linMap();

map.LocX = xdif + x;
map.LocY = ydif + y;
map.mapId = mapid;

ushort idx = br.ReadUInt16();
ushort idy = br.ReadUInt16();

//Logger.Logger.Log(true, "Map = x: " +
map.LocX + " (data " + idx.ToString() + ") y: " + map.LocY + " (data "
+ idy.ToString()+")");

LinMap.Add(map);
}

//
****************************************************************
}

if (filetype.Equals("seg"))
{
Logger.Logger.Log(true, "Loading " + filename);
//*LOAD SEG MAP
DATA**********************************************
//
// skip intro bytes
fs.Seek(16384, SeekOrigin.Begin);
// skip gfx part
ushort gfxLen = br.ReadUInt16();
long len = gfxLen * 4;
fs.Seek(len + 16384, SeekOrigin.Begin);

// HERE DATA BEGINS!!!!
for (int i = 0; i < 4096; i++)
{
byte idx = br.ReadByte();
byte idy = br.ReadByte();

Logger.Logger.Log(true, "id = x: " +
idx.ToString() + " y: " + idy.ToString());
}
//
****************************************************************
}
}

// close all file things
fs.Close();
fs.Dispose();
br.Close();
br = null;
}

#endregion


#region Utilites
public bool CheckFile(int locx, int locy, int mapid)
{
foreach (linMap map in LinMap)
{
if (map.LocX == locx && map.LocY == locy &&
map.mapId.Equals(mapid))
{
return true;
}
}
return false;
}

// convert hex value to integer.
public static Int32 FromString(string id)
{
int num = int.Parse(id,
System.Globalization.NumberStyles.HexNumber);
return num;
}
#endregion
}
}


On 19 jun, 16:19, Lysandria Zylaxyma <lysand...@gmail.com> wrote:

special_...@hotmail.com

unread,
Jun 19, 2011, 10:29:43 AM6/19/11
to l1j-en
i didnt received you email

On 19 jun, 16:19, Lysandria Zylaxyma <lysand...@gmail.com> wrote:

Chris Wall

unread,
Jun 19, 2011, 9:19:07 PM6/19/11
to l1j...@googlegroups.com
Hi, the code I was referring to was code I wrote myself a long time
ago for my bot. I'll have to dig it up, I forgot where I stored it.

special_...@hotmail.com

unread,
Jun 21, 2011, 2:19:02 PM6/21/11
to l1j-en
lets hope that you can find it

On 20 jun, 03:19, Chris Wall <chris.wa...@gmail.com> wrote:
> Hi, the code I was referring to was code I wrote myself a long time
> ago for my bot. I'll have to dig it up, I forgot where I stored it.
>
> On Sun, Jun 19, 2011 at 10:29 AM, special_darkw...@hotmail.com
>
>
>
> <special_darkw...@hotmail.com> wrote:
> > i didnt received you email
>
> > On 19 jun, 16:19, Lysandria Zylaxyma <lysand...@gmail.com> wrote:
> >> I have a utility that lets me view the map data, tho not the graphic
> >> itself, more of a bit flag viewer. It does create the map.txt data so
> >> I have gone thru each map by hand and recreated them, that project is
> >> complete and has been passed to rick and bruce for submission. We will
> >> need to test that in game, I also sent them a copy of my database
> >> rebuild work, essential to map testing is the updated maps table,
> >> please add that table and the new map.txt content in to the server,
> >> let me know and I'll test the map changes directly. The big changes
> >> are the new Crystal Caves, Ant Caves, SKT map corrections and also
> >> adds teh new maps the client has. Once I can verify the new maps are
> >> working correctly, I can incorporate the db content to go with them- Tekst uit oorspronkelijk bericht niet weergeven -

special_...@hotmail.com

unread,
Jun 28, 2011, 3:31:24 PM6/28/11
to l1j-en
zylia can you come on msn?

On 21 jun, 20:19, "special_darkw...@hotmail.com"
> > - Tekst uit oorspronkelijk bericht weergeven -- Tekst uit oorspronkelijk bericht niet weergeven -

special_...@hotmail.com

unread,
Jun 29, 2011, 12:46:48 PM6/29/11
to l1j-en
zylia doesnt matter i used your files and made the offsets right and
still the building and fences problem. the txt files will never canna
work.
i got astar done only need the real file maps get loaded. then it will
work perfect.

On 28 jun, 21:31, "special_darkw...@hotmail.com"

special_...@hotmail.com

unread,
Jun 29, 2011, 1:40:21 PM6/29/11
to l1j-en
i found the maptool source and this is the way how we should done it
in java then it works for loading the client map files.

private string mapid;
private Hashtable list_all ;
private int[,] tileList;

private int xLength;
private int yLength;

private byte[] decrypt;

private void readMapFiles(string mapNumber)
{
list_all = new Hashtable();
decrypt = null;
DirectoryInfo di = new DirectoryInfo(map_path + "\\" +
mapNumber);
FileSystemInfo[] diArr = di.GetFileSystemInfos();
int min_areax = 65535;
int max_areax = 0;
int min_areay = 65535;
int max_areay = 0;

foreach (FileSystemInfo dri in diArr)
{
//取得地圖名稱
string fileName = dri.Name.ToLower();
if (fileName.Length == 12 &&!fileName.EndsWith("ini"))
{
string index = fileName.Substring(0, 8).ToLower();

if (!list_all.ContainsKey(index))
{
list_all.Add(index, fileName);
int areax =
Convert.ToInt32(fileName.Substring(0, 4), 16);
int areay =
Convert.ToInt32(fileName.Substring(4, 4), 16);
min_areax = Math.Min(min_areax, areax);
min_areay = Math.Min(min_areay, areay);
max_areax = Math.Max(max_areax, areax);
max_areay = Math.Max(max_areay, areay);
}
}
}

if (list_all.Count == 0)
{
toolStripStatusLabel1.Text = "找不到可讀取的資料 ";
return;
}

int xblockcount = max_areax - min_areax +1;
int yblockcount = max_areay - min_areay +1;

xLength = xblockcount * 64;
yLength = yblockcount * 64;

toolStripStatusLabel1.Text = "地圖區塊 = " + xblockcount +
"X" + yblockcount ;

int xEnd = (max_areax - 0x7fff) * 64 + 32767;
int yEnd = (max_areay - 0x7fff) * 64 + 32767;

toolStripStatusLabel1.Text = "X座標範圍 = (" + (xEnd - xLength
+1) + "~" + xEnd + ")"
+ " Y座標範圍 = (" + (yEnd - yLength+1) + "~" + yEnd +
")";

tileList = new int[xLength,yLength];

progressBar1.Visible = true;
progressBar1.Maximum = list_all.Count + 1;
progressBar1.Value = 0;

foreach (FileSystemInfo dri in diArr)
{
//取得地圖名稱
string fileName = dri.Name.ToLower();
string idx = fileName.Substring(0, 8).ToLower();
if (list_all.ContainsKey(idx))
{
int x = Convert.ToInt32(idx.Substring(0,4),16);
int y = Convert.ToInt32(idx.Substring(4, 4), 16);
decrypt = File.ReadAllBytes(map_path + mapNumber +
"\\" + fileName);

if (fileName.EndsWith(".s32"))
{
readS32(decrypt, x - min_areax, y -
min_areay);
}
else if (fileName.EndsWith(".seg"))
{
readSeg(decrypt, x - min_areax, y -
min_areay);
}
list_all.Remove(idx);
progressBar1.Value += 1;
}
}

//output
paintMap();
progressBar1.Visible = false;
}


private void readS32(byte[] data, int pp, int ppp)
{
MemoryStream ms = new MemoryStream(data);
BinaryReader br = new BinaryReader(ms);
int off = 0;
off = 32768;
br.BaseStream.Seek(off, SeekOrigin.Begin);
int unknow1 = br.ReadUInt16();
off += unknow1 * 6;
br.BaseStream.Seek(off+2, SeekOrigin.Begin);

int t1, t2, t3, t4 = 0;
int count = 0;
int count2 = -1;
while (true)
{
if (count % 64 == 0)
{
count2++;
count = 0;
}
if (count2 >= 64)
{
break;
}
t1 = br.ReadByte();
t2 = br.ReadByte();
t3 = br.ReadByte();
t4 = br.ReadByte();
int x = count + pp * 64;
int y = count2 + ppp * 64;
tileList[x, y] = decryptData(t1, t3);
count++;
}
ms.Dispose();
br.Close();
}

private void readSeg(byte[] data, int pp, int ppp)
{
MemoryStream ms = new MemoryStream(data);
BinaryReader br = new BinaryReader(ms);
int off = 0;
off = 16384;
br.BaseStream.Seek(off, SeekOrigin.Begin);
int unknow1 = br.ReadUInt16();
off += unknow1 * 4;
br.BaseStream.Seek(off+2, SeekOrigin.Begin);

int t1, t3 = 0;
int count = 0;
int count2 = -1;
while (true)
{
if (count % 64 == 0)
{
count2++;
count = 0;
}
if (count2 >= 64)
{
break ;
}
t1 = br.ReadByte();
t3 = br.ReadByte();

int x = count + pp * 64;
int y = count2 + ppp * 64;
tileList[x, y] = decryptData(t1, t3);
count++;
}
ms.Dispose();
br.Close();
}

////
private int decryptData(int t1, int t3)
{
int score = 0;
string s0, s1;
string st1 = t1.ToString("X2");
s0 = st1.Substring(0, 1);
s1 = st1.Substring(1, 1);
//一般
if (s1.Equals("0") || s1.Equals("1") || s1.Equals("2") ||
s1.Equals("3"))
{
score += 0;
//安全
}
else if (s1.Equals("4") || s1.Equals("5") ||
s1.Equals("6") || s1.Equals("7")
|| s1.Equals("C") || s1.Equals("D") ||
s1.Equals("E") || s1.Equals("F"))
{
score += 16;
//戰鬥
}
else if (s1.Equals("8") || s1.Equals("9") ||
s1.Equals("A") || s1.Equals("B"))
{
score += 32;
}
if (t1 < 16)
{
//可移動 & 箭可通過 ?( 不確定)
if ((t1 & 1) == 0)
{
score += 10;
}
}
else
{

//箭可通過
if ((Convert.ToInt32(s0, 16) & 1) == 0)
{
score += 8;
}
//可移動
if ((Convert.ToInt32(s1, 16) & 1) == 0)
{
score += 2;
}


}

string st3 = t3.ToString("X2");
s0 = st3.Substring(0, 1);
s1 = st3.Substring(1, 1);


if (t3 < 16)
{
//可移動 & 箭可通過 ?( 不確定)
if ((t3 & 1) == 0)
{
score += 5;
}
}
else
{

//箭可通過
if ((Convert.ToInt32(s0, 16) & 1) == 0)
{
score += 4;
}
//可移動
if ((Convert.ToInt32(s1, 16) & 1) == 0)
{
score += 1;
}

}
return score;
}

On 29 jun, 18:46, "special_darkw...@hotmail.com"

special_...@hotmail.com

unread,
Jun 30, 2011, 10:12:23 AM6/30/11
to l1j-en
this will not work. what we use now the txtmapreader. because it only
reads the cells and not the tilesets thats why it walk trough
buildings fences etc. you can see it that it loads a flatmap without
objects. so map txt files are useless. and then it doesnt load seg
files. so another missing thing.

only the .s32 files are transfered to the txt files and that are the
cells.
i tryed it to convert the script too java but without succes. so we
need to find someone that have this done in java (since they guy that
made it for hes bot doesnt answer anymore)

On 29 jun, 19:40, "special_darkw...@hotmail.com"
> ...
>
> meer lezen >>- Tekst uit oorspronkelijk bericht niet weergeven -
Reply all
Reply to author
Forward
0 new messages