网络游戏资源代码热更新开发方案

说是方案不如说 一个没有成熟的想法。

客户端这边没什么逻辑 就是下载 文件  如果是压缩文件解压 放到相应的目录。

关键的逻辑 就是 每次更新都有哪些文件 哪些资源修改 删除 新增 ,我是这么实现的 记录每个版本 中每个文件的MD5值 然后进行每次判断。

说下我的想法,一个脚本或者一个程序 没执行一次 算是生成一个新的版本 。每次执行 记录每个文件的MD5值 存入本地数据库中 我用的是sqlite 方便快捷 没执行一次 生成一个version_xxx 表 xxx 为版本号 。

1、执行多次 产生多个表 然后写sql语句 判断 最新的表和之前的表中的文件名还MD5有什么不同。 找出不同 的文件名 写入一个文件中  或者是将新增 更新文件复制到 version_xxx 文件夹下 将删除的文件 文件名 写到一个单独的文件中

2、判断不同后直接将新增 更新文件和删除文件 分别写到一个version.txt文件中

以上两种情况 也是我不知道用哪个好,

第一种 我可以把新增文件和更新文件 打包到一个包中 压缩 然后供下载 优点 客户端下载时可以下载一个文件   下载一个大包 比下载多个散列文件 方便速度快。 还有一点 就是有个人跟我说过 在游戏维护时  和版本多时 多则200个版本  同时维护会产生 200个version_xxx 上传到cdn 上很慢。维护不方便。

第二种 直接下载一个version.txt 读取内容判断哪些文件需要下载哪些需要删除  每个新增文件都可以开一个线程去下载 。没什么需要维护的  只需将version_xxx 传到cdn上就可以了 。不用浪费存储空间 。

各有优缺点 但是综合来说还是第二个方案比较好。

下面我将代码贴出来。

package com.pigsns;
package com.pigsns;
 
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
 
