slua已经申诉成功

早在一个月前,就已经申诉成功了,一直忘记更新,一些朋友还在关注,这里特别说明下。

slua从申诉开始,我就比较有信心,因为根本不存在非法代码,存在争议的代码(就一个函数)本来就是开源代码,github管理员也认同了这个事实,所以很快就恢复了。

关于slua被DMCA的一些简单说明

 

我简单解释下, 首先放我个人结论,我认为那不是老外(ulua原作者版权,以下简称topher)的版权,topher认为我早期代码仓库里的代码是他的版权,如下:

/*
* Copyright (C) polynation games ltd – All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Christopher Redden, December 2013
* This file is based off of MonoLuaInterface’s wrapping: https://github.com/stevedonovan/MonoLuaInterface
*/
#define LUA_LIB
#include “lua.h”
#include “lauxlib.h”
#include <stdio.h>
/*  LUA INTERFACE SUPPORT  */
#ifndef _WIN32
#define __stdcall
#endif
typedef int (__stdcall *lua_stdcallCFunction) (lua_State *L);
static int stdcall_closure(lua_State *L)
{
    lua_stdcallCFunction fn = (lua_stdcallCFunction)lua_touserdata(L, lua_upvalueindex(1));
    int r = fn(L);
    return r;
}
LUA_API void lua_pushstdcallcfunction(lua_State *L, lua_stdcallCFunction fn)
{
    lua_pushlightuserdata(L, fn);
    lua_pushcclosure(L, stdcall_closure, 1);
}
LUA_API void luaS_newudata(lua_State *L, int val)
{
  int* pointer=(int*)lua_newuserdata(L,sizeof(int));
  *pointer=val;
}
LUA_API int luaS_rawnetobj(lua_State *L,int index)
{
  int *udata=lua_touserdata(L,index);
  if(udata!=NULL) return *udata;
  return -1;
}
但这个4个函数都是luamonointerface的代码,看这里,https://github.com/stevedonovan/MonoLuaInterface/blob/master/src/luastdcall.c

我和topher都针对各自的软件名字,改了这2个函数的名字(仅仅改了函数名字):
LUA_API void luaS_newudata(lua_State *L, int val)
{
  int* pointer=(int*)lua_newuserdata(L,sizeof(int));
  *pointer=val;
}
LUA_API int luaS_rawnetobj(lua_State *L,int index)
{
  int *udata=lua_touserdata(L,index);
  if(udata!=NULL) return *udata;
  return -1;
}

