Difference between revisions of "ClassicWorld file format"

From wiki.vg
Jump to navigation Jump to search
m
m (add to Minecraft Classic category)
 
(32 intermediate revisions by 6 users not shown)
Line 1: Line 1:
 
'''ClassicWorld''' is a level format devised by [[User:Tyteen4a03|tyteen4a03]] with input from [[User:F|fragmer]] for use by custom Minecraft Classic servers. Originally designed for [https://github.com/tyteen4a03/cloudBox cloudBox], it is extensible enough to be used by any server. Metadata allows to attach software-specific data, and to easily share [[Classic Protocol Extension]] settings.
 
'''ClassicWorld''' is a level format devised by [[User:Tyteen4a03|tyteen4a03]] with input from [[User:F|fragmer]] for use by custom Minecraft Classic servers. Originally designed for [https://github.com/tyteen4a03/cloudBox cloudBox], it is extensible enough to be used by any server. Metadata allows to attach software-specific data, and to easily share [[Classic Protocol Extension]] settings.
 +
 +
ClassicWorld files should have extension <code>.cw</code>
  
 
==Specification==
 
==Specification==
 
===Basic structure===
 
===Basic structure===
All tags are required, unless noted otherwise.
+
The map file consists of a GZip-compressed, big-endian [[NBT]] data structure.
 +
 
 +
All tags are required, unless noted otherwise. Also note that ordering of named tags cannot always be guaranteed in NBT libraries. Therefore, when reading files, do not assume a specific order of named tags within a compound tag. However, when writing files, do try to stick to this order if possible.
 
  TAG_Compound("ClassicWorld"): 14 entries {
 
  TAG_Compound("ClassicWorld"): 14 entries {
   TAG_Byte("LevelVersion")
+
   TAG_Byte("FormatVersion")
 
   TAG_String("Name")
 
   TAG_String("Name")
 
   TAG_Byte_Array("UUID")
 
   TAG_Byte_Array("UUID")
Line 33: Line 37:
 
  }
 
  }
  
*'''LevelVersion''' is a constant, currently at <code>1</code>
+
*'''FormatVersion''' is a constant, currently at <code>1</code>
 
*'''Name''' should be limited to 64 characters in length, and contain only characters that can be displayed in Minecraft.
 
*'''Name''' should be limited to 64 characters in length, and contain only characters that can be displayed in Minecraft.
 
*'''UUID''' is a unique 128-bit identifier for this map (for example a GUID), saved as a 16-byte array. Must be set when the map is originally generated, and must be preserved intact when loading/saving worlds. UUID stands for [http://en.wikipedia.org/wiki/Universally_unique_identifier Universally Unique Identifier].
 
*'''UUID''' is a unique 128-bit identifier for this map (for example a GUID), saved as a 16-byte array. Must be set when the map is originally generated, and must be preserved intact when loading/saving worlds. UUID stands for [http://en.wikipedia.org/wiki/Universally_unique_identifier Universally Unique Identifier].
Line 39: Line 43:
 
*'''Y''' is the height (vertical dimension) of the map.
 
*'''Y''' is the height (vertical dimension) of the map.
 
*'''Z''' is the length (second horizontal dimension) of the map.
 
*'''Z''' is the length (second horizontal dimension) of the map.
*'''CreatedBy''' identifies the creator of this map. Optional compound.
+
*'''''TimeCreated''''' is a UTC Unix timestamp that's set when the map is originally generated. Optional.
**'''Username''' is the name of a player.
+
*'''''LastModified''''' is a UTC Unix timestamp that's set when the map's blocks are modified. Changes to spawn point or metadata should not affect this date. Optional.
**'''Service''' can be "Minecraft" (for Minecraft.net usernames) or "ClassiCube" (for ClassiCube.net usernames).
+
*'''''LastAccessed''''' is a UTC Unix timestamp that's set when the map is accessed by any player. Optional.
*'''MapGenerator''' contains information related to map generation. Optional compound.
 
**'''Software''' is the name of software that originally generated this map (e.g. "cloudBox" or "fCraft").
 
**'''MapGeneratorName''' is the name of a specific generation method used by the software (e.g. "Realistic" or "Flat").
 
*'''TimeCreated''' is a UTC Unix timestamp that's set when the map is originally generated. Optional.
 
*'''LastModified''' is a UTC Unix timestamp that's set when the map's blocks are modified. Changes to spawn point or metadata should not affect this date. Optional.
 
*'''LastAccessed''' is a UTC Unix timestamp that's set when the map is accessed by any player. Optional.
 
 
*'''Spawn''' compound tag defines the point where players spawn on the map. All coordinates are in player-position units (32 units per block).
 
*'''Spawn''' compound tag defines the point where players spawn on the map. All coordinates are in player-position units (32 units per block).
 
**'''X''' is the first horizontal component of player's position.
 
**'''X''' is the first horizontal component of player's position.
 
**'''Y''' is the vertical component of player's position.
 
**'''Y''' is the vertical component of player's position.
 
**'''Z''' is the second horizontal component of player's position.
 
**'''Z''' is the second horizontal component of player's position.
**'''H''' is the heading (yew) component of player's orientation.
+
**'''H''' is the heading (yaw) component of player's orientation.
 
**'''P''' is the pitch component of player's orientation.
 
**'''P''' is the pitch component of player's orientation.
 +
*'''''CreatedBy''''' identifies the creator of this map. Optional compound.
 +
**'''''Username''''' is the name of a player. A valid username is expected, and editors should to their best of their ability ensure that it is so at the time of editing.
 +
**'''''Service''''' can be "Minecraft" (for Minecraft.net usernames) or "ClassiCube" (for ClassiCube.net usernames).
 +
*'''''MapGenerator''''' contains information related to map generation. Optional compound.
 +
**'''''Software''''' is the name of software that originally generated this map (e.g. "cloudBox" or "fCraft").
 +
**'''''MapGeneratorName''''' is the name of a specific generation method used by the software (e.g. "Realistic" or "Flat").
 
*'''BlockArray''' contains the actual block data, 1 byte per block. Same packing order as in [[Classic_Protocol#Server_.E2.86.92_Client_packets|LevelDataChunk packets]].
 
*'''BlockArray''' contains the actual block data, 1 byte per block. Same packing order as in [[Classic_Protocol#Server_.E2.86.92_Client_packets|LevelDataChunk packets]].
*'''Metadata''' contains all kinds of software- and plugin-specific data.
+
*'''Metadata''' contains all kinds of software- and plugin-specific data. Servers should preserve unrecognized metadata from other software, if possible.
  
 
===Metadata structure===
 
===Metadata structure===
The ''Metadata'' compound tag must contain zero or more named compound tags, each named after a specific software that produced them. Each of those compound tags should contain zero or more named compound tags, one for each logical grouping (e.g. for each plugin or subsystem).
+
The '''Metadata''' compound tag must contain zero or more named compound tags, each named after a specific software that produced them. Each of those compound tags should contain zero or more named compound tags, one for each logical grouping (e.g. for each plugin or subsystem). An example metadata tag, with two softwares and 3 groups, is shown here:
  TAG_Compound("Metadata") {}
+
  TAG_Compound("Metadata") {
   TAG_Compound("(SomeSoftware)") {
+
   TAG_Compound("SomeSoftware") {
     TAG_Compound("(OneGroup)") {
+
     TAG_Compound("OneGroup") {
 
       ...implementation-specific contents...
 
       ...implementation-specific contents...
 
     }
 
     }
     TAG_Compound("(AnotherGroup)") {
+
     TAG_Compound("AnotherGroup") {
 
       ...implementation-specific contents...
 
       ...implementation-specific contents...
 
     }
 
     }
 
   }
 
   }
   TAG_Compound("(OtherSoftware)") {
+
   TAG_Compound("OtherSoftware") {
     TAG_Compound("(Group)") {
+
     TAG_Compound("Group") {
 
       ...implementation-specific contents...
 
       ...implementation-specific contents...
 
     }
 
     }
Line 79: Line 83:
 
For metadata that does not refer to specific blocks, any tag structure within a group tag is fine.
 
For metadata that does not refer to specific blocks, any tag structure within a group tag is fine.
  
====Sparse metadata for some blocks/coordinates====
+
====Sparse spatial metadata (some blocks/coordinates)====
For metadata that refers to several specific blocks/coordinates, ClassicWorld format suggests the following format.
+
For metadata that refers to a few specific blocks/coordinates, ClassicWorld format suggests the following format.
  TAG_Compound("(SomeSoftware)") {
+
  TAG_Compound("SomeSoftware") {
   TAG_Compound("(SomeGroup)") {
+
   TAG_Compound("SomeGroup") {
 
     TAG_Int_Array("BlockIndices")
 
     TAG_Int_Array("BlockIndices")
 
     TAG_List("BlockMetadata") {}
 
     TAG_List("BlockMetadata") {}
Line 90: Line 94:
 
:*'''BlockMetadata''' is a list of unnamed tags, of the same length and order as BlockIndices. Each tag in BlockMetadata refers to a coordinate in BlockIndices.
 
:*'''BlockMetadata''' is a list of unnamed tags, of the same length and order as BlockIndices. Each tag in BlockMetadata refers to a coordinate in BlockIndices.
  
====Data for every block/coordinate====
+
====Spatial metadata (every block/coordinate)====
 
In the common case of 1 byte or 1 integer being attached to every block/coordinate on the map, simply use a ByteArray/IntArray tag of the same length and order as BlockArray.
 
In the common case of 1 byte or 1 integer being attached to every block/coordinate on the map, simply use a ByteArray/IntArray tag of the same length and order as BlockArray.
  
Line 96: Line 100:
  
 
When a variable amount of information is needed to be stored for every block/coordinate, use a List of Compound tags. Again, same length and order as BlockArray should be used.
 
When a variable amount of information is needed to be stored for every block/coordinate, use a List of Compound tags. Again, same length and order as BlockArray should be used.
  TAG_Compound("(SomeSoftware)") {
+
  TAG_Compound("SomeSoftware") {
   TAG_Compound("(SomeGroup)") {
+
   TAG_Compound("SomeGroup") {
 
     TAG_Byte_Array("OneByteForEveryBlock")
 
     TAG_Byte_Array("OneByteForEveryBlock")
 
     TAG_Int_Array("OneIntForEveryBlock")
 
     TAG_Int_Array("OneIntForEveryBlock")
Line 108: Line 112:
  
 
===ClickDistance===
 
===ClickDistance===
 +
Stores world-specific setting for [[Classic_Protocol_Extension#ClickDistance|ClickDistance]] extension. Note that '''Distance''' should be in player-position units (32 units per block).
 
  TAG_Compound("ClickDistance"): 2 entries {
 
  TAG_Compound("ClickDistance"): 2 entries {
 
   TAG_Int("ExtensionVersion")
 
   TAG_Int("ExtensionVersion")
Line 114: Line 119:
  
 
===CustomBlocks===
 
===CustomBlocks===
 +
Stores world-specific setting for [[Classic_Protocol_Extension#CustomBlocks|CustomBlocks]] extension.
 
  TAG_Compound("CustomBlocks"): 3 entries {
 
  TAG_Compound("CustomBlocks"): 3 entries {
 
   TAG_Int("ExtensionVersion")
 
   TAG_Int("ExtensionVersion")
Line 119: Line 125:
 
   TAG_Byte_Array("Fallback"): [256 bytes]
 
   TAG_Byte_Array("Fallback"): [256 bytes]
 
  }
 
  }
The purpose of the ''Fallback'' array is to allow servers with lower ''SupportLevel'' to load maps created by servers with higher ''SupportLevel''. In such cases, any unsupported block IDs can be substituted using ''Fallback'' mapping. For instance, when a [hypothetical] block ID <code>200</code> is encountered, loading software should substitute it with the value of Fallback[200].
+
The purpose of the ''Fallback'' array is to allow servers with lower ''SupportLevel'' to load maps created by servers with higher ''SupportLevel''. In such cases, any unsupported block IDs can be substituted using ''Fallback'' mapping. For instance, when a [hypothetical] block ID <code>200</code> is encountered, loading software should substitute it with the value of <code>Fallback[200]</code>.
  
 
===EnvColors===
 
===EnvColors===
 +
Stores world-specific setting for [[Classic_Protocol_Extension#EnvColors|EnvColors]] extension. Color variables that are simply set to "default" (-1,-1,-1) may be omitted.
 
  TAG_Compound("EnvColors"): 6 entries {
 
  TAG_Compound("EnvColors"): 6 entries {
 
   TAG_Int("ExtensionVersion")
 
   TAG_Int("ExtensionVersion")
Line 152: Line 159:
  
 
===EnvMapAppearance===
 
===EnvMapAppearance===
 +
Stores world-specific setting for [[Classic_Protocol_Extension#EnvMapAppearance|EnvMapAppearance]] extension.
 
  TAG_Compound("EnvMapAppearance"): 5 entries {
 
  TAG_Compound("EnvMapAppearance"): 5 entries {
 
   TAG_Int("ExtensionVersion")
 
   TAG_Int("ExtensionVersion")
Line 158: Line 166:
 
   TAG_Byte("EdgeBlock")
 
   TAG_Byte("EdgeBlock")
 
   TAG_Short("SideLevel")
 
   TAG_Short("SideLevel")
 +
}
 +
 +
===EnvWeatherType===
 +
Stores world-specific setting for [[Classic_Protocol_Extension#EnvWeatherType|EnvWeatherType]] extension.
 +
TAG_Compound("EnvWeatherType"): 1 entry {
 +
  TAG_Int("ExtensionVersion")
 +
  TAG_Byte("WeatherType")
 +
}
 +
 +
===BlockDefinitions===
 +
:Stores world-specific setting for [[Classic_Protocol_Extension#BlockDefinitions|BlockDefinitions]] and [[Classic_Protocol_Extension#BlockDefinitionsExt|BlockDefinitionsExt]] extensions.
 +
 +
:Note: You must treat TAG_Byte/TAG_Byte_Array values as unsigned values. The ExtensionVersion for the compound tag is currently defined as version 1.
 +
:<ID> in the TAG_Compound is replaced with the numerical value of the block's id in decimal. (e.g. Block20, Block50)
 +
 +
TAG_Compound("BlockDefinitions"): variable number of entries {
 +
  TAG_Int("ExtensionVersion")
 +
  TAG_Compound("Block<ID>"): 10 entries {
 +
    TAG_Byte("ID")
 +
    TAG_String("Name")
 +
    TAG_Float("Speed")
 +
    Tag_Byte_Array("Textures") { Top, Bottom, Left, Right, Front, Back }
 +
    Tag_Byte("TransmitsLight")
 +
    TAG_Byte("WalkSound")
 +
    TAG_Byte("Shape")
 +
    TAG_Byte("BlockDraw")
 +
    TAG_Byte_Array("Fog") { Density, Red, Green, Blue }
 +
    TAG_Byte_Array("Coords") { MinX, MinY, MinZ, MaxX, MaxY, MaxZ }
 
  }
 
  }
  
 
==Support==
 
==Support==
 
These custom servers plan to add support for CW World Format: [http://github.com/tyteen4a03/cloudBox cloudBox], [http://www.fcraft.net/ fCraft]
 
These custom servers plan to add support for CW World Format: [http://github.com/tyteen4a03/cloudBox cloudBox], [http://www.fcraft.net/ fCraft]
 +
 +
The [http://www.classicube.net ClassiCube] and [https://github.com/UnknownShadow200/ClassicalSharp ClassicalSharp] clients supports loading and saving basic tags from the ClassicWorld file format.
 +
 +
Libraries:
 +
 +
C#: [https://github.com/umby24/ClassicWorld.Net ClassicWorld.Net]
 +
 +
[[Category:Minecraft Classic]]

Latest revision as of 22:01, 19 July 2018

ClassicWorld is a level format devised by tyteen4a03 with input from fragmer for use by custom Minecraft Classic servers. Originally designed for cloudBox, it is extensible enough to be used by any server. Metadata allows to attach software-specific data, and to easily share Classic Protocol Extension settings.

ClassicWorld files should have extension .cw

Specification

Basic structure

The map file consists of a GZip-compressed, big-endian NBT data structure.

All tags are required, unless noted otherwise. Also note that ordering of named tags cannot always be guaranteed in NBT libraries. Therefore, when reading files, do not assume a specific order of named tags within a compound tag. However, when writing files, do try to stick to this order if possible.

TAG_Compound("ClassicWorld"): 14 entries {
  TAG_Byte("FormatVersion")
  TAG_String("Name")
  TAG_Byte_Array("UUID")
  TAG_Short("X")
  TAG_Short("Y")
  TAG_Short("Z")
  TAG_Compound("CreatedBy"): 2 entries {
    TAG_String("Service")
    TAG_String("Username")
  }
  TAG_Compound("MapGenerator"): 2 entries {
    TAG_String("Software")
    TAG_String("MapGeneratorName")
  }
  TAG_Long("TimeCreated")
  TAG_Long("LastAccessed")
  TAG_Long("LastModified")
  TAG_Compound("Spawn"): 5 entries {
    TAG_Short("X")
    TAG_Short("Y")
    TAG_Short("Z")
    TAG_Byte("H")
    TAG_Byte("P")
  }
  TAG_Byte_Array("BlockArray")
  TAG_Compound("Metadata") {}
}
  • FormatVersion is a constant, currently at 1
  • Name should be limited to 64 characters in length, and contain only characters that can be displayed in Minecraft.
  • UUID is a unique 128-bit identifier for this map (for example a GUID), saved as a 16-byte array. Must be set when the map is originally generated, and must be preserved intact when loading/saving worlds. UUID stands for Universally Unique Identifier.
  • X is the width (first horizontal dimension) of the map.
  • Y is the height (vertical dimension) of the map.
  • Z is the length (second horizontal dimension) of the map.
  • TimeCreated is a UTC Unix timestamp that's set when the map is originally generated. Optional.
  • LastModified is a UTC Unix timestamp that's set when the map's blocks are modified. Changes to spawn point or metadata should not affect this date. Optional.
  • LastAccessed is a UTC Unix timestamp that's set when the map is accessed by any player. Optional.
  • Spawn compound tag defines the point where players spawn on the map. All coordinates are in player-position units (32 units per block).
    • X is the first horizontal component of player's position.
    • Y is the vertical component of player's position.
    • Z is the second horizontal component of player's position.
    • H is the heading (yaw) component of player's orientation.
    • P is the pitch component of player's orientation.
  • CreatedBy identifies the creator of this map. Optional compound.
    • Username is the name of a player. A valid username is expected, and editors should to their best of their ability ensure that it is so at the time of editing.
    • Service can be "Minecraft" (for Minecraft.net usernames) or "ClassiCube" (for ClassiCube.net usernames).
  • MapGenerator contains information related to map generation. Optional compound.
    • Software is the name of software that originally generated this map (e.g. "cloudBox" or "fCraft").
    • MapGeneratorName is the name of a specific generation method used by the software (e.g. "Realistic" or "Flat").
  • BlockArray contains the actual block data, 1 byte per block. Same packing order as in LevelDataChunk packets.
  • Metadata contains all kinds of software- and plugin-specific data. Servers should preserve unrecognized metadata from other software, if possible.

Metadata structure

The Metadata compound tag must contain zero or more named compound tags, each named after a specific software that produced them. Each of those compound tags should contain zero or more named compound tags, one for each logical grouping (e.g. for each plugin or subsystem). An example metadata tag, with two softwares and 3 groups, is shown here:

TAG_Compound("Metadata") {
  TAG_Compound("SomeSoftware") {
    TAG_Compound("OneGroup") {
      ...implementation-specific contents...
    }
    TAG_Compound("AnotherGroup") {
      ...implementation-specific contents...
    }
  }
  TAG_Compound("OtherSoftware") {
    TAG_Compound("Group") {
      ...implementation-specific contents...
    }
  }
}

Metadata implementation suggestions

Non-spatial metadata

For metadata that does not refer to specific blocks, any tag structure within a group tag is fine.

Sparse spatial metadata (some blocks/coordinates)

For metadata that refers to a few specific blocks/coordinates, ClassicWorld format suggests the following format.

TAG_Compound("SomeSoftware") {
  TAG_Compound("SomeGroup") {
    TAG_Int_Array("BlockIndices")
    TAG_List("BlockMetadata") {}
  }
}
  • BlockIndices is an array of block indices, each referring to a position in the BlockArray.
  • BlockMetadata is a list of unnamed tags, of the same length and order as BlockIndices. Each tag in BlockMetadata refers to a coordinate in BlockIndices.

Spatial metadata (every block/coordinate)

In the common case of 1 byte or 1 integer being attached to every block/coordinate on the map, simply use a ByteArray/IntArray tag of the same length and order as BlockArray.

When more fixed-length data is needed, use one or more List tags, each of the same length and order as BlockArray.

When a variable amount of information is needed to be stored for every block/coordinate, use a List of Compound tags. Again, same length and order as BlockArray should be used.

TAG_Compound("SomeSoftware") {
  TAG_Compound("SomeGroup") {
    TAG_Byte_Array("OneByteForEveryBlock")
    TAG_Int_Array("OneIntForEveryBlock")
    TAG_List("CustomDataForEveryBlock")
  }
}

Classic Protocol Extension metadata

Metadata name "CPE" is reserved for Classic Protocol Extension data, and has standardized contents. Any or all of the below sections may be present, each corresponding to a CPE extension. Each group contains an Int tag named ExtensionVersion.

ClickDistance

Stores world-specific setting for ClickDistance extension. Note that Distance should be in player-position units (32 units per block).

TAG_Compound("ClickDistance"): 2 entries {
  TAG_Int("ExtensionVersion")
  TAG_Short("Distance")
}

CustomBlocks

Stores world-specific setting for CustomBlocks extension.

TAG_Compound("CustomBlocks"): 3 entries {
  TAG_Int("ExtensionVersion")
  TAG_Short("SupportLevel")
  TAG_Byte_Array("Fallback"): [256 bytes]
}

The purpose of the Fallback array is to allow servers with lower SupportLevel to load maps created by servers with higher SupportLevel. In such cases, any unsupported block IDs can be substituted using Fallback mapping. For instance, when a [hypothetical] block ID 200 is encountered, loading software should substitute it with the value of Fallback[200].

EnvColors

Stores world-specific setting for EnvColors extension. Color variables that are simply set to "default" (-1,-1,-1) may be omitted.

TAG_Compound("EnvColors"): 6 entries {
  TAG_Int("ExtensionVersion")
  TAG_Compound("Sky"): 3 entries {
    TAG_Short("R")
    TAG_Short("G")
    TAG_Short("B")
  }
  TAG_Compound("Cloud"): 3 entries {
    TAG_Short("R")
    TAG_Short("G")
    TAG_Short("B")
  }
  TAG_Compound("Fog"): 3 entries {
    TAG_Short("R")
    TAG_Short("G")
    TAG_Short("B")
  }
  TAG_Compound("Ambient"): 3 entries {
    TAG_Short("R")
    TAG_Short("G")
    TAG_Short("B")
  }
  TAG_Compound("Sunlight"): 3 entries {
    TAG_Short("R")
    TAG_Short("G")
    TAG_Short("B")
  }
}

EnvMapAppearance

Stores world-specific setting for EnvMapAppearance extension.

TAG_Compound("EnvMapAppearance"): 5 entries {
  TAG_Int("ExtensionVersion")
  TAG_String("TextureURL")
  TAG_Byte("SideBlock")
  TAG_Byte("EdgeBlock")
  TAG_Short("SideLevel")
}

EnvWeatherType

Stores world-specific setting for EnvWeatherType extension.

TAG_Compound("EnvWeatherType"): 1 entry {
  TAG_Int("ExtensionVersion")
  TAG_Byte("WeatherType")
}

BlockDefinitions

Stores world-specific setting for BlockDefinitions and BlockDefinitionsExt extensions.
Note: You must treat TAG_Byte/TAG_Byte_Array values as unsigned values. The ExtensionVersion for the compound tag is currently defined as version 1.
<ID> in the TAG_Compound is replaced with the numerical value of the block's id in decimal. (e.g. Block20, Block50)
TAG_Compound("BlockDefinitions"): variable number of entries {
 TAG_Int("ExtensionVersion")
 TAG_Compound("Block<ID>"): 10 entries {
   TAG_Byte("ID")
   TAG_String("Name")
   TAG_Float("Speed")
   Tag_Byte_Array("Textures") { Top, Bottom, Left, Right, Front, Back }
   Tag_Byte("TransmitsLight")
   TAG_Byte("WalkSound")
   TAG_Byte("Shape")
   TAG_Byte("BlockDraw")
   TAG_Byte_Array("Fog") { Density, Red, Green, Blue }
   TAG_Byte_Array("Coords") { MinX, MinY, MinZ, MaxX, MaxY, MaxZ }
}

Support

These custom servers plan to add support for CW World Format: cloudBox, fCraft

The ClassiCube and ClassicalSharp clients supports loading and saving basic tags from the ClassicWorld file format.

Libraries:

C#: ClassicWorld.Net