The future of WWW, the Virtual World Transfer Protocol and the Virtual World Traveler

December 26th, 2011

With more than 2 decades development, the internet industry advanced hugely, at many aspects. But in general life, browsing 2D WWW pages rendered in browsers is still the main user experience. Although there are many rich media types embedded, the experience is still not very engaged.  It is really the time to welcome a more impressive web surfing implementation. It is very possible the new implementation is the very old concept – Virtual World. Thanks to Linden Lab for bringing us the Second Life world, which surely is the early prototype of the future WWW. But the implementation of Second Life is far from the ideal.

What Second Life has done bad?

  • closed ecosystem. Although the client development is open, but the server side is closed. Especially, the world is closed.
  • Linden Lab is too ambitious. They want to build all aspects of the next generation of WWW.
  • the world is flat. Not that bad, but really not real.
  • lame avatar animations and controls

What Second Life has done good?

  • one world instance. This is great comparing to WoW.
  • the world is being modified continuously by users. This is another important good point comparing to WoW.
  • embed 2D pages in 3D world.

What is an ideal implementation?

I really don’t know it clearly, but I think the most important point is to keep the world open. At the server side, any companies/communities/people can create and manage a subset of the world. We can call this role as land service provider, just as nowadays’ page hosting service provider. A sub world may be a small planet, a shopping mall or other weird models.

At the client side, all developers can contribute to Virtual World Traveler, just as nowadays’ browsers. Certainly, there will be many technology standard proposals born and vanish. But surely some will be the winners, just as nowadays’ HTML and CSS.

The HTTP protocol is not good enough for the Virtual World, we need a new protocol, Virtual World Transfer Protocol (VWTP) is a good name, which will keep the connection state alive between the server and clients. The domain system still works, and the coordinate will be important part in URL, such as vwtp://example.com/(x,y). There will be many type of coordinates. The coordinates may have 2, 3 or 4 and more dimensions.

What is the corresponding concepts of HTML/CSS in the Virtual World, maybe they will be the triangle meshes and textures used in nowadays’ 3D games, maybe they are others, hybrid model formats showing in one scene is not a bad idea. Renderer will be a important component in the Virtual World Traveler. Multiple renderers functioning at the same time will be very possible.

AI has not counterpart in nowdays’ browsers.

There will be many creative application types in the Virtual World, such as virtual play fields, virtual theatre, etc.

The Virtual World even will affect the development of future operating systems.

The age of NoCompiler languages will come.

December 4th, 2011

The mobile age is coming, with a very fast pace. But most people today still think mobile devices will not replace PC completely. One reason may be programming is difficult on mobile devices. This is right, if we will always program by typing texts.

The “Text Typing -> Compile -> Link” model is so successful in the past decades so that other models fall into the shade. But with the coming of mobile age, the situation will change.

Text programming obviously is not a good model in mobile age. How about visual programming? Visual programming really has more natural advantages than text programming. They will surely get a larger market share on mobile devices than PC. But I don’t think traditional visual programmings will be the final winner on mobile devices. If it will, it has defeated text programming on PC already. The reasons why it didn’t win on PC are exact the reasons why it will not will win on mobile device.

(Here, I mean the narrow visual programming definition, for example, LabView. In a wider visual programming definition, using Adobe Flash CS even PhotoShop can also be viewed as visual programming. Artists are programmers, programmers are also artists. :))

What programming language model will win on mobile devices? I really don’t know. But I have a candidate solution, which is a combination of traits from both text programming and traditional visual programming. Finger Programming is a good name for it. But I would call it NoCompiler Programming or YouComiler Programming language. Yes, this type languages need NOT compiler. You, the programmer, is the compiler. When a program is coded done, it is already compiled. We just need a linker to create the final execute program or a no-compiler VM to run it.

It is hard for me to describe clearly how to use NoCompiler languages to program here. If you ever used Phyard Builder’s script editor, you know what I say above. Now the Piapia language, Phyard Builder’s script language, is still far to the perfect implementation of a good NoCompiler language. I will improve it continuously. In fact, the current implementation is much like the script editor of Flash MX Studio (I really don’t know what it is like in Flash CS), but there will be more and more differences.

If NoCompiler programming model, or another model for mobile age, wins text programming, the PC age will be over.
Another impact of NoCompiler languages is the current predicted future Cloud Compilers will become Cloud Linkers.

mobile internet age is coming, cloud app age is coming

September 5th, 2011

what these mean for game 2.0?
need to think.

Load local image and get its pixels in actionscript

September 5th, 2011

After some researches, I found this problem only occurs for local swfs with url starts with “file:///”. So this article would be not useful for you if you run your swfs on the web.