只不过我的代码,原始是从他的文件中拷贝的,拷贝后我修改了函数名字,只是不小心保留了
/*
* Copyright (C) polynation games ltd – All Rights Reserved
* Unauthorized copying of this file, via any medium is strictly prohibited
* Proprietary and confidential
* Written by Christopher Redden, December 2013
这段申明,我保留这段申明是有意义的,之前在群里有人让我删除,我说要尊重原始版权人的申明,毕竟我从他那里复制的,但我知道这个函数是来自于开源软件的。

但大家可以看到,这4个函数完全是MIT opensource 的代码,根本不是topher的版权,也许这个文件里有其他函数是他的版权,但并没有出现在我的代码里。

topher仅仅针对文件头的版权申明,来认定属于他的版权,我认为是不公平的,他没有做任何修改,也没有任何新的功能,仅仅加了一个版权申明,再后来我找到其他解决调用约定的方法,就删除了上述代码和版权声明。

所以当topher找我的时候,让我删除slua的整个库,我不鸟他,他就提起dmca申请了,事情就这样。 目前正在与github沟通进行反通知,希望能申诉回来,我有信心。

slua benchmark

 

===== 6月2日更新

发布新版, 再次超越cstolua,不想贴测试数据了, 有兴趣的自己测试吧, 免得引起对面不喜.

=====第一版测试对比分割线

slua在开发初期,就始终坚持速度是第一开发目标,可以没有太多功能,可以功能比较简单,但速度一定是第一位的,如果不能把lua用在实际的游戏开发中,那么slua只能当做玩具,所以slua首要解决的就是速度,是的slua用于游戏核心逻辑书写成为可能。

与ulua相比,slua的实现不依赖反射,避免了额外的gc alloc,所以比ulua快是必然的,只是快多少的问题;
cstolua可以理解为ulua的插件,可以无缝的同ulua协同工作,它的实现和slua原理相同,去掉了反射,代码优化层面有细微差别,slua同样比cstolua快。

一下测试基于文章发表日时最新版的ulua和cstolua,已经最新版的slua.

我准备5个测试用例,来说明不同情况效率的比较, 这5个测试用例分别是:

1)属性的读取和设置

    local start = os.clock()
    for i=1,200000 do
        local v = transform.position
        transform.position=v
    end
    print(“test1/lua ” .. (os.clock() – start));

2)成员方法的调用

local v = transform.position
local t = os.clock()
for i=1,200000 do
    v:Normalize()
end
print(“lua cost time: ” .. (os.clock() – t));

3)静态方法调用并返回

local t = os.clock()
for i=1,200000 do
    v = Vector3.Normalize(v)
end
print(“lua cost time: ” .. (os.clock() – t));

4)纯属性设置

local v = transform.position
local t = os.clock()
for i=1,200000 do
    transform.position=v
end
print(“lua cost time: ” .. (os.clock() – t));

5)构造valuetype并返回

local t = os.clock()
for i=1,200000 do
    v = Vector3(i,i,i)
end
print(“lua cost time: ” .. (os.clock() – t));

 

为了公平起见,

1)每个测试用例都跑5遍去平均值

2)同时都是在加载UnityVS插件的情况下比较, 这里需要注意仅仅加载插件,开启了vs,但没有处于调状态.我发现加载UnityVS插件性能有一定损失,

3)同时cstolua默认是关闭MULTI_STATE的, slua/ulua默认是开启, 为了公平也将开启MULTI_STATE比较.

4)都采用luajit

 

QQ Photo20150204161045

 

结论是slua真的很快, 平均100次调用开销<3ms, 欢迎测试, 测试demo pull, 打开perf工程, 点击测试.

 

 

===== 5月7日更新

我想还是针对最新版的cstolua做一个全面的评测吧, 之前的评测是针对cstolua老版本,老是挂着之前的评测难免有误导的嫌疑, 特别是总有群里的用户拿着对面群的聊天记录来质疑slua是否真的快, 我是该说点什么了.

新版的cstolua做了一个更新, 就是把valuetype(类似Vector3)等用lua来实现, 这个思想我从第一版push到github的代码就有了,但后来并没有采用,主要原因在于:

1)全部的valuetype都实现一遍代码量太大, 而slua专注解决c#和lua绑定的问题, 希望保持简洁精简, 这也是不会引入任何第三方库的原因, 我实在搞不明白ulua(注意不是cstolua)为啥要绑定json/protobuf/sqlite等库,也许它喜欢大而全的方案吧, 但cjson快吗?不快, protfobuf好吗?不好, sqlite到是不错,但一般情况下用不到.

2)不得不说的是,全部用lua来实现的valuetype在仅适用valuetype来测试的时候是快的,但任何时候需要与其他接受valuetype作为参数的接口交互时就不一定快了,需要考量;

3)全部用lua实现的valuetype仅限于jit里是快的, 而slua支持lua5.3, iOS又不允许jit, 使得slua不能采用这样*可能*退化的方法.

基于上述理由,slua需要提供的是综合的全面的平衡的速度, 那么slua和新版的cstlua综合的对比到底谁快呢, 为此我特别再次做了测试, 当然也为此修改了测试代码, 使得他能更全面的对比, 会比较valuetype的优势,也会比较其他情况下的速度.

带点私货,  @cstolua作者, 拿着商业代码开源了, ulua原作者估计要被他气死, 还问我不会再*山寨*他的valuetype优化吧(这里答复你,不屑做那种玩过的东西),  欢迎你做各种优化, 也欢迎你超越slua, 但不要那么阴暗, 你要相信大多是程序员不会拿着人家东西心安理得的当作自己的.

好了,评测正式开始, 测试例子如下

1)属性的读取和设置, 有valuetype的属性赋值

    local start = os.clock()
    for i=1,200000 do
        local v = transform.position
        transform.position=v
    end
    print(“test1/lua ” .. (os.clock() – start));

2)成员方法的调用, 非valuetype的成员方法调用

local start = os.clock()
for i=1,200000 do
    transform:Rotate(Vector3.up, 90)
end
print(“test2/lua ” .. (os.clock() – start));

3)构造valuetype并返回, valuetype的访问, cstolua确实快, 我认同, 但并不赞同这样做, 特别是cstolua实现的四元数还有bug的情况下.

local t = os.clock()
for i=1,200000 do
    v = Vector3(i,i,i)
end
print(“lua cost time: ” .. (os.clock() – t));

4)构造class并返回 ,非valuetype构造

local t = os.clock()
for i=1,200000 do
    local v = GameObject()
end
print(“lua cost time: ” .. (os.clock() – t));

5)常见游戏代码, 更普遍的游戏逻辑代码

local start = os.clock()
for i=1,20000 do –注意这里是2w次,太多会爆掉
    local v = GameObject()
    v:AddComponent(“SkinnedMeshRenderer”) –注意这里,我本来想用更普遍的component,可惜cstolua默认没导出而使用反射的方法, 为了表示公平,都用大家导出的类型.
    local c=v:GetComponent(“SkinnedMeshRenderer”)
    c.castShadows=false
    c.receiveShadows=false
end
print(“test5/lua ” .. (os.clock() – start));

6)为了彰显valuetype的优势,特别加入纯valuetype的方法调用测试, 这里cstolua也是快的

function test6()
    local transform=cube.transform
    local start = os.clock()
    for i=1,200000 do
        local t=Quaternion.Euler(100,100,100)
        local q=Quaternion.Slerp(Quaternion.identity,t,0.5)
    end
    print(“test6/lua ” .. (os.clock() – start));
end

数据表格如下, 针对cstolua1.9.4 / slua 5月7日github版 / windows 7 / luajit (不要在mac下测试, slua mac下不支持jit)

1O)PN4AS8QE2)_A}{)EA5E1

结论是在jit情况下, valuetype的构造和方法调用, 确实cstolua快了一倍左右, 但其他测试都是slua领先, 不服来辩.

最后针对slua用户,那么能不能像cstolua一样在lua里实现valuetype呢,

重申下:全部用lua实现的valuetype仅限于jit里是快的, 而slua支持lua5.3, iOS又不允许jit, 使得slua不能采用这样*可能*退化的方法.

但你不在乎的话:

答案是可以的slua兼容这种情况,有需要的可以实现, 也可以参考slua之前的代码实现, slua甚至支持部分lua实现, 部分沿用c#实现, 针对个别函数精确优化, 请参考main demo 继承 vector3的例子. 邪恶的可以直接重载了, copy&paste cstolua的lua实现嘛, 虽然有bug….

如果你舍得花点时间, 我建议大家用c代码来实现valuetype, 这样无论是否jit都会获得最大的优化, 如果有时间的话,也许我会在以后的版本中加入c代码实现的valuetype.

===== 5月19日更新

上面提到花点时间用c代码实现valuetype,  趁最近把c++11/14复习的机会,用c++14实现了一把cpptolua,顺便把valuetype实现了cpp话,算作复习作业, 经过cpp实现后的valuetype,无论jit与否(比如iOS系统下,比如使用5.3)都健步如飞, 这才是一劳永逸的解决方案.

在Unity中使用slua

使用Unity开发手游, 热更新是比较麻烦的, 在iOS下由于系统的限制, mono的jit是无法使用的, 这直接导致iOS下根本无法热更新, 但这个特性对于很多上线的游戏是必不可少的, 这符合互联网快速迭代的思路. 所以为了绕过mono的限制, 采用lua开发是一个不错的方案, 这就涉及到如何导出Unity的函数到lua中使用, 在unity store里有一个叫做ulua的插件, 他采用反射的机制, 可以直接在lua中使用unity函数, 但它最大的问题就是效率, 因为大量使用反射, 导致效率低下, 同时大量gc alloc导致频繁gc导致时不时卡顿, 在实际游戏开发中估计很难直接采用ulua开发.

针对以上问题, 我开发并开源了slua, www.slua.net , slua遵循MIT协议, slua采用静态代码生成技术, 没有任何反射调用, 没有任何额外调用开销, 避免额外gc alloc, 所以slua非常快, 和ulua相比, 快到没朋友:).

slua交流QQ群:15647305

slua仍在持续开发中, 目前完成:

  • 速度就是快, 这是slua的核心目标
  • 避免额外gc alloc, 去掉性能杀手
  • 90%以上UnityEngine接口导出(主要去掉了flash,平台相关的接口);
  • 100% UnityEngine.UI接口导出 (4.6+ 版本)
  • 支持UnityEvent/UnityAction, 使用lua function
  • 支持delegation,使用lua function
  • 支持导出自定义类
  • 所有enum导出为number
  • 所有数组返回值导出为lua table
  • 使用luajit 64bit(完整支持armv7,armv7s,arm64), 可用lua5.3替换
  • 支持il2cpp/il2cpp 64

嵌入mono作为脚本

原文 by Miguel de lcaza,

最近在看Unity3D源代码,对于mono的嵌入非常感兴趣,特翻译此文,文章没有完全按照原文翻译, 是将2篇文章合二为一,保留重要有意义的部分.

脚本

以前, 软件都是采用单一语言实现全部功能, 开发者必须在快速的低级语言和高效的高级语言之间做出选择.

对于C/C++程序看起来是这样:

Text2286.png

脚本语言看起来是这样:

Text2304.png

高级语言非常有开发效率,但运行速度稍差,因为中间脚本层的运行效率不如native code速度快, 某些脚本也不能充分利用cpu资源. 后来开发者开始思考仅在某些对效率不敏感的代码中采用脚本,例如ui,交互等,而其他部分还是使用速度更快的低级语言开发.

结果看起来是这样:

Text2330.png

某些脚本语言易于嵌入到低级语言中使用(例如Lua), 而某些语言则没有清晰的api用于嵌入.

mono的优势

mono提供一系列可选择的开发语言,比如静态的C#,VB.net,动态的Boo,IronPython,甚至函数式语言F#. 对于所有语言, 都提供了jit产生native code, 让最终软件可以告诉运行. 同时, mono非常易于嵌入,易于调用现成的native code, 这包括:

    • PInvoke 允许你调用c方法
    • InternalMethod提供了访问了c/c++方法的能力
    • c函数指针同脚本函数的转换

最后,mono可以重用现成的大量.net库,不必重复开发.

mono是如何工作的

首先你需要从一个现有的c程序开始:

Existing.png

通过连接libmono库, 嵌入mono的运行时, 这样你的程序就有了mono的虚拟运行环境,:

Linked.png

mono提供了一系列api用于嵌入目的, 使用这些api可以方便的控制mono运行状态,访问cil代码等.

首先你需要初始化mono运行时, 完成初始化后, 你可以加载感兴趣的cil/.net代码:

Loaded.png

c代码被称为 非托管代码(unmanaged code), 而cil代码成为托管代码(managed code).

c代码可以调用托管代码, 托管代码也可以调用c代码.

托管代码调用c代码一般采用P/Invoke或者mono的api.

嵌入运行时

在linux下仅需要link libmono即可,在windows下,需要导入dll,对应的dll def文件在这里:

http://github.com/mono/mono/blob/master/msvc/mono.def

产生mono.lib可以使用如下命令:

lib /nologo /def:mono.def /out:mono.lib /machine:x86

然后,就是初始化mono运行环境:

#include <glib/glib.h>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>  MonoDomain *domain;   domain = mono_jit_init (domain_name);

这样将返回MonoDomain, domain_name一般是应用的名字,如果需要特定版本的MonoDomain,可以调用:

domain = mono_jit_init_version ("myapp", ""v2.0.50727);

有了MonoDomain后,就可以加载程序集了:

MonoAssembly *assembly;   assembly = mono_domain_assembly_open (domain, "file.exe");
if (!assembly)

error ();

通过上面的代码,可以把file.exe对应的程序集(Assembly)加载到当前域(Domain), 但不会运行, 可以使用如下代码运行:

retval = mono_jit_exec (domain, assembly, argc - 1, argv + 1);

你需要确保有一个静态的Main函数, 这样mono_jit_exec函数就会从这个函数开始运行, Main函数运行之后, 你可以调用任意其他函数, 稍后我们会谈到.

某些特别的运行时特性,比如DLL重映射(DLL remapping)需要依赖一个配置文件, 为了加载配置文件,可以:

   mono_config_parse (NULL);

这样将会加载默认的配置文件(通常在/etc/mono/config), 当然你也可以加载自己的配置文件, 那么只需要传入对应参数即可:

mono_config_parse ("my_mappings");

当mono嵌入程序,你需要告诉他如何查找对应的程序集或者配置文件, 默认mono将会在系统定义的位置去查找(通常是/usr/lib/mono, 配置文件则在/etc/mono), 如果你需要定义自己的查找位置,则:

mono_set_dirs (myapp_lib, myapp_etc);

关闭mono运行环境,这样:

mono_jit_cleanup (domain);

由于目前版本mono不能在同一个进程重复加载,所以只有你确定不再初始化mono,你才需要调用mono_jit_cleanup().

mono提供了2种机制用于导出c代码到CIL空间,分别是internal calls和c代码实现, internal calls和运行时紧密关联, c语言数据类型和CIL的数据类型不提供数据列集(marshalling)支持.

比如, 传递c# string类型到c代码中则是MonoString*(这意味一个指向托管对象的指针), 而传递c# string到P/Invoke c函数中则是utf8的 char*, 取决于数据列集策略.

另外的选择是P/Invoke,加入你有一个c代码如下:

void DoSomething ()
{
   /* ... */
}

