Skip to content

Commit

Permalink
[WIP] Download localization from web (#608)
Browse files Browse the repository at this point in the history
* Cleaning LocalizationLoader for StyleCop.

* Added basic logic for downloading loc files

* Unzipping archive downloaded from github.

* Downloading and replacing files completed. Now let's go with bug fixing...

* Some bug fixing, next: move downloading to a separate class

* Fixed untracked files

* Reorganized files.

* Revert modification of Settings.xml

* Making StyleCop happy

* Fixed README file

* Fixed .gitignore... Again... Also some tune-up to downloader

* Updated CONTRIBUTING.md
  • Loading branch information
QuiZr authored and alexanderfast committed Aug 22, 2016
1 parent 750636a commit 7a0998e
Show file tree
Hide file tree
Showing 112 changed files with 320 additions and 2,155 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
*.txt text
*.lang text
*.md text
*.ver text
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/[Bb]uilds/
/Assets/AssetStoreTools*
/Assets/StreamingAssets/Data/Mods/*
!/Assets/StreamingAssets/Data/Mods/README.md
!/Assets/StreamingAssets/Data/Mods/README.md.meta
/[Pp]roject[Ss]ettings/ProjectSettings.asset
/[Pp]roject[Ss]ettings/ProjectVersion.txt

Expand Down Expand Up @@ -34,3 +36,6 @@ sysinfo.txt

# StyleCop
**StyleCop.Cache*

# Localization files
/Assets/StreamingAssets/Localization/*
237 changes: 237 additions & 0 deletions Assets/Scripts/Localization/LocalizationDownloader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#region License
// ====================================================
// Project Porcupine Copyright(C) 2016 Team Porcupine
// This program comes with ABSOLUTELY NO WARRANTY; This is free software,
// and you are welcome to redistribute it under certain conditions; See
// file LICENSE, which is part of this source code package, for details.
// ====================================================
#endregion
using System.Collections;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using UnityEngine;

namespace ProjectPorcupine.Localization
{
public static class LocalizationDownloader
{
// TODO: Change this to the official repo before PR.
private static readonly string LocalizationRepositoryZipLocation = "https://github.com/QuiZr/ProjectPorcupineLocalization/archive/" + World.current.currentGameVersion + ".zip";

private static readonly string LocalizationFolderPath = Path.Combine(Application.streamingAssetsPath, "Localization");

// Object for downloading localization data from web.
private static WWW www;

/// <summary>
/// Check if there are any new updates for localization. TODO: Add a choice for a user to not update them right now.
/// </summary>
public static IEnumerator CheckIfCurrentLocalizationIsUpToDate()
{
// Check current version of localization
string currentLocalizationVersion;
try
{
currentLocalizationVersion = File.OpenText(Path.Combine(LocalizationFolderPath, "curr.ver")).ReadToEnd();
}
catch (FileNotFoundException e)
{
Logger.LogWarning(e.Message);
currentLocalizationVersion = string.Empty;
}
catch (DirectoryNotFoundException e)
{
Logger.LogWarning(e.Message);
currentLocalizationVersion = string.Empty;
}
catch (System.Exception e)
{
Logger.LogException(e);
yield break;
}

// Download curr.ver file from localization repository and check
// if it's matching the localizationFolderPath/curr.ver file
string avaibleVersionLocation = "https://raw.githubusercontent.com/QuiZr/ProjectPorcupineLocalization/" + World.current.currentGameVersion + "/curr.ver";
WWW versionChecker = new WWW(avaibleVersionLocation);

yield return versionChecker;

if (versionChecker.error != null)
{
// This could be a thing when for example user has no internet connection.
Logger.LogError("Error while checking for available localization updates: " + www.error);
yield break;
}

if (versionChecker.text != currentLocalizationVersion)
{
// There are still some updates available. We should probably notify
// user about it and offer him an option to download it right now.
// For now... Let's just force it >.> Beginners task!
Logger.LogVerbose("There is an update for localization files!");
yield return DownloadLocalizationFromWeb();
}
}

// For now Unity's implementation of .net WebClient will do just fine,
// especially that it doesn't have problems with downloading from https.
private static IEnumerator DownloadLocalizationFromWeb()
{
// If there were some files downloading previously (maybe user tried to download the newest
// language pack and mashed a download button?) just cancel them and start a new one.
if (www != null)
{
www.Dispose();
}

Logger.LogVerbose("Localization files download has started");

www = new WWW(LocalizationRepositoryZipLocation);

// Wait for www to download current localization files.
yield return www;

Logger.LogVerbose("Localization files download has finished!");

// Almost like a callback call
OnDownloadLocalizationComplete();
}

/// <summary>
/// Callback for DownloadLocalizationFromWeb.
/// It replaces current content of localizationFolderPath with fresh, downloaded one.
/// </summary>
private static void OnDownloadLocalizationComplete()
{
if (www.isDone == false)
{
// This should never happen.
Logger.LogException(new System.Exception("OnDownloadLocalizationComplete got called before www finished downloading."));
www.Dispose();
return;
}

if (www.error != null)
{
// This could be a thing when for example user has no internet connection.
Logger.LogError("Error while downloading localizations file: " + www.error);
return;
}

try
{
// Turn's out that System.IO.Compression.GZipStream is not working in unity:
// http://forum.unity3d.com/threads/cant-use-gzipstream-from-c-behaviours.33973/
// So I need to use some sort of 3rd party solution.

// Clear Application.streamingAssetsPath/Localization folder
DirectoryInfo localizationFolderInfo = new DirectoryInfo(LocalizationFolderPath);
foreach (FileInfo file in localizationFolderInfo.GetFiles())
{
// If there are files without that extension then:
// a) someone made a change to localization system and didn't update this
// b) We are in a wrong directory, so let's hope we didn't delete anything important.
if (file.Extension != ".lang" && file.Extension != ".meta" && file.Extension != ".ver")
{
Logger.LogException(new System.Exception("SOMETHING WENT HORRIBLY WRONG AT DOWNLOADING LOCALIZATION!"));
Debug.Break();
return;
}
file.Delete();
}
foreach (DirectoryInfo dir in localizationFolderInfo.GetDirectories())
{
dir.Delete(true);
}

// Convert array of downloaded bytes to stream.
using (ZipInputStream zipReadStream = new ZipInputStream(new MemoryStream(www.bytes)))
{
ZipEntry theEntry;

// While there are still files inside zip archive.
while ((theEntry = zipReadStream.GetNextEntry()) != null)
{
string directoryName = Path.GetDirectoryName(theEntry.Name);
string fileName = Path.GetFileName(theEntry.Name);

// If there was a subfolder in zip (which there probably is) create one.
if (string.IsNullOrEmpty(directoryName) == false)
{
string directoryFullPath = Path.Combine(LocalizationFolderPath, directoryName);
if (Directory.Exists(directoryFullPath) == false)
{
Directory.CreateDirectory(directoryFullPath);
}
}

// Read files from stream to files on HDD.
// 2048 buffer should be plenty.
if (string.IsNullOrEmpty(fileName) == false)
{
string fullFilePath = Path.Combine(LocalizationFolderPath, theEntry.Name);
using (FileStream fileWriter = File.Create(fullFilePath))
{
int size = 2048;
byte[] fdata = new byte[2048];
while (true)
{
size = zipReadStream.Read(fdata, 0, fdata.Length);
if (size > 0)
{
fileWriter.Write(fdata, 0, size);
}
else
{
break;
}
}
}
}
}
}

// At this point we should have an subfolder in Application.streamingAssetsPath/Localization
// called ProjectPorcupineLocalization-*branch name*. Now we need to move all files from that directory
// to Application.streamingAssetsPath/Localization.
FileInfo[] fileInfo = localizationFolderInfo.GetFiles();
if (fileInfo.Length > 0)
{
Logger.LogError("There should be no files here.");
}

DirectoryInfo[] dirInfo = localizationFolderInfo.GetDirectories();
if (dirInfo.Length > 1)
{
Logger.LogError("There should be only one directory");
}

// Move files from ProjectPorcupineLocalization-*branch name* to Application.streamingAssetsPath/Localization.
string[] filesToMove = Directory.GetFiles(dirInfo[0].FullName);

foreach (string file in filesToMove)
{
string fileName = Path.GetFileName(file);
string destFile = Path.Combine(LocalizationFolderPath, fileName);
File.Copy(file, destFile, true);
File.Delete(file);
}

// Remove ProjectPorcupineLocalization-*branch name*
Directory.Delete(dirInfo[0].FullName);

// Maybe there is an easy fix to that restart-need thing?
// Beginners task!
Logger.Log("New localization downloaded, please restart the game for it to take effect.");
}
catch (System.Exception e)
{
// Something happen in the file system.
// TODO: Handle this properly, for now this is as useful as:
// http://i.imgur.com/9ArGADw.png
Logger.LogException(e);
}
}
}
}
12 changes: 12 additions & 0 deletions Assets/Scripts/Localization/LocalizationDownloader.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 20 additions & 20 deletions Assets/Scripts/Localization/LocalizationLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
// file LICENSE, which is part of this source code package, for details.
// ====================================================
#endregion
using UnityEngine;
using System.IO;
using System.Collections;
using UnityEngine;

namespace ProjectPorcupine.Localization
{
Expand All @@ -20,44 +19,45 @@ namespace ProjectPorcupine.Localization
[AddComponentMenu("Localization/Localization Loader")]
public class LocalizationLoader : MonoBehaviour
{
//Initialize the localization files before Unity loads the scene entirely.
//Used to ensure that the TextLocalizer scripts won't throw errors.
void Awake()
// Initialize the localization files before Unity loads the scene entirely.
// Used to ensure that the TextLocalizer scripts won't throw errors.
private void Awake()
{
//Check if the languages have already been loaded before.
// Check if the languages have already been loaded before.
if (LocalizationTable.initialized)
{
//Return in this case.
// Return in this case.
return;
}

//Get the filepath.
// Update localization from the internet.
StartCoroutine(LocalizationDownloader.CheckIfCurrentLocalizationIsUpToDate());

// Get the file path.
string filePath = Path.Combine(Application.streamingAssetsPath, "Localization");
//Loop through all files.
foreach(string file in Directory.GetFiles(filePath))

// Loop through all files.
foreach (string file in Directory.GetFiles(filePath))
{
//Check if the file is really a .lang file, and nothing else.
//TODO: Think over the extension ".lang", might change that in the future.
if(file.EndsWith(".lang"))
// Check if the file is really a .lang file, and nothing else.
// TODO: Think over the extension ".lang", might change that in the future.
if (file.EndsWith(".lang"))
{
//The file extention is .lang, load it.
// The file extension is .lang, load it.
LocalizationTable.LoadLocalizationFile(file);

// Just write a little debug info into the console.
Logger.LogVerbose("Loaded localization at path\n" + file);
}
}


// Atempt to get setting of currently selected language. (Will default to English).
// Attempt to get setting of currently selected language. (Will default to English).
string lang = Settings.getSetting("localization", "en_US");

// setup LocalizationTable with either loaded or defaulted language
// Setup LocalizationTable with either loaded or defaulted language
LocalizationTable.currentLanguage = lang;


//Tell the LocalizationTable that it has been initialized.
// Tell the LocalizationTable that it has been initialized.
LocalizationTable.initialized = true;
}
}
Expand Down
4 changes: 4 additions & 0 deletions Assets/Scripts/Models/World.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
[MoonSharpUserData]
public class World : IXmlSerializable
{
// TODO: Should this be also saved with the world data?
// If so - beginner task!
public readonly string currentGameVersion = "Someone_will_come_up_with_a_proper_naming_scheme_later";

// A two-dimensional array to hold our tile data.
Tile[,] tiles;
public List<Character> characters;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 7a0998e

Please sign in to comment.