Flash player 10 introduces FileReference, which makes load local files possible. But for unknown secure reason, you can only display a local image but can’t get the pixels of local images. When you try to cast the Loader.content to a Bitmap instance, a #2148 security error will be thrown. Here is a solution for how to bypass this secure restriction. This solution also works for loading any image file data embedded with ByteArray forms.

Firstly, a template.swf needs to be created for embedding:

package
{
   import flash.display.Sprite;
   import flash.display.Bitmap;

   public class Template extends Sprite
   {
      [Embed(source="small.jpg")]
      public var BitmapClass:Class;

      public function GetBitmap ():Bitmap
      {
         return new BitmapClass ();
      }
   }
}

Note there is a GetBitmap function in it. And you MUST embed a jpg file so that the solution supports 3 image formats: jpg, png and gif. If you are lazy to compile this file, here is one: template.swf

Now you can use the following LocalImageLoader class to replace the core Loader class.

package com.tapirgames.util
{
   import flash.utils.ByteArray;
   import flash.utils.Endian;
   import flash.display.Loader;
   import flash.system.LoaderContext;
   import flash.events.Event;
   import flash.events.ProgressEvent;
   import flash.events.IOErrorEvent;
   import flash.events.SecurityErrorEvent;

   public class LocalImageLoader extends Loader
   {
   // template swf file

      [Embed(source="template.swf", mimeType="application/octet-stream")]
      private static var SwfFile:Class;

   // original data

      private var mOriginalData:ByteArray = null;
      private var mIsBadSwfFile:Boolean; // valid when mOriginalData != null

      private var mStartOffset_TagDefineBitsJPEG2:int;
      private var mEndOffset_TagDefineBitsJPEG2:int;
      private var mCharacterID_TagDefineBitsJPEG2:int;

   // load

      override public function loadBytes (imageFileData:ByteArray, context:LoaderContext = null):void
      {
         try
         {

         // parse

            if (mOriginalData == null)
            {
               var swfFile:ByteArray = new SwfFile ();
               swfFile.endian = Endian.LITTLE_ENDIAN;
               swfFile.position = 8;
               mOriginalData = new ByteArray ();
               mOriginalData.endian = Endian.LITTLE_ENDIAN;
               swfFile.readBytes (mOriginalData);
               mOriginalData.uncompress ();

               var numBits:int = (mOriginalData [0] & 0xFF) >> 3;
               mOriginalData.position = (((numBits << 2) + 5 + 7) >> 3) + 4;

               while (true)
               {
                  var startOffset:int = mOriginalData.position;
                  var tagTypeAndLength:int = mOriginalData.readUnsignedShort ();
                  var tagType:int = tagTypeAndLength >> 6;
                  if (tagType == 0) // end tag
                  {
                     mIsBadSwfFile = true;
                     break;
                  }

                  var tagLength:int = tagTypeAndLength & 0x3f;
                  if (tagLength == 0x3f)
                  {
   	               tagLength = mOriginalData.readInt ();
                  }

                  var endOffset:int = mOriginalData.position + tagLength;

                  if (tagType == 21) // tag DefineBitsJPEG2
                  {
                     mCharacterID_TagDefineBitsJPEG2 = mOriginalData.readShort ();
                     mStartOffset_TagDefineBitsJPEG2 = startOffset;
                     mEndOffset_TagDefineBitsJPEG2 = endOffset;

                     mIsBadSwfFile = false;
                     break;
                  }

                  mOriginalData.position = endOffset;
               }
            }

            mOriginalData.position = 0;

            if (mIsBadSwfFile)
               throw new Error ("bad template swf file");

         // write

            var newData:ByteArray = new ByteArray ();
            newData.endian = Endian.LITTLE_ENDIAN;
            newData.writeBytes (mOriginalData, 0, mStartOffset_TagDefineBitsJPEG2);
            newData.writeShort ((21 << 6) | 0x3f);
            newData.writeInt (2 + imageFileData.length); // 2 for CharacterID
            newData.writeShort (mCharacterID_TagDefineBitsJPEG2);
            if (imageFileData.length > 0)
            {
               newData.writeBytes (imageFileData);
            }
            newData.writeBytes (mOriginalData, mEndOffset_TagDefineBitsJPEG2);
            var newFileLength:int = newData.length + 8;
            newData.compress ();

            var newSwfFile:ByteArray = new ByteArray ();
            newSwfFile.endian = Endian.LITTLE_ENDIAN;
            newSwfFile.writeByte (0x43);
            newSwfFile.writeByte (0x57);
            newSwfFile.writeByte (0x53);
            newSwfFile.writeByte (10);
            newSwfFile.writeUnsignedInt (newFileLength);
            newSwfFile.writeBytes (newData);
            newSwfFile.position = 0;
         }
         catch (error:Error)
         {
            dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, false, false, error.message));
         }

      // load, now, no secure errors. Why adobe makes some troubles for developers?

         super.loadBytes (newSwfFile, context);
      }
   }
}