为了让运行时可以在当前的环境中找到DoSomething函数,需要使用__Internal这个特别的库名:

using System.Runtime.InteropServices;   [DllImport ("__Internal", EntryPoint="DoSomething")]
static extern void DoSomething ();

通过使用__Internal库名,mono将不会在外部查找DoSomething,而是在当前的可执行空间内查找.P/Invoke提供数据列集支持(转换字符串,转换数据类型,映射代理为函数指针等),这是最简单调用c代码的机制.

你还可以采用托管代码延迟绑定c代码,达到internal call的目的,例如:

mono_add_internal_call ("Hello::Sample", sample);

在C#那边,需要:

using System;
using System.Runtime.CompilerServices;
	
class Hello {
[MethodImplAttribute(MethodImplOptions.InternalCall)]
extern static string Sample ();
}

然后实现你的c代码:

static MonoString*
Sample () 
{
	return mono_string_new (mono_domain_get (), "Hello!");
}

注意你需要返回MonoString,我们使用mono_string_new来构造string.

调用CIL方法

首先你需要获得MonoMethod,通过:

 

MonoImage *
        mono_assembly_get_image  (MonoAssembly *assembly);

	MonoClass *
	mono_class_from_name (MonoImage *image, const char* name_space, const char *name);

然后循环遍历返回的方法数组,直到找到你需要的函数,当然也有一种简单的方法,通过方法描述:

"System.Object:GetHashCode()"

首先创建一个方法描述句柄

#include <mono/metadata/debug-helpers.h>

MonoMethodDesc* mono_method_desc_new (const char *name, gboolean include_namespace);

这样你可以使用方法描述句柄在类或者当前空间中查找方法了:

 

MonoMethod*     mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass);
MonoMethod*     mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image);

你只需要查找一次MonoMethod,返回的指针可以存下来,一边后续重用.

有了MonoMethod指针就可以调用对应的方法了,通过:

MonoObject*
	mono_runtime_invoke         (MonoMethod *method, void *obj, void **params,
	                             MonoObject **exc);
MonoObject*
	mono_runtime_invoke_array   (MonoMethod *method, void *obj, MonoArray *params,
	                             MonoObject **exc);

obj就是this指针,如果传入NULL则意味着静态方法,否则调用对应对象的成员方法.

param则是需要传入的参数,他们其实是MonoObject指针,_invoke_array的不同之处在于传入的object []数组, 这时数组内的数据将装箱(box).

注意, 方法调用不处理虚拟方法,它将确定的调用你传入的方法, 有需要的话,导出一个查找虚拟方法的函数.

exc参数用于是否接收异常,如果是NULL,则表示你不接收异常,否则是抛出的异常对象,如果有异常抛出,函数返回值MonoObject*不可用.