public class ResourcesVersion {
	public static String CommPath="/Users/liuxy/Documents/workspace/bqsg2";
	public static String DBpath = CommPath;
	public static String Resourcepath = CommPath + "/bqsg";
	public static String Publishpath = CommPath + "/version/publish/ios";
 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List pathList = new ArrayList();
		pathList.add(CommPath + "/bqsg/data");
		pathList.add(CommPath + "/bqsg/font");
		pathList.add(CommPath + "/bqsg/Shaders");
		pathList.add(CommPath + "/bqsg/sound");
		pathList.add(CommPath + "/bqsg/UI");
		ResourcesVersion resourcesUtil = new ResourcesVersion();
		resourcesUtil.dealResources(pathList);
	}
	public void dealResources(List pathList) {
		// 删除目标文件中的所有文件
		FileUtils fileUtils = new FileUtils();
		fileUtils.delAllFile(Publishpath);
		try {
			// 连接SQLite的JDBC
			Class.forName("org.sqlite.JDBC");
			// 建立一个数据库名zieckey.db的连接,如果不存在就在当前目录下创建之
			Connection conn = DriverManager.getConnection("jdbc:sqlite:"
					+ DBpath + "/database.db");
			Statement stat = conn.createStatement();
			stat.executeUpdate(String
					.format("create table if not exists version (name char(256),version integer,versionMin integer);"));// 创建一个表,两列
			// 查找现有的版本号
			ResultSet rs = stat
					.executeQuery("SELECT * FROM version ORDER BY version DESC limit 1");
			int version = 0;
			int versionMin = 0;
			while (rs.next()) {
				version = rs.getInt("version");
				versionMin = rs.getInt("versionMin");
				version++;
			}
			// 如果版本号为0 说明没有版本文件数据 新增版本数据
			if (version == 0) {
				String sql = "insert into version values('1.0.0',0,1)";
				int r = stat.executeUpdate(sql);
				if (r == 0) {
					System.out.println("插入version失败");
					return;
				}
				version++;
				versionMin++;
			}
			// 创建新的版本表
			stat.executeUpdate(String
					.format("create table if not exists file_version_%d (file_name char(256) NOT NULL PRIMARY KEY,file_md5 varchar(33)  NOT NULL);",
							version));// 创建一个表,两列
			// 清空新的版本表
			stat.executeUpdate(String.format("delete from file_version_%d",
					version));
			String sql = "insert into file_version_" + version + " values(?,?)";
			PreparedStatement ps = conn.prepareStatement(sql);
			int result = 0;
			for (int i = 0; i < pathList.size(); i++) {
				// 计算文件MD5 并保存到数据表中
				result = fileUtils.initFilesMd5(pathList.get(i), Resourcepath
						+ "/", ps);
				if (result == 0) {
					break;
				}
			}
			if (result == 1) {
				// 更新一个新的版本号 +1
				String sql2 = "update version set version = " + version;
				int r = stat.executeUpdate(sql2);
				if (r == 0) {
					System.out.println("更新版本号失败");
				}
			}
			// 循环所有强制更新版本之后的数据
			for (int i = versionMin; i < version; i++) {
				// 计算文件差异性 并生成增量文件 便于更新
				String sqlAddUp = "select * ,1 from file_version_"
						+ (version)
						+ " as a  where not exists(select file_name from file_version_"
						+ i
						+ " as b where b.file_name=a.file_name)" // 新增更新
						+ " UNION "
						+ "select * ,1 from file_version_"
						+ (version)
						+ " as a  where not exists(select file_md5 from file_version_"
						+ i + " as b where b.file_md5=a.file_md5)";// 新增更新
				ResultSet rs2 = stat.executeQuery(sqlAddUp);
				int index = 0;
				String versionPath = Publishpath + "/version_" + i;
				while (rs2.next()) {
					// 有不同的记录下。
					if (index == 0) {
						System.out.println("\n+++++++++++++版本号为:" + i + "->"
								+ version + "++++++++++++++++++\n");
						index++;
					}
					File file = new File(versionPath);
					if (file != null && !file.exists()) {
						file.mkdirs();
					}
					System.out.println(rs2.getString("file_name") + "++++++++");
					fileUtils.copyFile(
							Resourcepath + "/" + rs2.getString("file_name"),
							versionPath + "/" + rs2.getString("file_name"));
				}
				// 删除的文件
				String sqlDelete = "select * ,0 as state from file_version_"
						+ i
						+ " as a  where not exists(select  file_name  from file_version_"
						+ (version) + " as b where b.file_name=a.file_name)"; // 删除
				ResultSet rsDelete = stat.executeQuery(sqlDelete);
				List updateList = new ArrayList();
				while (rsDelete.next()) {
					// 有不同的记录下。
					if (index == 0) {
						System.out.println("\n+++++++++++++版本号为:" + i + "->"
								+ version + "++++++++++++++++++\n");
						index++;
					}
					System.out.println(rs2.getString("file_name") + "--------");
					// 写到一个文件中供前端下载 并执行删除操作
					updateList.add(rs2.getString("file_name"));
				}
				if (updateList.size() != 0) {
					fileUtils.createAndUpateFile(versionPath + "/version",
							updateList);
				}
				if (index != 0) {
					// 压缩
					ZipCompressor zc = new ZipCompressor(versionPath + ".zip");
					zc.compress(versionPath);
					// 删除 包含自己
					fileUtils.delFolder(versionPath);
				}
			}
			System.out.println("版本更新成功--版本号:" + version);
			conn.close(); // 结束数据库的连接
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("over");
	}
}

package com.pigsns;
package com.pigsns;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.PrintWriter;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
 
public class FileUtils {
	public int initFilesMd5(String path, String replacePath,
			PreparedStatement ps) {
		File[] files = new File(path).listFiles();
		for (File f : files) {
			if (f.isDirectory() && f.getName().indexOf(".svn") == -1
					&& f.getName().indexOf(".") == -1
					&& f.getName().indexOf("..") == -1) {
				initFilesMd5(f.getAbsolutePath(), replacePath, ps);
			} else if (f.getName().indexOf("Thumbs.db") == -1
					&& f.getName().indexOf(".svn") == -1
					&& f.getName().indexOf(".DS_Store") == -1) {
				String dd = f.getAbsolutePath().replace(replacePath, "");
				try {
					ps.setString(1, dd);
					ps.setString(2,
							MakeFileHash.getFileMD5(f.getAbsolutePath()));
					ps.executeUpdate();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return 0;
				} // 插入数据
			}
		}
		return 1;
	}
 