Here is the example on how to use it:

public function LoadLocalImage  (imageFileData:ByteArray):void
{
   //var loader:Loader = new Loader(); // Don't use this
   var loader:LocalImageLoader = new LocalImageLoader ();
   loader.contentLoaderInfo.addEventListener (Event.COMPLETE, OnLoadLocalImageComplete);
   loader.loadBytes (imageFileData);
}

private function OnLoadLocalImageComplete (event:Event):void
{
   // var newBitmap:Bitmap = event.target.content as Bitmap; // Don't use this
   var newBitmap:Bitmap = ((event.target.content.GetBitmap as Function) ()) as Bitmap;
   mBitmap = new Bitmap (newBitmap.bitmapData); // done! no error #2148 will be thrown
}

actionscript 3 base64

September 5th, 2011

Didn’t get satisfied actionscript base64 code snippet by google. So made one by self.

package
{
   public class Base64
   {
      public static const Base64Chars:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

      private static var Base64Char2Index:Array = null;
      private static function GetBase64Char2IndexTable ():Array
      {
         if (Base64Char2Index == null)
         {
            Base64Char2Index = new Array (128); // all char codes in Base64Chars are smaller than 128

            for (var i_char:int = Base64Chars.length - 2; i_char >= 0; -- i_char) // "Base64Chars.length - 2" is to ignore the "=" cahr
            {
               Base64Char2Index [Base64Chars.charCodeAt (i_char)] = i_char;
            }
         }

         return Base64Char2Index;
      }

      public static function DecodeString2ByteArray (text:String):ByteArray
      {
         if (text == null)
         {
            return null;
         }

         var num_chars:int = text.length;
         var num_triples:int = num_chars / 4;
         var num_extras:int = num_chars - num_triples * 4;

         if (num_extras != 0)
         {
            return null;
         }

         var data:ByteArray = new ByteArray ();

         var b0:int, b1:int, b2:int, b3:int;

         var table_char2index:Array = GetBase64Char2IndexTable ();
         var i_char:int = 0;
         data.position = 0;

         while (i_char < num_chars)
         {
            b0 = table_char2index [text.charCodeAt (i_char ++)];
            b1 = table_char2index [text.charCodeAt (i_char ++)];
            b2 = table_char2index [text.charCodeAt (i_char ++)];
            b3 = table_char2index [text.charCodeAt (i_char ++)];

            data.writeByte ((b0 << 2) | ((b1 & 0x30) >> 4));
            data.writeByte (((b1 & 0x0f) << 4) | ((b2 & 0x3c) >> 2));
            data.writeByte (((b2 & 0x03) << 6) | b3);
         }

         data.position = 0;
         return data;
      }

      public static function EncodeByteArray2String(data:ByteArray):String
      {
         if (data == null)
         {
            return null;
         }

         var num_triples:int = data.length / 3;
         var num_extras:int = data.length - num_triples * 3;

         var num_chars:int = (num_extras > 0 ? num_triples + 1 : num_triples) * 4;
         var char_indexes:ByteArray = new ByteArray ();
         char_indexes.length = num_chars;

         var b0:int, b1:int, b2:int;

         data.position = 0;
         char_indexes.length = 0;

         for (var i_triple:int = 0; i_triple < num_triples; ++ i_triple)
         {
            b0 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values
            b1 = data.readByte () & 0xFF;
            b2 = data.readByte () & 0xFF;

            char_indexes.writeByte ((b0 & 0xFC) >> 2);
            char_indexes.writeByte (((b0 & 0x03) << 4) | (b1 >> 4));
            char_indexes.writeByte (((b1 & 0x0F) << 2) | (b2 >> 6));
            char_indexes.writeByte (b2 & 0x3F);
         }

         if (num_extras > 0)
         {
            b0 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values

            if (num_extras == 2)
            {
               b1 = data.readByte () & 0xFF; // "& 0xFF" is essential for negative values

               char_indexes.writeByte ((b0 & 0xFC) >> 2);
               char_indexes.writeByte (((b0 & 0x03) << 4) | (b1 >> 4));
               char_indexes.writeByte ((b1 & 0x0F) << 2);
               char_indexes.writeByte (64); // "="
            }
            else // num_extras == 1
            {
               char_indexes.writeByte ((b0 & 0xFC) >> 2);
               char_indexes.writeByte ((b0 & 0x03) << 4);
               char_indexes.writeByte (64); // "="
               char_indexes.writeByte (64); // "="
            }
         }

         var char_index:int;

         for (var i_char:int = 0; i_char < num_chars; ++ i_char)
         {
            char_index = char_indexes [i_char];
            char_indexes [i_char] = Base64Chars.charCodeAt(char_index);
         }

         char_indexes.position = 0;
         return char_indexes.readUTFBytes (num_chars);
      }
   }
}