如果函数有返回值,它会被装箱(box)为一个object,例如:

class MyClass {
    static void Foo (int value) {
      ...
    }   int Bar (string name) {
      ...
    }
  }

假定你得到了对应的MonoMethod, 你只需要:

 

/* we execute methods that take one argument */
  void *args [1];
  int val = 10;
  /* Note we put the address of the value type in the args array */
  args [0] = &val;   /* execute Foo (10);
   * it's a static method, so use NULL as the second argument.
   */
  mono_runtime_invoke (foo_method, NULL, args, NULL);   /* a string is a reference, so we put it directly in the args array */
  args [0] = mono_string_new (domain, "Hello");
  /* execute my_class_instance.Bar ("Hello");
   * See the Creating Objects section to learn how to get this_arg.
   */
  MonoObject *result = mono_runtime_invoke (bar_method, this_arg, args, NULL);
  /* we always get a MonoObject* from mono_runtime_invoke (), so to get
   * the integer value we need to unbox (which returns a pointer to
   * the value stored in the object) and dereference.
   */
  int int_result = *(int*)mono_object_unbox (result);

创建对象

创建对性一般就2步:分配内存,然后调用构造函数.

如果构造函数没有参数,则非常简单:

/* we usually get the class we need during initialization */
  MonoImage *image = mono_assembly_get_image (assembly);
  MonoClass *my_class = mono_class_from_name (image, "MyNamespace", "MyClass");
  ...
  /* allocate memory for the object */
  MonoObject *my_class_instance = mono_object_new (domain, my_class);
  /* execute the default argument-less constructor */
  mono_runtime_object_init (my_class_instance);