	public boolean createAndUpateFile(String strFilePath,
			List updateList) {
		boolean bFlag = true;
		try {
			File file = new File(strFilePath.toString());
			if (!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			if (!file.exists()) {
				bFlag = file.createNewFile();
			}
			if (bFlag == Boolean.TRUE) {
				FileWriter fw = new FileWriter(file);
				PrintWriter pw = new PrintWriter(fw);
				for (int i = 0; i < updateList.size(); i++) {
					pw.println(updateList.get(i));
				}
				pw.close();
			}
		} catch (Exception e) {
			// logger.error("新建文件操作出错" + e.getLocalizedMessage());
			e.printStackTrace();
			return false;
		}
		return bFlag;
	}
	/**
	 * 复制单个文件
	 * 
	 * @param oldPath
	 *            String 原文件路径 如:c:/fqf.txt
	 * @param newPath
	 *            String 复制后路径 如:f:/fqf.txt
	 * @return boolean
	 */
	public void copyFile(String oldPath, String newPath) {
		File file = new File(newPath);
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		try {
			int byteread = 0;
			File oldfile = new File(oldPath);
			if (oldfile.exists()) { // 文件存在时
				InputStream inStream = new FileInputStream(oldPath); // 读入原文件
				FileOutputStream fs = new FileOutputStream(newPath);
				byte[] buffer = new byte[1444];
				while ((byteread = inStream.read(buffer)) != -1) {
					fs.write(buffer, 0, byteread);
				}
				inStream.close();
				fs.close();
			}
		} catch (Exception e) {
			System.out.println("复制单个文件操作出错");
			e.printStackTrace();
		}
	}
	public void initFilesHashCode(String path, String replacePath,
			Statement stat) {
		File[] files = new File(path).listFiles();
		for (File f : files) {
			if (f.isDirectory() && f.getName().indexOf(".svn") == -1) {
				initFilesHashCode(f.getAbsolutePath(), replacePath, stat);
			} else if (f.getName().indexOf("Thumbs.db") == -1
					&& f.getName().indexOf(".svn") == -1
					&& f.getName().indexOf(".DS_Store") == -1
					&& f.getName().indexOf(".") == -1
					&& f.getName().indexOf("..") == -1) {
				String dd = f.getAbsolutePath().replace(replacePath, "");
				int hash0 = MakeStringHash.getHashCode(dd, 0);
				int hash1 = MakeStringHash.getHashCode(dd, 1);
				int hash2 = MakeStringHash.getHashCode(dd, 2);
				int result = 0;
				try {
					result = stat.executeUpdate("insert into file_info values("
							+ hash0 + "," + hash1 + "," + hash2 + ",'" + dd
							+ "','"
							+ MakeFileHash.getFileMD5(f.getAbsolutePath())
							+ "'," + 1 + ");");
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} // 插入数据
				System.out.println(result + "===" + dd);
			}
		}
	}
	public void initFilesContent(String path, String replacePath,
			PreparedStatement ps) throws Exception {
		File[] files = new File(path).listFiles();
		for (File f : files) {
			if (f.isDirectory() && f.getName().indexOf(".svn") == -1) {
				initFilesContent(f.getAbsolutePath(), replacePath, ps);
			} else if (f.getName().indexOf("Thumbs.db") == -1
					&& f.getName().indexOf(".svn") == -1
					&& f.getName().indexOf(".DS_Store") == -1) {
				String dd = f.getAbsolutePath().replace(replacePath, "");
				int hash0 = MakeStringHash.getHashCode(dd, 0);
				int hash1 = MakeStringHash.getHashCode(dd, 1);
				int hash2 = MakeStringHash.getHashCode(dd, 2);
				int result = 0;
				try {
					// 创建对应长度Byte数组:
					byte[] bytes = getBytesFromFile(f);
					ps.setInt(1, hash0);
					ps.setInt(2, hash1);
					ps.setInt(3, hash2);
					ps.setBytes(4, bytes);
					ps.setInt(5, (int) f.length());
					result = ps.executeUpdate();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} // 插入数据
				System.out.println(result + "+++++" + dd);
			}
		}
	}
	// 返回一个byte数组
	public byte[] getBytesFromFile(File file) throws Exception {
		InputStream is = new FileInputStream(file);
		// 获取文件大小
		long length = file.length();
		if (length > Integer.MAX_VALUE) {
			// 文件太大,无法读取
		}
		// 创建一个数据来保存文件数据
		byte[] bytes = new byte[(int) length];
		// 读取数据到byte数组中
		int offset = 0;
		int numRead = 0;
		while (offset < bytes.length
				&& (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
			offset += numRead;
		}
		// 确保所有数据均被读取
		if (offset < bytes.length) {
			System.out.println("Could not completely read file "
					+ file.getName());
		}
		// Close the input stream and return bytes
		is.close();
		return bytes;
	}
	/**
	 * 
	 * @param folderPath
	 *            包含删除自己
	 */
	public void delFolder(String folderPath) {
		try {
			delAllFile(folderPath); // 删除完里面所有内容
			String filePath = folderPath;
			filePath = filePath.toString();
			java.io.File myFilePath = new java.io.File(filePath);
			myFilePath.delete(); // 删除空文件夹
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 
	 * @param path
	 *            删除指定文件夹下所有文件 文件夹完整绝对路径 不包含自己
	 * @return
	 */
	public boolean delAllFile(String path) {
		boolean flag = false;
		File file = new File(path);
		if (!file.exists()) {
			return flag;
		}
		if (!file.isDirectory()) {
			return flag;
		}
		String[] tempList = file.list();
		File temp = null;
		for (int i = 0; i < tempList.length; i++) {
			if (path.endsWith(File.separator)) {
				temp = new File(path + tempList[i]);
			} else {
				temp = new File(path + File.separator + tempList[i]);
			}
			if (temp.isFile()) {
				temp.delete();
			}
			if (temp.isDirectory()) {
				delAllFile(path + "/" + tempList[i]);// 先删除文件夹里面的文件
				delFolder(path + "/" + tempList[i]);// 再删除空文件夹
				flag = true;
			}
		}
		return flag;
	}
}

package com.pigsns;
 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
 
 
public class ZipCompressor {  
    static final int BUFFER = 8192;  
  
    private File zipFile;  
  
    public ZipCompressor(String pathName) {  
        zipFile = new File(pathName);  
    }  
  
    public void compress(String srcPathName) {  
        File file = new File(srcPathName);  
        if (!file.exists())  
            throw new RuntimeException(srcPathName + "不存在!");  
        try {  
            FileOutputStream fileOutputStream = new FileOutputStream(zipFile);  
            CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,  
                    new CRC32());  
            ZipOutputStream out = new ZipOutputStream(cos);  
            String basedir = "";  
            compress(file, out, basedir);  
            out.close();  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    private void compress(File file, ZipOutputStream out, String basedir) {  
        /* 判断是目录还是文件 */  
        if (file.isDirectory()) {  
            this.compressDirectory(file, out, basedir);  
        } else {  
            this.compressFile(file, out, basedir);  
        }  
    }  
  
    /** 压缩一个目录 */  
    private void compressDirectory(File dir, ZipOutputStream out, String basedir) {  
        if (!dir.exists())  
            return;  
  
        File[] files = dir.listFiles();  
        for (int i = 0; i < files.length; i++) {  
            /* 递归 */  
            compress(files[i], out, basedir + dir.getName() + "/");  
        }  
    }  
  
    /** 压缩一个文件 */  
    private void compressFile(File file, ZipOutputStream out, String basedir) {  
        if (!file.exists()) {  
            return;  
        }  
        try {  
            BufferedInputStream bis = new BufferedInputStream(  
                    new FileInputStream(file));  
            ZipEntry entry = new ZipEntry(basedir + file.getName());  
            out.putNextEntry(entry);  
            int count;  
            byte data[] = new byte[BUFFER];  
            while ((count = bis.read(data, 0, BUFFER)) != -1) {  
                out.write(data, 0, count);  
            }  
            bis.close();  
        } catch (Exception e) {  
            throw new RuntimeException(e);  
        }  
    }  
}  
 
package com.pigsns;
 
import java.security.MessageDigest;
 
public class MakeStringHash {
	private static String getHash(byte[] plainByte,String hashType) {
		try {
			MessageDigest md = MessageDigest.getInstance(hashType);
			md.update(plainByte);
			byte b[] = md.digest();
			int i;
			StringBuffer buf = new StringBuffer("");
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append("0");
				buf.append(Integer.toHexString(i));
			}
			return buf.toString();
 
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	 public static String getStringMD5(String filename)
	    {
	        String str = "";
	        try
	        {
	            str = getHash(filename.getBytes(), "MD5");
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
	        return str;
	    }
	    public static String getStringSHA1(String filename)
	    {
	        String str = "";
	        try
	        {
	            str = getHash(filename.getBytes(), "SHA1");
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
	        return str;
	    }
	    public static String getStringSHA256(String filename)
	    {
	        String str = "";
	        try
	        {
	            str = getHash(filename.getBytes(), "SHA-256");
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
	        return str;
	    }
	    public static String getStringSHA384(String filename)
	    {
	        String str = "";
	        try
	        {
	            str = getHash(filename.getBytes(), "SHA-384");
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
	        return str;
	    }
	    public static String getStringSHA512(String filename)
	    {
	        String str = "";
	        try
	        {
	            str = getHash(filename.getBytes(), "SHA-512");
	        }
	        catch(Exception e)
	        {
	            e.printStackTrace();
	        }
	        return str;
	    }
 
	    public static int getHashCode(String str,int h) {
			int off = 0;
			int len = str.length();
			for (int i = 0; i < len; i++) {
				h = 31 * h + str.charAt(off++);
			}
			return h;
		}
}
 
package com.pigsns;
 
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.MessageDigest;
 
public class MakeFileHash {
private static char hexChar[] = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
        'a', 'b', 'c', 'd', 'e', 'f'
    };
    public MakeFileHash()
    {
    }
    public static String getFileMD5(String filename)
    {
        String str = "";
        try
        {
            str = getHash(filename, "MD5");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return str;
    }
    public static String getFileSHA1(String filename)
    {
        String str = "";
        try
        {
            str = getHash(filename, "SHA1");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return str;
    }
    public static String getFileSHA256(String filename)
    {
        String str = "";
        try
        {
            str = getHash(filename, "SHA-256");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return str;
    }
    public static String getFileSHA384(String filename)
    {
        String str = "";
        try
        {
            str = getHash(filename, "SHA-384");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return str;
    }
    public static String getFileSHA512(String filename)
    {
        String str = "";
        try
        {
            str = getHash(filename, "SHA-512");
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return str;
    }
    private static String getHash(String fileName, String hashType)
        throws Exception
    {
        InputStream fis = new FileInputStream(fileName);
        byte buffer[] = new byte[1024];
        MessageDigest md5 = MessageDigest.getInstance(hashType);
        for(int numRead = 0; (numRead = fis.read(buffer)) > 0;)
        {
            md5.update(buffer, 0, numRead);
        }
        fis.close();
        return toHexString(md5.digest());
    }
    private static String toHexString(byte b[])
    {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for(int i = 0; i < b.length; i++)         {             sb.append(hexChar[(b[i] & 0xf0) >>> 4]);
            sb.append(hexChar[b[i] & 0xf]);
        }
        return sb.toString();
    }
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值