对于复杂的构造函数,可以使用之前提到的mono_runtime_invoke:

/* execute my_class_instance = new MyClass ("Mono rocks"); */
  MonoObject *my_class_instance = mono_object_new (domain, my_class);
  void *args [1];
  args [0] = mono_string_new (domain, "Mono rocks");
  /* constructor methods return void, so we ignore the return value,
   * the constructed object is my_class_instance.
   */
  mono_runtime_invoke (ctor_method, my_class_instance, args, NULL);

数据类型

不同于P/Invoke, 直接使用mono api无法做到自动化的数据列集,这意味着你需要自己实现,对应的数据转换规则:

{CQ{Z0_@%PU_WBLU6QA{YJQ

从mono 2.0开始, 可以将MonoMethod转换c函数指针, 我们把它叫做thunk, 你可以像使用c函数指针一样调用mono方法:

typedef gint32 (*GetHashCode) (MonoObject *obj);   GetHashCode func = mono_method_get_unmanaged_thunk (System_Object_GetHashCode_method);   gint32 hashvalue = func (myobject);

C# 的代理到c 函数指针

当传递C# 代理给c函数, mono自动创建对应的thunk, 在c代码看起来就是一个函数指针:

using System.Runtime.InteropServices;   class Demo {
    delegate int MyCallback1 (int a, int b);   [DllImport ("MyRuntime")]
    extern static void RegisterCallback (MyCallback1 callback1);   static int Add (int a, int b) { return a + b; }
    static int Sub (int a, int b) { return a - b; }   void Init ()
    {
        // This one registers the method "Add" to be invoked back by C code
        RegisterCallback (Add);
    }
}

c代码:

typedef int (*callback-t) (int a, int b);
static callback_t my_callback;   void RegisterCallback (my_callback_t cb)
{
    my_callback = cb;
}   int InvokeManagedCode (int a, int b)
{
    if (my_callback == NULL){
         printf ("Managed code has not initialized this library yet");
         abort ();
    }
    return (*my_callback) (a, b);
}

解决一个mac版unreal4 bug

昨天unreal4宣布19美元购买,附带开放全部源代码,去官方发现暂时不对中国地区开放,需要验证信用卡,好在万能淘宝有海外代购,需要120rmb,于是果断付款购买。迫不及待下载mac尝鲜,可是无论如何在我的mac上都无法启动,于是github全部源代码,编译代码,看看到底发生了什么,结果发现一个bug,这是一个经常犯的错误,代码如下:

// Open HID Manager – this will cause add device callbacks for all presently connected devices

IOReturn Result = IOHIDManagerOpen( HIDManager, kIOHIDOptionsTypeNone );

if( Result != kIOReturnSuccess )

{

IOHIDManagerUnscheduleFromRunLoop( HIDManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode );

IOHIDManagerRegisterDeviceMatchingCallback( HIDManager, NULL, NULL );

IOHIDManagerRegisterDeviceRemovalCallback( HIDManager, NULL, NULL );

CFRelease( HIDManager );

HIDManager = NULL;

return;

}

for ( int32 ControllerIndex=0; ControllerIndex < MAX_NUM_HIDINPUT_CONTROLLERS; ++ControllerIndex )

{

FControllerState& ControllerState = ControllerStates[ControllerIndex];

FMemory::Memzero( &ControllerState, sizeof(FControllerState) );

ControllerState.ControllerId = ControllerIndex;

}

可以看到当不能正确IOHIDManagerOpen后,代码进行了一些清理后就return了,导致后面的MemZero ControllerStates不能执行,而后面的代码会尝试使用这个不可预期的内存,导致访问违例(AV),修改方法也很简单,就是把

for ( int32 ControllerIndex=0; ControllerIndex < MAX_NUM_HIDINPUT_CONTROLLERS; ++ControllerIndex )

{

FControllerState& ControllerState = ControllerStates[ControllerIndex];

FMemory::Memzero( &ControllerState, sizeof(FControllerState) );

ControllerState.ControllerId = ControllerIndex;

}

移动到HIDInputInterface类构造函数的最前面,保证无论如何,都能正确ZeroMemory这个数组,这样问题解决了。顺便把代码提交给了官方。

Qt5.0 连接 webkit 错误解决

新版的qt5.0把webkit拆分为webkit和webkitwidgets两个部分,所以如果遇到错误:

Undefined symbols for architecture x86_64:
“QWebView::setUrl(QUrl const&)”, referenced from:
Ui_MainWindow::setupUi(QMainWindow*) in mainwindow.o
“QWebView::QWebView(QWidget*)”, referenced from:
Ui_MainWindow::setupUi(QMainWindow*) in mainwindow.o

需要加入:

QT += webkitwidgets

这个错误折腾了我1天,记录下来,让更多人可以快速解决, 另外还有一个尚未解决的问题,目测是qt5的bug,就是连接过程会输出:

ld: warning: directory not found for option ‘-F/Users/xxx/Qt5.0.2/5.0.2/clang_64/qtdeclarative/lib’
ld: warning: directory not found for option ‘-F/Users/xxx/Qt5.0.2/5.0.2/clang_64/qtbase/lib’
ld: warning: directory not found for option ‘-F/Users/xxx/Qt5.0.2/5.0.2/clang_64/qtwebkit/lib’
ld: warning: directory not found for option ‘-F/Users/xxx/Qt5.0.2/5.0.2/clang_64/qtjsbackend/lib’

这样的警告,不影响最终生成程序,只能忍受着。

webgl版的minecraft

 这个东西挺有意思,webgl版的minecraft,使用three.js作为底层webgl封装,使用vexel.js作为voxel类游戏框架,voxel.js专门用于制作类似minecraft这样体素感觉的游戏,有点类似乐高积木。

需要chrome浏览器才能运行下面的demo。

迁移博客

最近国内的虚拟主机到期了,之前本来已经购买了linode,就果断迁移过来了。

迁移最大的问题是之前使用z-blog, 我个人感觉这个博客系统还是挺好用的,不过无法在linux主机上运行,因为采用asp和access系统,经过一番比较迁移到了wp上,并且把之前的数据都导过来了。

因为现在mac系统下办公,之前习惯使用live writer没有很好的替代产品,将就着使用 MarsEdit也还不错。

在flash中用webp图像

webp是google的发布的新的互联网图片格式,与传统的jpg,png图片相比,在不明显损失图片质量的情况下,拥有更大的图片压缩比, 同时支持alpha通道图片,与flash自带的透明图像压缩方法(通道分离+jpg图片压缩)相比也有更大压缩比,所以在flash游戏中如果采用webp格式将会显著降低资源尺寸,加速下载, 可惜的是flash player没有直接提供对webp的支持,想到可以用alchemy移植webp的代码.

开始在google搜索,找到一个blog,干了同样的事情,可是下载回来发现它提供的swc不支持带alpha通道的图片格式,真是见鬼了,这不是google提供的代码吗?难道他还有删减? 不死心,于是自己download webp源码移植到alchemy, 才发现各种问题, 怪不得人家不支持, 首先是alchemy不支持__asm__的内联汇编,修改为c代码后,发现自己生成的库也不支持alpha通道图片,还是不死心,代码内加入各种log,跟踪到底是哪里出的问题,最后发现是alchemy不支持uint64的位移操作,所有<<,>>这样的操作都返回0, 这可要命了, 没有办法手写c代码替换n多位移操作,总算可以编译通过,解码带有alpha通道的图片了,够折腾的.

大概在5天前adobe发了新版alchemy, 正式命名flasCC, 采用新版的alchemy以上的问题都不用操心了,可以顺畅最终生成webp的库.

最终生成的 webpswc.swc 在这里, 已经采用flasCC编译.

完整支持webp各种格式, 包括带alpha通道的webp格式.

===updated 2012/10/12

发现使用上面的webpswc库在flash cs5.5里是无法工作的,但是在flash builder里没有问题,暂时搞不清楚flash和flash builder对待alchemy编译出来代码的差别, 请指导的同学告知,谢谢.

=====

测试代码如下:

package
{
   
    import flash.display.*;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.geom.*;
    import flash.utils.*;
    import flash.utils.ByteArray;
    import flash.net.*;
    import flash.events.*;
    import sineysoft.WebpSwc;

    public class webptest extends Sprite
    {
        [Embed(source="img.webp", mimeType="application/octet-stream")]
        private const WebPImageClass:Class;
       
        [Embed(source="monster.webp", mimeType="application/octet-stream")]
        private const MonterClass:Class;
       
       
        public function webptest()
        {       
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            init();
        }
       
        private function init():void
        {
           
            var now:int = getTimer();
            var ba:ByteArray = new WebPImageClass() as ByteArray;
            var bitmapData:BitmapData = WebpSwc.decode( ba );
            trace(getTimer()-now);
           
            addChild(new Bitmap(bitmapData as BitmapData));
           
            now = getTimer();
            ba = new MonterClass() as ByteArray;
            bitmapData = WebpSwc.decode( ba );
            trace(getTimer()-now);
           
            addChild(new Bitmap(bitmapData as BitmapData));
           
        }
    }
}

 

 

数据对比

格式 —- 对应webp尺寸, 标准webp输出质量 80
monster.png( png8 格式,带alpha通道 ) 44452 bytes
  23814 bytes

需要chrome浏览器才可以预览
img.jpg (photoshop 输出质量 8) 232407 bytes

点击查看原图
  91238 bytes

需要chrome浏览器才可以预览
monster.swf(采用swf压缩,jpg 压缩质量80)  28492 bytes

Content on this page requires a newer version of Adobe Flash Player.

Get Adobe Flash player

  23814 bytes
需要chrome浏览器才可以预览

 

 

最终的测试swf如下:

点击查看

Swc library:

最终生成的 webpswc.swc 在这里

 

不足之处

用alchemy移植的webp解码速度还是不能和原生c代码相比,甚至可以用不堪忍受来形容,原生的c代码解码webp仅需要1-4ms左右,而alchemy版本需要100-1000ms之间,这样的解码速度肯定不能直接用在游戏资源下载,否则一次解码就让客户端卡住,但不不是完全没有优化方案.

优化方法1, 采用分帧解码, 等有空了可以修改下现有的swc库,支持分帧解码.

优化方法2,采用flash 11.5提供的worker,完全后台解码,充分利用多核计算,但这个优化方法应该效果不错,可惜对flash版本有要求.

Decode time:

 

Compress compare:

如果哪天flash原生支持webp应该是个不错的消息.